<?php
 namespace MongoDB\Tests\SpecTests;
 use ArrayIterator;
 use IteratorIterator;
 use LogicException;
 use MongoDB\Collection;
 use MongoDB\Driver\Server;
 use MongoDB\Tests\FunctionalTestCase as BaseFunctionalTestCase;
 use MultipleIterator;
 use PHPUnit\Framework\SkippedTest;
 use stdClass;
 use Symfony\Bridge\PhpUnit\SetUpTearDownTrait;
 use UnexpectedValueException;
 use function in_array;
 use function json_encode;
 use function MongoDB\BSON\fromJSON;
 use function MongoDB\BSON\toPHP;
 use function sprintf;
 use function version_compare;
 class FunctionalTestCase extends BaseFunctionalTestCase {
 use SetUpTearDownTrait;
 const TOPOLOGY_SINGLE = 'single';
 const TOPOLOGY_REPLICASET = 'replicaset';
 const TOPOLOGY_SHARDED = 'sharded';
 private $context;
 private function doSetUp() {
 parent::setUp();
 $this->context = null;
 }
 private function doTearDown() {
 $this->context = null;
 parent::tearDown();
 }
 public static function assertCommandMatches(stdClass $expected, stdClass $actual) {
 throw new LogicException(sprintf('%s does not assert CommandStartedEvents', static::class));
 }
 public static function assertCommandReplyMatches(stdClass $expected, stdClass $actual) {
 throw new LogicException(sprintf('%s does not assert CommandSucceededEvents', static::class));
 }
 protected static function assertDocumentsMatch($expectedDocument, $actualDocument, $message = '') {
 $constraint = new DocumentsMatchConstraint($expectedDocument, true, true);
 static::assertThat($actualDocument, $constraint, $message);
 }
 protected function assertOutcomeCollectionData(array $expectedDocuments, $resultExpectation = ResultExpectation::ASSERT_SAME_DOCUMENT) {
 $outcomeCollection = $this->getOutcomeCollection($this->getContext()->outcomeReadOptions);
 $mi = new MultipleIterator(MultipleIterator::MIT_NEED_ANY);
 $mi->attachIterator(new ArrayIterator($expectedDocuments));
 $mi->attachIterator(new IteratorIterator($outcomeCollection->find()));
 foreach ($mi as $documents) {
 list($expectedDocument, $actualDocument) = $documents;
 $this->assertNotNull($expectedDocument);
 $this->assertNotNull($actualDocument);
 switch ($resultExpectation) {
 case ResultExpectation::ASSERT_SAME_DOCUMENT: $this->assertSameDocument($expectedDocument, $actualDocument);
 break;
 case ResultExpectation::ASSERT_DOCUMENTS_MATCH: $this->assertDocumentsMatch($expectedDocument, $actualDocument);
 break;
 default: $this->fail(sprintf('Invalid result expectation "%d" for %s', $resultExpectation, __METHOD__));
 }
 }
 }
 protected function checkServerRequirements(array $runOn) {
 foreach ($runOn as $req) {
 $minServerVersion = isset($req->minServerVersion) ? $req->minServerVersion : null;
 $maxServerVersion = isset($req->maxServerVersion) ? $req->maxServerVersion : null;
 $topologies = isset($req->topology) ? $req->topology : null;
 if ($this->isServerRequirementSatisifed($minServerVersion, $maxServerVersion, $topologies)) {
 return;
 }
 }
 $serverVersion = $this->getServerVersion();
 $topology = $this->getTopology();
 $this->markTestSkipped(sprintf('Server version "%s" and topology "%s" do not meet test requirements: %s', $serverVersion, $topology, json_encode($runOn)));
 }
 protected function decodeJson($json) {
 return toPHP(fromJSON($json));
 }
 protected function getContext() {
 if (! $this->context instanceof Context) {
 throw new LogicException('Context has not been set');
 }
 return $this->context;
 }
 protected function setContext(Context $context) {
 $this->context = $context;
 }
 protected function dropTestAndOutcomeCollections() {
 $context = $this->getContext();
 if ($context->bucketName !== null) {
 $bucket = $context->getGridFSBucket($context->defaultWriteOptions);
 $bucket->drop();
 }
 $collection = null;
 if ($context->collectionName !== null) {
 $collection = $context->getCollection($context->defaultWriteOptions);
 $collection->drop();
 }
 if ($context->outcomeCollectionName !== null) {
 $outcomeCollection = $this->getOutcomeCollection($context->defaultWriteOptions);
 if ($collection === null || $outcomeCollection->getNamespace() !== $collection->getNamespace()) {
 $outcomeCollection->drop();
 }
 }
 }
 protected function insertDataFixtures(array $documents, $collectionName = null) {
 if (empty($documents)) {
 return;
 }
 $context = $this->getContext();
 $collection = $collectionName ? $context->selectCollection($context->databaseName, $collectionName) : $context->getCollection();
 $collection->insertMany($documents, $context->defaultWriteOptions);
 return;
 }
 private function getOutcomeCollection(array $collectionOptions = []) {
 $context = $this->getContext();
 return new Collection($this->manager, $context->databaseName, $context->outcomeCollectionName, $collectionOptions);
 }
 private function getTopology() {
 $topologyTypeMap = [ Server::TYPE_STANDALONE => self::TOPOLOGY_SINGLE, Server::TYPE_RS_PRIMARY => self::TOPOLOGY_REPLICASET, Server::TYPE_MONGOS => self::TOPOLOGY_SHARDED, ];
 $primaryType = $this->getPrimaryServer()->getType();
 if (isset($topologyTypeMap[$primaryType])) {
 return $topologyTypeMap[$primaryType];
 }
 throw new UnexpectedValueException('Toplogy is neither single nor RS nor sharded');
 }
 private function isServerRequirementSatisifed($minServerVersion, $maxServerVersion, array $topologies = null) {
 $serverVersion = $this->getServerVersion();
 if (isset($minServerVersion) && version_compare($serverVersion, $minServerVersion, '<')) {
 return false;
 }
 if (isset($maxServerVersion) && version_compare($serverVersion, $maxServerVersion, '>')) {
 return false;
 }
 $topology = $this->getTopology();
 if (isset($topologies) && ! in_array($topology, $topologies)) {
 return false;
 }
 return true;
 }
 }
 