<?php
 namespace MongoDB\Tests\SpecTests;
 use ArrayIterator;
 use LogicException;
 use MongoDB\Driver\Monitoring\CommandFailedEvent;
 use MongoDB\Driver\Monitoring\CommandStartedEvent;
 use MongoDB\Driver\Monitoring\CommandSubscriber;
 use MongoDB\Driver\Monitoring\CommandSucceededEvent;
 use MultipleIterator;
 use function count;
 use function in_array;
 use function key;
 use function MongoDB\Driver\Monitoring\addSubscriber;
 use function MongoDB\Driver\Monitoring\removeSubscriber;
 class CommandExpectations implements CommandSubscriber {
 private $actualEvents = [];
 private $expectedEvents = [];
 private $ignoreCommandFailed = false;
 private $ignoreCommandStarted = false;
 private $ignoreCommandSucceeded = false;
 private $ignoreExtraEvents = false;
 private $ignoredCommandNames = [];
 private function __construct(array $events) {
 foreach ($events as $event) {
 switch (key($event)) {
 case 'command_failed_event': $this->expectedEvents[] = [$event->command_failed_event, CommandFailedEvent::class];
 break;
 case 'command_started_event': $this->expectedEvents[] = [$event->command_started_event, CommandStartedEvent::class];
 break;
 case 'command_succeeded_event': $this->expectedEvents[] = [$event->command_succeeded_event, CommandSucceededEvent::class];
 break;
 default: throw new LogicException('Unsupported event type: ' . key($event));
 }
 }
 }
 public static function fromChangeStreams(array $expectedEvents) {
 $o = new self($expectedEvents);
 $o->ignoreCommandFailed = true;
 $o->ignoreCommandSucceeded = true;
 $o->ignoreExtraEvents = true;
 return $o;
 }
 public static function fromClientSideEncryption(array $expectedEvents) {
 $o = new self($expectedEvents);
 $o->ignoreCommandFailed = true;
 $o->ignoreCommandSucceeded = true;
 return $o;
 }
 public static function fromCommandMonitoring(array $expectedEvents) {
 return new self($expectedEvents);
 }
 public static function fromCrud(array $expectedEvents) {
 $o = new self($expectedEvents);
 $o->ignoreCommandFailed = true;
 $o->ignoreCommandSucceeded = true;
 return $o;
 }
 public static function fromRetryableReads(array $expectedEvents) {
 $o = new self($expectedEvents);
 $o->ignoreCommandFailed = true;
 $o->ignoreCommandSucceeded = true;
 $o->ignoreExtraEvents = true;
 return $o;
 }
 public static function fromTransactions(array $expectedEvents) {
 $o = new self($expectedEvents);
 $o->ignoreCommandFailed = true;
 $o->ignoreCommandSucceeded = true;
 $o->ignoredCommandNames = ['buildInfo', 'getParameter', 'configureFailPoint'];
 return $o;
 }
 public function commandFailed(CommandFailedEvent $event) {
 if ($this->ignoreCommandFailed || $this->isEventIgnored($event)) {
 return;
 }
 $this->actualEvents[] = $event;
 }
 public function commandStarted(CommandStartedEvent $event) {
 if ($this->ignoreCommandStarted || $this->isEventIgnored($event)) {
 return;
 }
 $this->actualEvents[] = $event;
 }
 public function commandSucceeded(CommandSucceededEvent $event) {
 if ($this->ignoreCommandSucceeded || $this->isEventIgnored($event)) {
 return;
 }
 $this->actualEvents[] = $event;
 }
 public function startMonitoring() {
 addSubscriber($this);
 }
 public function stopMonitoring() {
 removeSubscriber($this);
 }
 public function assert(FunctionalTestCase $test, Context $context) {
 $test->assertCount(count($this->expectedEvents), $this->actualEvents);
 $mi = new MultipleIterator(MultipleIterator::MIT_NEED_ANY);
 $mi->attachIterator(new ArrayIterator($this->expectedEvents));
 $mi->attachIterator(new ArrayIterator($this->actualEvents));
 foreach ($mi as $events) {
 list($expectedEventAndClass, $actualEvent) = $events;
 list($expectedEvent, $expectedClass) = $expectedEventAndClass;
 $test->assertInstanceOf($expectedClass, $actualEvent);
 if (isset($expectedEvent->command_name)) {
 $test->assertSame($expectedEvent->command_name, $actualEvent->getCommandName());
 }
 if (isset($expectedEvent->database_name)) {
 $test->assertSame($expectedEvent->database_name, $actualEvent->getDatabaseName());
 }
 if (isset($expectedEvent->command)) {
 $test->assertInstanceOf(CommandStartedEvent::class, $actualEvent);
 $expectedCommand = $expectedEvent->command;
 $context->replaceCommandSessionPlaceholder($expectedCommand);
 $test->assertCommandMatches($expectedCommand, $actualEvent->getCommand());
 }
 if (isset($expectedEvent->reply)) {
 $test->assertInstanceOf(CommandSucceededEvent::class, $actualEvent);
 $test->assertCommandReplyMatches($expectedEvent->reply, $actualEvent->getReply());
 }
 }
 }
 private function isEventIgnored($event) {
 return ($this->ignoreExtraEvents && count($this->actualEvents) === count($this->expectedEvents)) || in_array($event->getCommandName(), $this->ignoredCommandNames);
 }
 }
 