Przeglądaj źródła

Merge pull request #28 from alcaeus/run-ext-mongo-tests

Run testsuite against ext-mongo
Andreas 10 lat temu
rodzic
commit
e8bb929d76
35 zmienionych plików z 660 dodań i 392 usunięć
  1. 7 2
      .travis.yml
  2. 12 2
      README.md
  3. 26 7
      lib/Alcaeus/MongoDbAdapter/AbstractCursor.php
  4. 29 12
      lib/Alcaeus/MongoDbAdapter/ExceptionConverter.php
  5. 8 4
      lib/Mongo/MongoClient.php
  6. 84 74
      lib/Mongo/MongoCollection.php
  7. 19 0
      lib/Mongo/MongoCommandCursor.php
  8. 2 2
      lib/Mongo/MongoCursor.php
  9. 13 14
      lib/Mongo/MongoDB.php
  10. 10 10
      lib/Mongo/MongoGridFS.php
  11. 48 15
      lib/Mongo/MongoWriteBatch.php
  12. 22 18
      lib/Mongo/functions.php
  13. 6 0
      phpunit.xml.dist
  14. 1 7
      tests/Alcaeus/MongoDbAdapter/ExceptionConverterTest.php
  15. 14 1
      tests/Alcaeus/MongoDbAdapter/MongoBinDataTest.php
  16. 18 21
      tests/Alcaeus/MongoDbAdapter/MongoClientTest.php
  17. 14 1
      tests/Alcaeus/MongoDbAdapter/MongoCodeTest.php
  18. 127 96
      tests/Alcaeus/MongoDbAdapter/MongoCollectionTest.php
  19. 10 9
      tests/Alcaeus/MongoDbAdapter/MongoCommandCursorTest.php
  20. 19 11
      tests/Alcaeus/MongoDbAdapter/MongoCursorTest.php
  21. 3 2
      tests/Alcaeus/MongoDbAdapter/MongoDBRefTest.php
  22. 41 27
      tests/Alcaeus/MongoDbAdapter/MongoDBTest.php
  23. 15 0
      tests/Alcaeus/MongoDbAdapter/MongoDateTest.php
  24. 3 11
      tests/Alcaeus/MongoDbAdapter/MongoDeleteBatchTest.php
  25. 1 1
      tests/Alcaeus/MongoDbAdapter/MongoGridFSCursorTest.php
  26. 10 9
      tests/Alcaeus/MongoDbAdapter/MongoGridFSFileTest.php
  27. 6 5
      tests/Alcaeus/MongoDbAdapter/MongoGridFSTest.php
  28. 7 3
      tests/Alcaeus/MongoDbAdapter/MongoIdTest.php
  29. 14 11
      tests/Alcaeus/MongoDbAdapter/MongoInsertBatchTest.php
  30. 4 2
      tests/Alcaeus/MongoDbAdapter/MongoMaxKeyTest.php
  31. 2 0
      tests/Alcaeus/MongoDbAdapter/MongoMinKeyTest.php
  32. 13 0
      tests/Alcaeus/MongoDbAdapter/MongoRegexTest.php
  33. 13 0
      tests/Alcaeus/MongoDbAdapter/MongoTimestampTest.php
  34. 21 15
      tests/Alcaeus/MongoDbAdapter/MongoUpdateBatchTest.php
  35. 18 0
      tests/Alcaeus/MongoDbAdapter/TestCase.php

+ 7 - 2
.travis.yml

@@ -7,8 +7,12 @@ php:
   - 7.0
   - 7.0
 
 
 env:
 env:
-  matrix:
-    - DRIVER_VERSION=1.1.1
+  - DRIVER_VERSION=1.1.1
+
+matrix:
+  include:
+    - php: 5.6
+      env: DRIVER_VERSION=1.1.1 LEGACY_DRIVER_VERSION=1.6.12
 
 
 addons:
 addons:
   apt:
   apt:
@@ -20,6 +24,7 @@ addons:
 before_script:
 before_script:
   - pecl install -f mongodb-${DRIVER_VERSION}
   - pecl install -f mongodb-${DRIVER_VERSION}
   - composer install
   - composer install
+  - if [ "x$LEGACY_DRIVER_VERSION" != "x" ]; then yes '' | pecl -q install -f mongo-${LEGACY_DRIVER_VERSION}; fi
 
 
 script:
 script:
     - ./vendor/bin/phpunit --coverage-clover=coverage.clover
     - ./vendor/bin/phpunit --coverage-clover=coverage.clover

+ 12 - 2
README.md

@@ -91,6 +91,8 @@ return `0` as connection ID.
 
 
 ## MongoCollection
 ## MongoCollection
 
 
+ - The [aggregate](https://secure.php.net/manual/en/mongocollection.aggregate.php)
+ method is not working because of a bug in the underlying library.
  - The [createIndex](https://secure.php.net/manual/en/mongocollection.createindex.php)
  - The [createIndex](https://secure.php.net/manual/en/mongocollection.createindex.php)
  method does not yet return the same result as the original method. Instead, it
  method does not yet return the same result as the original method. Instead, it
  always returns the name of the index created.
  always returns the name of the index created.
@@ -109,14 +111,22 @@ return `0` as connection ID.
  - The [hasNext](https://php.net/manual/en/mongocursor.hasnext.php)
  - The [hasNext](https://php.net/manual/en/mongocursor.hasnext.php)
  method is not yet implemented.
  method is not yet implemented.
  - The [info](https://php.net/manual/en/mongocursor.info.php) method does not
  - The [info](https://php.net/manual/en/mongocursor.info.php) method does not
- reliably fill all fields in the cursor information. This includes the `at`, `numReturned`,
- and `server` keys once the cursor has started iterating.
+ reliably fill all fields in the cursor information. This includes the `numReturned`
+ and `server` keys once the cursor has started iterating. The `numReturned` field
+ will always show the same value as the `at` field. The `server` field is lacking
+ authentication information.
  - The [setFlag](https://php.net/manual/en/mongocursor.setflag.php)
  - The [setFlag](https://php.net/manual/en/mongocursor.setflag.php)
  method is not yet implemented.
  method is not yet implemented.
 
 
 ## MongoCommandCursor
 ## MongoCommandCursor
  - The [createFromDocument](https://php.net/manual/en/mongocommandcursor.createfromdocument.php)
  - The [createFromDocument](https://php.net/manual/en/mongocommandcursor.createfromdocument.php)
  method is not yet implemented.
  method is not yet implemented.
+ - The [info](https://php.net/manual/en/mongocommandcursor.info.php) method does not
+ reliably fill all fields in the cursor information. This includes the `at`, `numReturned`,
+ `firstBatchAt` and `firstBatchNumReturned` fields. The `at` and `numReturned`
+ fields always return 0 for compatibility to MongoCursor. The `firstBatchAt` and
+ `firstBatchNumReturned` fields will contain the same value, which is the internal
+ position of the iterator.
 
 
 ## Types
 ## Types
 
 

+ 26 - 7
lib/Alcaeus/MongoDbAdapter/AbstractCursor.php

@@ -29,7 +29,7 @@ abstract class AbstractCursor
     /**
     /**
      * @var int
      * @var int
      */
      */
-    protected $batchSize;
+    protected $batchSize = 0;
 
 
     /**
     /**
      * @var Collection
      * @var Collection
@@ -67,6 +67,11 @@ abstract class AbstractCursor
     protected $startedIterating = false;
     protected $startedIterating = false;
 
 
     /**
     /**
+     * @var int
+     */
+    protected $position = 0;
+
+    /**
      * @var array
      * @var array
      */
      */
     protected $optionNames = [
     protected $optionNames = [
@@ -113,7 +118,10 @@ abstract class AbstractCursor
      */
      */
     public function current()
     public function current()
     {
     {
-        $this->startedIterating = true;
+        if (! $this->startedIterating) {
+            return null;
+        }
+
         $document = $this->ensureIterator()->current();
         $document = $this->ensureIterator()->current();
         if ($document !== null) {
         if ($document !== null) {
             $document = TypeConverter::toLegacy($document);
             $document = TypeConverter::toLegacy($document);
@@ -129,6 +137,10 @@ abstract class AbstractCursor
      */
      */
     public function key()
     public function key()
     {
     {
+        if (! $this->startedIterating) {
+            return null;
+        }
+
         return $this->ensureIterator()->key();
         return $this->ensureIterator()->key();
     }
     }
 
 
@@ -141,11 +153,12 @@ abstract class AbstractCursor
      */
      */
     public function next()
     public function next()
     {
     {
-        if (!$this->startedIterating) {
+        if (! $this->startedIterating) {
             $this->ensureIterator();
             $this->ensureIterator();
             $this->startedIterating = true;
             $this->startedIterating = true;
         } else {
         } else {
             $this->ensureIterator()->next();
             $this->ensureIterator()->next();
+            $this->position++;
         }
         }
 
 
         return $this->current();
         return $this->current();
@@ -162,6 +175,7 @@ abstract class AbstractCursor
         // We can recreate the cursor to allow it to be rewound
         // We can recreate the cursor to allow it to be rewound
         $this->reset();
         $this->reset();
         $this->startedIterating = true;
         $this->startedIterating = true;
+        $this->position = 0;
         $this->ensureIterator()->rewind();
         $this->ensureIterator()->rewind();
     }
     }
 
 
@@ -172,6 +186,10 @@ abstract class AbstractCursor
      */
      */
     public function valid()
     public function valid()
     {
     {
+        if (! $this->startedIterating) {
+            return false;
+        }
+
         return $this->ensureIterator()->valid();
         return $this->ensureIterator()->valid();
     }
     }
 
 
@@ -323,11 +341,12 @@ abstract class AbstractCursor
                     $typeString = 'STANDALONE';
                     $typeString = 'STANDALONE';
             }
             }
 
 
+            $cursorId = (string) $this->cursor->getId();
             $iterationInfo += [
             $iterationInfo += [
-                'id' => (string) $this->cursor->getId(),
-                'at' => null, // @todo Complete info for cursor that is iterating
-                'numReturned' => null, // @todo Complete info for cursor that is iterating
-                'server' => null, // @todo Complete info for cursor that is iterating
+                'id' => (int) $cursorId,
+                'at' => $this->position,
+                'numReturned' => $this->position, // This can't be obtained from the new cursor
+                'server' => sprintf('%s:%d;-;.;%d', $this->cursor->getServer()->getHost(), $this->cursor->getServer()->getPort(), getmypid()),
                 'host' => $this->cursor->getServer()->getHost(),
                 'host' => $this->cursor->getServer()->getHost(),
                 'port' => $this->cursor->getServer()->getPort(),
                 'port' => $this->cursor->getServer()->getPort(),
                 'connection_type_desc' => $typeString,
                 'connection_type_desc' => $typeString,

+ 29 - 12
lib/Alcaeus/MongoDbAdapter/ExceptionConverter.php

@@ -28,8 +28,11 @@ class ExceptionConverter
      *
      *
      * @return \MongoException
      * @return \MongoException
      */
      */
-    public static function convertException(Exception\Exception $e, $fallbackClass = 'MongoException')
+    public static function toLegacy(Exception\Exception $e, $fallbackClass = 'MongoException')
     {
     {
+        $message = $e->getMessage();
+        $code = $e->getCode();
+
         switch (get_class($e)) {
         switch (get_class($e)) {
             case Exception\AuthenticationException::class:
             case Exception\AuthenticationException::class:
             case Exception\ConnectionException::class:
             case Exception\ConnectionException::class:
@@ -40,7 +43,25 @@ class ExceptionConverter
 
 
             case Exception\BulkWriteException::class:
             case Exception\BulkWriteException::class:
             case Exception\WriteException::class:
             case Exception\WriteException::class:
-                $class = 'MongoCursorException';
+                $writeResult = $e->getWriteResult();
+
+                if ($writeResult) {
+                    $writeError = $writeResult->getWriteErrors()[0];
+
+                    $message = $writeError->getMessage();
+                    $code = $writeError->getCode();
+                }
+
+                switch ($code) {
+                    // see https://github.com/mongodb/mongo-php-driver-legacy/blob/ad3ed45739e9702ae48e53ddfadc482d9c4c7e1c/cursor_shared.c#L540
+                    case 11000:
+                    case 11001:
+                    case 12582:
+                        $class = 'MongoDuplicateKeyException';
+                        break;
+                    default:
+                        $class = 'MongoCursorException';
+                }
                 break;
                 break;
 
 
             case Exception\ExecutionTimeoutException::class:
             case Exception\ExecutionTimeoutException::class:
@@ -51,18 +72,14 @@ class ExceptionConverter
                 $class = $fallbackClass;
                 $class = $fallbackClass;
         }
         }
 
 
-        if (strpos($e->getMessage(), 'No suitable servers found') !== false) {
-            return new \MongoConnectionException($e->getMessage(), $e->getCode(), $e);
+        if (strpos($message, 'No suitable servers found') !== false) {
+            return new \MongoConnectionException($message, $code, $e);
         }
         }
 
 
-        return new $class($e->getMessage(), $e->getCode(), $e);
-    }
+        if ($message === "cannot use 'w' > 1 when a host is not replicated") {
+            return new \MongoWriteConcernException($message, $code, $e);
+        }
 
 
-    /**
-     * @throws \MongoException
-     */
-    public static function toLegacy(Exception\Exception $e, $fallbackClass = 'MongoException')
-    {
-        throw self::convertException($e, $fallbackClass);
+        return new $class($message, $code, $e);
     }
     }
 }
 }

+ 8 - 4
lib/Mongo/MongoClient.php

@@ -191,11 +191,11 @@ class MongoClient
         try {
         try {
             $servers = $this->manager->getServers();
             $servers = $this->manager->getServers();
         } catch (\MongoDB\Driver\Exception\Exception $e) {
         } catch (\MongoDB\Driver\Exception\Exception $e) {
-            ExceptionConverter::toLegacy($e);
+            throw ExceptionConverter::toLegacy($e);
         }
         }
 
 
         foreach ($servers as $server) {
         foreach ($servers as $server) {
-            $key = sprintf('%s:%d', $server->getHost(), $server->getPort());
+            $key = sprintf('%s:%d;-;.;%d', $server->getHost(), $server->getPort(), getmypid());
             $info = $server->getInfo();
             $info = $server->getInfo();
 
 
             switch ($server->getType()) {
             switch ($server->getType()) {
@@ -246,7 +246,7 @@ class MongoClient
         try {
         try {
             $databaseInfoIterator = $this->client->listDatabases();
             $databaseInfoIterator = $this->client->listDatabases();
         } catch (\MongoDB\Driver\Exception\Exception $e) {
         } catch (\MongoDB\Driver\Exception\Exception $e) {
-            ExceptionConverter::toLegacy($e);
+            throw ExceptionConverter::toLegacy($e);
         }
         }
 
 
         $databases = [
         $databases = [
@@ -256,7 +256,11 @@ class MongoClient
         ];
         ];
 
 
         foreach ($databaseInfoIterator as $databaseInfo) {
         foreach ($databaseInfoIterator as $databaseInfo) {
-            $databases['databases'][] = $databaseInfo->getName();
+            $databases['databases'][] = [
+                'name' => $databaseInfo->getName(),
+                'empty' => $databaseInfo->isEmpty(),
+                'sizeOnDisk' => $databaseInfo->getSizeOnDisk(),
+            ];
             $databases['totalSize'] += $databaseInfo->getSizeOnDisk();
             $databases['totalSize'] += $databaseInfo->getSizeOnDisk();
         }
         }
 
 

+ 84 - 74
lib/Mongo/MongoCollection.php

@@ -101,7 +101,7 @@ class MongoCollection
             return $this->getWriteConcern()[$name];
             return $this->getWriteConcern()[$name];
         }
         }
 
 
-        return $this->db->selectCollection($this->name . '.' . $name);
+        return $this->db->selectCollection($this->name . '.' . str_replace(chr(0), '', $name));
     }
     }
 
 
     /**
     /**
@@ -144,19 +144,23 @@ class MongoCollection
             $options = $op;
             $options = $op;
         }
         }
 
 
-        $command = [
-            'aggregate' => $this->name,
-            'pipeline' => $pipeline
-        ];
+        if (isset($options['cursor'])) {
+            $options['useCursor'] = true;
 
 
-        $command += $options;
+            if (isset($options['cursor']['batchSize'])) {
+                $options['batchSize'] = $options['cursor']['batchSize'];
+            }
 
 
-        try {
-            return $this->db->command($command);
-        } catch (MongoCursorTimeoutException $e) {
-            throw new MongoExecutionTimeoutException($e->getMessage(), $e->getCode(), $e);
+            unset($options['cursor']);
+        } else {
+            $options['useCursor'] = false;
         }
         }
 
 
+        try {
+            return $this->collection->aggregate(TypeConverter::fromLegacy($pipeline), $options);
+        } catch (\MongoDB\Driver\Exception\Exception $e) {
+            throw ExceptionConverter::toLegacy($e);
+        }
     }
     }
 
 
     /**
     /**
@@ -177,7 +181,7 @@ class MongoCollection
 
 
         // Convert cursor option
         // Convert cursor option
         if (! isset($options['cursor'])) {
         if (! isset($options['cursor'])) {
-            $options['cursor'] = true;
+            $options['cursor'] = new \stdClass();
         }
         }
 
 
         $command += $options;
         $command += $options;
@@ -263,7 +267,7 @@ class MongoCollection
     public function insert(&$a, array $options = [])
     public function insert(&$a, array $options = [])
     {
     {
         if (! $this->ensureDocumentHasMongoId($a)) {
         if (! $this->ensureDocumentHasMongoId($a)) {
-            trigger_error(sprintf('%s expects parameter %d to be an array or object, %s given', __METHOD__, 1, gettype($a)), E_USER_WARNING);
+            trigger_error(sprintf('%s(): expects parameter %d to be an array or object, %s given', __METHOD__, 1, gettype($a)), E_USER_WARNING);
             return;
             return;
         }
         }
 
 
@@ -276,17 +280,8 @@ class MongoCollection
                 TypeConverter::fromLegacy($a),
                 TypeConverter::fromLegacy($a),
                 $this->convertWriteConcernOptions($options)
                 $this->convertWriteConcernOptions($options)
             );
             );
-        } catch (\MongoDB\Driver\Exception\BulkWriteException $e) {
-            $writeResult = $e->getWriteResult();
-            $writeError = $writeResult->getWriteErrors()[0];
-            return [
-                'ok' => 0.0,
-                'n' => 0,
-                'err' => $writeError->getCode(),
-                'errmsg' => $writeError->getMessage(),
-            ];
         } catch (\MongoDB\Driver\Exception\Exception $e) {
         } catch (\MongoDB\Driver\Exception\Exception $e) {
-            ExceptionConverter::toLegacy($e);
+            throw ExceptionConverter::toLegacy($e);
         }
         }
 
 
         if (! $result->isAcknowledged()) {
         if (! $result->isAcknowledged()) {
@@ -341,7 +336,7 @@ class MongoCollection
                 $this->convertWriteConcernOptions($options)
                 $this->convertWriteConcernOptions($options)
             );
             );
         } catch (\MongoDB\Driver\Exception\Exception $e) {
         } catch (\MongoDB\Driver\Exception\Exception $e) {
-            ExceptionConverter::toLegacy($e);
+            throw ExceptionConverter::toLegacy($e, 'MongoResultException');
         }
         }
 
 
         if (! $result->isAcknowledged()) {
         if (! $result->isAcknowledged()) {
@@ -349,12 +344,12 @@ class MongoCollection
         }
         }
 
 
         return [
         return [
+            'ok' => 1.0,
             'connectionId' => 0,
             'connectionId' => 0,
             'n' => 0,
             'n' => 0,
             'syncMillis' => 0,
             'syncMillis' => 0,
             'writtenTo' => null,
             'writtenTo' => null,
             'err' => null,
             'err' => null,
-            'errmsg' => null,
         ];
         ];
     }
     }
 
 
@@ -381,19 +376,8 @@ class MongoCollection
                 TypeConverter::fromLegacy($newobj),
                 TypeConverter::fromLegacy($newobj),
                 $this->convertWriteConcernOptions($options)
                 $this->convertWriteConcernOptions($options)
             );
             );
-        } catch (\MongoDB\Driver\Exception\BulkWriteException $e) {
-            $writeResult = $e->getWriteResult();
-            $writeError = $writeResult->getWriteErrors()[0];
-            return [
-                'ok' => 0.0,
-                'nModified' => $writeResult->getModifiedCount(),
-                'n' => $writeResult->getMatchedCount(),
-                'err' => $writeError->getCode(),
-                'errmsg' => $writeError->getMessage(),
-                'updatedExisting' => $writeResult->getUpsertedCount() == 0,
-            ];
         } catch (\MongoDB\Driver\Exception\Exception $e) {
         } catch (\MongoDB\Driver\Exception\Exception $e) {
-            ExceptionConverter::toLegacy($e);
+            throw ExceptionConverter::toLegacy($e);
         }
         }
 
 
         if (! $result->isAcknowledged()) {
         if (! $result->isAcknowledged()) {
@@ -433,7 +417,7 @@ class MongoCollection
                 $this->convertWriteConcernOptions($options)
                 $this->convertWriteConcernOptions($options)
             );
             );
         } catch (\MongoDB\Driver\Exception\Exception $e) {
         } catch (\MongoDB\Driver\Exception\Exception $e) {
-            ExceptionConverter::toLegacy($e);
+            throw ExceptionConverter::toLegacy($e);
         }
         }
 
 
         if (! $result->isAcknowledged()) {
         if (! $result->isAcknowledged()) {
@@ -508,12 +492,16 @@ class MongoCollection
 
 
                 $options['projection'] = is_array($fields) ? TypeConverter::fromLegacy($fields) : [];
                 $options['projection'] = is_array($fields) ? TypeConverter::fromLegacy($fields) : [];
 
 
-                $document = $this->collection->findOneAndUpdate($query, $update, $options);
+                if (! \MongoDB\is_first_key_operator($update)) {
+                    $document = $this->collection->findOneAndReplace($query, $update, $options);
+                } else {
+                    $document = $this->collection->findOneAndUpdate($query, $update, $options);
+                }
             }
             }
         } catch (\MongoDB\Driver\Exception\ConnectionException $e) {
         } catch (\MongoDB\Driver\Exception\ConnectionException $e) {
             throw new MongoResultException($e->getMessage(), $e->getCode(), $e);
             throw new MongoResultException($e->getMessage(), $e->getCode(), $e);
         } catch (\MongoDB\Driver\Exception\Exception $e) {
         } catch (\MongoDB\Driver\Exception\Exception $e) {
-            ExceptionConverter::toLegacy($e, 'MongoResultException');
+            throw ExceptionConverter::toLegacy($e, 'MongoResultException');
         }
         }
 
 
         if ($document) {
         if ($document) {
@@ -538,7 +526,7 @@ class MongoCollection
         try {
         try {
             $document = $this->collection->findOne(TypeConverter::fromLegacy($query), $options);
             $document = $this->collection->findOne(TypeConverter::fromLegacy($query), $options);
         } catch (\MongoDB\Driver\Exception\Exception $e) {
         } catch (\MongoDB\Driver\Exception\Exception $e) {
-            ExceptionConverter::toLegacy($e);
+            throw ExceptionConverter::toLegacy($e);
         }
         }
 
 
         if ($document !== null) {
         if ($document !== null) {
@@ -572,15 +560,21 @@ class MongoCollection
         }
         }
 
 
         if (! is_array($keys) || ! count($keys)) {
         if (! is_array($keys) || ! count($keys)) {
-            throw new MongoException('keys cannot be empty');
+            throw new MongoException('index specification has no elements');
         }
         }
 
 
         // duplicate
         // duplicate
         $neededOptions = ['unique' => 1, 'sparse' => 1, 'expireAfterSeconds' => 1, 'background' => 1, 'dropDups' => 1];
         $neededOptions = ['unique' => 1, 'sparse' => 1, 'expireAfterSeconds' => 1, 'background' => 1, 'dropDups' => 1];
         $indexOptions = array_intersect_key($options, $neededOptions);
         $indexOptions = array_intersect_key($options, $neededOptions);
-        $indexes = $this->collection->listIndexes();
-        foreach ($indexes as $index) {
+        $indexes = iterator_to_array($this->collection->listIndexes());
+        $indexCount = count($indexes);
 
 
+        // listIndexes returns 0 for non-existing collections while the legacy driver returns 1
+        if ($indexCount === 0) {
+            $indexCount = 1;
+        }
+
+        foreach ($indexes as $index) {
             if (! empty($options['name']) && $index->getName() === $options['name']) {
             if (! empty($options['name']) && $index->getName() === $options['name']) {
                 throw new \MongoResultException(sprintf('index with name: %s already exists', $index->getName()));
                 throw new \MongoResultException(sprintf('index with name: %s already exists', $index->getName()));
             }
             }
@@ -595,8 +589,8 @@ class MongoCollection
 
 
                 return [
                 return [
                     'createdCollectionAutomatically' => false,
                     'createdCollectionAutomatically' => false,
-                    'numIndexesBefore' => count($indexes),
-                    'numIndexesAfter' => count($indexes),
+                    'numIndexesBefore' => $indexCount,
+                    'numIndexesAfter' => $indexCount,
                     'note' => 'all indexes already exist',
                     'note' => 'all indexes already exist',
                     'ok' => 1.0
                     'ok' => 1.0
                 ];
                 ];
@@ -606,13 +600,13 @@ class MongoCollection
         try {
         try {
             $this->collection->createIndex($keys, $this->convertWriteConcernOptions($options));
             $this->collection->createIndex($keys, $this->convertWriteConcernOptions($options));
         } catch (\MongoDB\Driver\Exception\Exception $e) {
         } catch (\MongoDB\Driver\Exception\Exception $e) {
-            ExceptionConverter::toLegacy($e);
+            throw ExceptionConverter::toLegacy($e);
         }
         }
 
 
         return [
         return [
             'createdCollectionAutomatically' => true,
             'createdCollectionAutomatically' => true,
-            'numIndexesBefore' => count($indexes),
-            'numIndexesAfter' => count($indexes) + 1,
+            'numIndexesBefore' => $indexCount,
+            'numIndexesAfter' => $indexCount + 1,
             'ok' => 1.0
             'ok' => 1.0
         ];
         ];
     }
     }
@@ -623,14 +617,12 @@ class MongoCollection
      * @link http://www.php.net/manual/en/mongocollection.ensureindex.php
      * @link http://www.php.net/manual/en/mongocollection.ensureindex.php
      * @param array $keys Field or fields to use as index.
      * @param array $keys Field or fields to use as index.
      * @param array $options [optional] This parameter is an associative array of the form array("optionname" => <boolean>, ...).
      * @param array $options [optional] This parameter is an associative array of the form array("optionname" => <boolean>, ...).
-     * @return boolean always true
+     * @return array Returns the database response.
      * @deprecated Use MongoCollection::createIndex() instead.
      * @deprecated Use MongoCollection::createIndex() instead.
      */
      */
     public function ensureIndex(array $keys, array $options = [])
     public function ensureIndex(array $keys, array $options = [])
     {
     {
-        $this->createIndex($keys, $options);
-
-        return true;
+        return $this->createIndex($keys, $options);
     }
     }
 
 
     /**
     /**
@@ -644,13 +636,25 @@ class MongoCollection
     {
     {
         if (is_string($keys)) {
         if (is_string($keys)) {
             $indexName = $keys;
             $indexName = $keys;
+            if (! preg_match('#_-?1$#', $indexName)) {
+                $indexName .= '_1';
+            }
         } elseif (is_array($keys)) {
         } elseif (is_array($keys)) {
             $indexName = \MongoDB\generate_index_name($keys);
             $indexName = \MongoDB\generate_index_name($keys);
         } else {
         } else {
             throw new \InvalidArgumentException();
             throw new \InvalidArgumentException();
         }
         }
 
 
-        return TypeConverter::toLegacy($this->collection->dropIndex($indexName));
+        try {
+            return TypeConverter::toLegacy($this->collection->dropIndex($indexName));
+        } catch (\MongoDB\Driver\Exception\Exception $e) {
+            return [
+                'nIndexesWas' => count($this->getIndexInfo()),
+                'errmsg' => $e->getMessage(),
+                'ok' => 0.0,
+                'code' => $e->getCode(),
+            ];
+        }
     }
     }
 
 
     /**
     /**
@@ -697,7 +701,7 @@ class MongoCollection
         try {
         try {
             return $this->collection->count(TypeConverter::fromLegacy($query), $options);
             return $this->collection->count(TypeConverter::fromLegacy($query), $options);
         } catch (\MongoDB\Driver\Exception\Exception $e) {
         } catch (\MongoDB\Driver\Exception\Exception $e) {
-            ExceptionConverter::toLegacy($e);
+            throw ExceptionConverter::toLegacy($e);
         }
         }
     }
     }
 
 
@@ -728,22 +732,27 @@ class MongoCollection
                 TypeConverter::fromLegacy($document),
                 TypeConverter::fromLegacy($document),
                 $this->convertWriteConcernOptions($options)
                 $this->convertWriteConcernOptions($options)
             );
             );
-        } catch (\MongoDB\Driver\Exception\Exception $e) {
-            ExceptionConverter::toLegacy($e);
-        }
 
 
-        if (!$result->isAcknowledged()) {
-            return true;
-        }
+            if (! $result->isAcknowledged()) {
+                return true;
+            }
 
 
-        return [
-            'ok' => 1.0,
-            'nModified' => $result->getModifiedCount(),
-            'n' => $result->getMatchedCount(),
-            'err' => null,
-            'errmsg' => null,
-            'updatedExisting' => $result->getUpsertedCount() == 0,
-        ];
+            $resultArray = [
+                'ok' => 1.0,
+                'nModified' => $result->getModifiedCount(),
+                'n' => $result->getUpsertedCount() + $result->getModifiedCount(),
+                'err' => null,
+                'errmsg' => null,
+                'updatedExisting' => $result->getUpsertedCount() == 0,
+            ];
+            if ($result->getUpsertedId() !== null) {
+                $resultArray['upserted'] = TypeConverter::toLegacy($result->getUpsertedId());
+            }
+
+            return $resultArray;
+        } catch (\MongoDB\Driver\Exception\Exception $e) {
+            throw ExceptionConverter::toLegacy($e);
+        }
     }
     }
 
 
     /**
     /**
@@ -907,16 +916,13 @@ class MongoCollection
     {
     {
         $checkKeys = function($array) {
         $checkKeys = function($array) {
             foreach (array_keys($array) as $key) {
             foreach (array_keys($array) as $key) {
-                if (is_int($key) || empty($key) || strpos($key, '*') === 1) {
+                if (empty($key) || strpos($key, '*') === 1) {
                     throw new \MongoException('document contain invalid key');
                     throw new \MongoException('document contain invalid key');
                 }
                 }
             }
             }
         };
         };
 
 
         if (is_array($document)) {
         if (is_array($document)) {
-            if (empty($document)) {
-                throw new \MongoException('document cannot be empty');
-            }
             if (! isset($document['_id'])) {
             if (! isset($document['_id'])) {
                 $document['_id'] = new \MongoId();
                 $document['_id'] = new \MongoId();
             }
             }
@@ -925,9 +931,13 @@ class MongoCollection
 
 
             return $document['_id'];
             return $document['_id'];
         } elseif (is_object($document)) {
         } elseif (is_object($document)) {
-            if (empty((array) $document)) {
-                throw new \MongoException('document cannot be empty');
+            $reflectionObject = new \ReflectionObject($document);
+            foreach ($reflectionObject->getProperties() as $property) {
+                if (! $property->isPublic()) {
+                    throw new \MongoException('zero-length keys are not allowed, did you use $ with double quotes?');
+                }
             }
             }
+
             if (! isset($document->_id)) {
             if (! isset($document->_id)) {
                 $document->_id = new \MongoId();
                 $document->_id = new \MongoId();
             }
             }

+ 19 - 0
lib/Mongo/MongoCommandCursor.php

@@ -81,4 +81,23 @@ class MongoCommandCursor extends AbstractCursor implements MongoCursorInterface
             'fields' => null,
             'fields' => null,
         ];
         ];
     }
     }
+
+    /**
+     * @return array
+     */
+    protected function getIterationInfo()
+    {
+        $iterationInfo = parent::getIterationInfo();
+
+        if ($iterationInfo['started_iterating']) {
+            $iterationInfo += [
+                'firstBatchAt' => $iterationInfo['at'],
+                'firstBatchNumReturned' => $iterationInfo['numReturned'],
+            ];
+            $iterationInfo['at'] = 0;
+            $iterationInfo['numReturned'] = 0;
+        }
+
+        return $iterationInfo;
+    }
 }
 }

+ 2 - 2
lib/Mongo/MongoCursor.php

@@ -146,7 +146,7 @@ class MongoCursor extends AbstractCursor implements Iterator
         } catch (\MongoDB\Driver\Exception\ExecutionTimeoutException $e) {
         } catch (\MongoDB\Driver\Exception\ExecutionTimeoutException $e) {
             throw new MongoCursorTimeoutException($e->getMessage(), $e->getCode(), $e);
             throw new MongoCursorTimeoutException($e->getMessage(), $e->getCode(), $e);
         } catch (\MongoDB\Driver\Exception\Exception $e) {
         } catch (\MongoDB\Driver\Exception\Exception $e) {
-            ExceptionConverter::toLegacy($e);
+            throw ExceptionConverter::toLegacy($e);
         }
         }
 
 
         return $count;
         return $count;
@@ -167,7 +167,7 @@ class MongoCursor extends AbstractCursor implements Iterator
         } catch (\MongoDB\Driver\Exception\ExecutionTimeoutException $e) {
         } catch (\MongoDB\Driver\Exception\ExecutionTimeoutException $e) {
             throw new MongoCursorTimeoutException($e->getMessage(), $e->getCode(), $e);
             throw new MongoCursorTimeoutException($e->getMessage(), $e->getCode(), $e);
         } catch (\MongoDB\Driver\Exception\Exception $e) {
         } catch (\MongoDB\Driver\Exception\Exception $e) {
-            ExceptionConverter::toLegacy($e);
+            throw ExceptionConverter::toLegacy($e);
         }
         }
     }
     }
 
 

+ 13 - 14
lib/Mongo/MongoDB.php

@@ -112,8 +112,7 @@ class MongoDB
     public function __set($name, $value)
     public function __set($name, $value)
     {
     {
         if ($name === 'w' || $name === 'wtimeout') {
         if ($name === 'w' || $name === 'wtimeout') {
-            $this->setWriteConcernFromArray([$name => $value] + $this->getWriteConcern());
-            $this->createDatabaseObject();
+            trigger_error("The '{$name}' property is read-only", E_DEPRECATED);
         }
         }
     }
     }
 
 
@@ -134,7 +133,7 @@ class MongoDB
         try {
         try {
             $collections = $this->db->listCollections($options);
             $collections = $this->db->listCollections($options);
         } catch (\MongoDB\Driver\Exception\Exception $e) {
         } catch (\MongoDB\Driver\Exception\Exception $e) {
-            ExceptionConverter::toLegacy($e);
+            throw ExceptionConverter::toLegacy($e);
         }
         }
 
 
         $getCollectionInfo = function (CollectionInfo $collectionInfo) {
         $getCollectionInfo = function (CollectionInfo $collectionInfo) {
@@ -164,7 +163,7 @@ class MongoDB
         try {
         try {
             $collections = $this->db->listCollections($options);
             $collections = $this->db->listCollections($options);
         } catch (\MongoDB\Driver\Exception\Exception $e) {
         } catch (\MongoDB\Driver\Exception\Exception $e) {
-            ExceptionConverter::toLegacy($e);
+            throw ExceptionConverter::toLegacy($e);
         }
         }
 
 
         $getCollectionName = function (CollectionInfo $collectionInfo) {
         $getCollectionName = function (CollectionInfo $collectionInfo) {
@@ -270,6 +269,10 @@ class MongoDB
     public function createCollection($name, $options)
     public function createCollection($name, $options)
     {
     {
         try {
         try {
+            if (isset($options['capped'])) {
+                $options['capped'] = (bool) $options['capped'];
+            }
+
             $this->db->createCollection($name, $options);
             $this->db->createCollection($name, $options);
         } catch (\MongoDB\Driver\Exception\Exception $e) {
         } catch (\MongoDB\Driver\Exception\Exception $e) {
             return false;
             return false;
@@ -322,10 +325,10 @@ class MongoDB
             $id = $document_or_id;
             $id = $document_or_id;
         } elseif (is_object($document_or_id)) {
         } elseif (is_object($document_or_id)) {
             if (! isset($document_or_id->_id)) {
             if (! isset($document_or_id->_id)) {
-                return null;
+                $id = $document_or_id;
+            } else {
+                $id = $document_or_id->_id;
             }
             }
-
-            $id = $document_or_id->_id;
         } elseif (is_array($document_or_id)) {
         } elseif (is_array($document_or_id)) {
             if (! isset($document_or_id['_id'])) {
             if (! isset($document_or_id['_id'])) {
                 return null;
                 return null;
@@ -336,7 +339,7 @@ class MongoDB
             $id = $document_or_id;
             $id = $document_or_id;
         }
         }
 
 
-        return MongoDBRef::create($collection, $id, $this->name);
+        return MongoDBRef::create($collection, $id);
     }
     }
 
 
 
 
@@ -380,16 +383,12 @@ class MongoDB
             $cursor->setReadPreference($this->getReadPreference());
             $cursor->setReadPreference($this->getReadPreference());
 
 
             return iterator_to_array($cursor)[0];
             return iterator_to_array($cursor)[0];
-        } catch (\MongoDB\Driver\Exception\ExecutionTimeoutException $e) {
-            throw new MongoCursorTimeoutException($e->getMessage(), $e->getCode(), $e);
-        } catch (\MongoDB\Driver\Exception\RuntimeException $e) {
+        } catch (\MongoDB\Driver\Exception\Exception $e) {
             return [
             return [
-                'ok' => 0,
+                'ok' => 0.0,
                 'errmsg' => $e->getMessage(),
                 'errmsg' => $e->getMessage(),
                 'code' => $e->getCode(),
                 'code' => $e->getCode(),
             ];
             ];
-        } catch (\MongoDB\Driver\Exception\Excepiton $e) {
-            ExceptionConverter::toLegacy($e);
         }
         }
     }
     }
 
 

+ 10 - 10
lib/Mongo/MongoGridFS.php

@@ -15,8 +15,6 @@
 
 
 class MongoGridFS extends MongoCollection
 class MongoGridFS extends MongoCollection
 {
 {
-    const DEFAULT_CHUNK_SIZE = 262144; // 256 kb
-
     const ASCENDING = 1;
     const ASCENDING = 1;
     const DESCENDING = -1;
     const DESCENDING = -1;
 
 
@@ -45,6 +43,8 @@ class MongoGridFS extends MongoCollection
 
 
     private $prefix;
     private $prefix;
 
 
+    private $defaultChunkSize = 261120;
+
     /**
     /**
      * Files as stored across two collections, the first containing file meta
      * Files as stored across two collections, the first containing file meta
      * information, the second containing chunks of the actual file. By default,
      * information, the second containing chunks of the actual file. By default,
@@ -203,14 +203,14 @@ class MongoGridFS extends MongoCollection
         try {
         try {
             $file = $this->insertFile($record, $options);
             $file = $this->insertFile($record, $options);
         } catch (MongoException $e) {
         } catch (MongoException $e) {
-            throw new MongoGridFSException('Cannot insert file record', 0, $e);
+            throw new MongoGridFSException('Could not store file: '. $e->getMessage(), 0, $e);
         }
         }
 
 
         try {
         try {
             $this->insertChunksFromBytes($bytes, $file);
             $this->insertChunksFromBytes($bytes, $file);
         } catch (MongoException $e) {
         } catch (MongoException $e) {
             $this->delete($file['_id']);
             $this->delete($file['_id']);
-            throw new MongoGridFSException('Error while inserting chunks', 0, $e);
+            throw new MongoGridFSException('Could not store file: ' . $e->getMessage(), 0, $e);
         }
         }
 
 
         return $file['_id'];
         return $file['_id'];
@@ -253,14 +253,14 @@ class MongoGridFS extends MongoCollection
         try {
         try {
             $file = $this->insertFile($record, $options);
             $file = $this->insertFile($record, $options);
         } catch (MongoException $e) {
         } catch (MongoException $e) {
-            throw new MongoGridFSException('Cannot insert file record', 0, $e);
+            throw new MongoGridFSException('Could not store file: ' . $e->getMessage(), 0, $e);
         }
         }
 
 
         try {
         try {
             $length = $this->insertChunksFromFile($handle, $file, $md5);
             $length = $this->insertChunksFromFile($handle, $file, $md5);
         } catch (MongoException $e) {
         } catch (MongoException $e) {
             $this->delete($file['_id']);
             $this->delete($file['_id']);
-            throw new MongoGridFSException('Error while inserting chunks', 0, $e);
+            throw new MongoGridFSException('Could not store file: ' . $e->getMessage(), 0, $e);
         }
         }
 
 
 
 
@@ -273,7 +273,7 @@ class MongoGridFS extends MongoCollection
             try {
             try {
                 $update['md5'] = $md5;
                 $update['md5'] = $md5;
             } catch (MongoException $e) {
             } catch (MongoException $e) {
-                throw new MongoGridFSException('Error computing MD5 checksum', 0, $e);
+                throw new MongoGridFSException('Could not store file: ' . $e->getMessage(), 0, $e);
             }
             }
         }
         }
 
 
@@ -281,11 +281,11 @@ class MongoGridFS extends MongoCollection
             try {
             try {
                 $result = $this->update(['_id' => $file['_id']], ['$set' => $update]);
                 $result = $this->update(['_id' => $file['_id']], ['$set' => $update]);
                 if (! $this->isOKResult($result)) {
                 if (! $this->isOKResult($result)) {
-                    throw new MongoGridFSException('Error updating file record');
+                    throw new MongoGridFSException('Could not store file');
                 }
                 }
             } catch (MongoException $e) {
             } catch (MongoException $e) {
                 $this->delete($file['_id']);
                 $this->delete($file['_id']);
-                throw new MongoGridFSException('Error updating file record', 0, $e);
+                throw new MongoGridFSException('Could not store file: ' . $e->getMessage(), 0, $e);
             }
             }
 
 
         }
         }
@@ -427,7 +427,7 @@ class MongoGridFS extends MongoCollection
         $record += [
         $record += [
             '_id' => new MongoId(),
             '_id' => new MongoId(),
             'uploadDate' => new MongoDate(),
             'uploadDate' => new MongoDate(),
-            'chunkSize' => self::DEFAULT_CHUNK_SIZE,
+            'chunkSize' => $this->defaultChunkSize,
         ];
         ];
 
 
         $result = $this->insert($record, $options);
         $result = $this->insert($record, $options);

+ 48 - 15
lib/Mongo/MongoWriteBatch.php

@@ -115,38 +115,71 @@ class MongoWriteBatch
 
 
         try {
         try {
             $result = $collection->BulkWrite($this->items, $options);
             $result = $collection->BulkWrite($this->items, $options);
-            $ok = 1.0;
+            $ok = true;
         } catch (\MongoDB\Driver\Exception\BulkWriteException $e) {
         } catch (\MongoDB\Driver\Exception\BulkWriteException $e) {
             $result = $e->getWriteResult();
             $result = $e->getWriteResult();
-            $ok = 0.0;
+            $ok = false;
         }
         }
 
 
-        if ($ok === 1.0) {
+        if ($ok === true) {
             $this->items = [];
             $this->items = [];
         }
         }
 
 
-        return [
-            'ok' => $ok,
-            'nInserted' => $result->getInsertedCount(),
-            'nMatched' => $result->getMatchedCount(),
-            'nModified' => $result->getModifiedCount(),
-            'nUpserted' => $result->getUpsertedCount(),
-            'nRemoved' => $result->getDeletedCount(),
-        ];
+        switch ($this->batchType) {
+            case self::COMMAND_UPDATE:
+                $upsertedIds = [];
+                foreach ($result->getUpsertedIds() as $index => $id) {
+                    $upsertedIds[] = [
+                        'index' => $index,
+                        '_id' => TypeConverter::toLegacy($id)
+                    ];
+                }
+
+                $result = [
+                    'nMatched' => $result->getMatchedCount(),
+                    'nModified' => $result->getModifiedCount(),
+                    'nUpserted' => $result->getUpsertedCount(),
+                    'ok' => $ok,
+                ];
+
+                if (count($upsertedIds)) {
+                    $result['upserted'] = $upsertedIds;
+                }
+
+                return $result;
+
+            case self::COMMAND_DELETE:
+                return [
+                    'nRemoved' => $result->getDeletedCount(),
+                    'ok' => $ok,
+                ];
+
+            case self::COMMAND_INSERT:
+                return [
+                    'nInserted' => $result->getInsertedCount(),
+                    'ok' => $ok,
+                ];
+        }
     }
     }
 
 
     private function validate(array $item)
     private function validate(array $item)
     {
     {
         switch ($this->batchType) {
         switch ($this->batchType) {
             case self::COMMAND_UPDATE:
             case self::COMMAND_UPDATE:
-                if (! isset($item['q']) || ! isset($item['u'])) {
-                    throw new Exception('invalid item');
+                if (! isset($item['q'])) {
+                    throw new Exception("Expected \$item to contain 'q' key");
+                }
+                if (! isset($item['u'])) {
+                    throw new Exception("Expected \$item to contain 'u' key");
                 }
                 }
                 break;
                 break;
 
 
             case self::COMMAND_DELETE:
             case self::COMMAND_DELETE:
-                if (! isset($item['q']) || ! isset($item['limit'])) {
-                    throw new Exception('invalid item');
+                if (! isset($item['q'])) {
+                    throw new Exception("Expected \$item to contain 'q' key");
+                }
+                if (! isset($item['limit'])) {
+                    throw new Exception("Expected \$item to contain 'limit' key");
                 }
                 }
                 break;
                 break;
         }
         }

+ 22 - 18
lib/Mongo/functions.php

@@ -15,24 +15,28 @@
 
 
 use Alcaeus\MongoDbAdapter\TypeConverter;
 use Alcaeus\MongoDbAdapter\TypeConverter;
 
 
-/**
- * Deserializes a BSON object into a PHP array
- *
- * @param string $bson The BSON to be deserialized.
- * @return array Returns the deserialized BSON object.
- */
-function bson_decode($bson)
-{
-    return TypeConverter::toLegacy(\MongoDB\BSON\toPHP($bson));
+if (! function_exists('bson_decode')) {
+    /**
+     * Deserializes a BSON object into a PHP array
+     *
+     * @param string $bson The BSON to be deserialized.
+     * @return array Returns the deserialized BSON object.
+     */
+    function bson_decode($bson)
+    {
+        return TypeConverter::toLegacy(\MongoDB\BSON\toPHP($bson));
+    }
 }
 }
 
 
-/**
- * Serializes a PHP variable into a BSON string
- *
- * @param mixed $anything The variable to be serialized.
- * @return string Returns the serialized string.
- */
-function bson_encode($anything)
-{
-    return \MongoDB\BSON\fromPHP(TypeConverter::fromLegacy($anything));
+if (! function_exists('bson_encode')) {
+    /**
+     * Serializes a PHP variable into a BSON string
+     *
+     * @param mixed $anything The variable to be serialized.
+     * @return string Returns the serialized string.
+     */
+    function bson_encode($anything)
+    {
+        return \MongoDB\BSON\fromPHP(TypeConverter::fromLegacy($anything));
+    }
 }
 }

+ 6 - 0
phpunit.xml.dist

@@ -9,6 +9,12 @@
          stopOnFailure="false"
          stopOnFailure="false"
          syntaxCheck="false"
          syntaxCheck="false"
 >
 >
+    <php>
+        <!-- Disable deprecation warnings -->
+        <!-- php -r 'echo -1 & ~E_USER_DEPRECATED & ~E_DEPRECATED;' -->
+        <ini name="error_reporting" value="-24577"/>
+    </php>
+
     <testsuites>
     <testsuites>
         <testsuite name="Mongo driver adapter test suite">
         <testsuite name="Mongo driver adapter test suite">
             <directory>./tests/Alcaeus/MongoDbAdapter/</directory>
             <directory>./tests/Alcaeus/MongoDbAdapter/</directory>

+ 1 - 7
tests/Alcaeus/MongoDbAdapter/ExceptionConverterTest.php

@@ -7,18 +7,12 @@ use Alcaeus\MongoDbAdapter\ExceptionConverter;
 
 
 class ExceptionConverterTest extends \PHPUnit_Framework_TestCase
 class ExceptionConverterTest extends \PHPUnit_Framework_TestCase
 {
 {
-    public function testThrowException()
-    {
-        $this->setExpectedException('MongoException');
-        ExceptionConverter::toLegacy(new Exception\InvalidArgumentException());
-    }
-
     /**
     /**
      * @dataProvider exceptionProvider
      * @dataProvider exceptionProvider
      */
      */
     public function testConvertException($e, $expectedClass)
     public function testConvertException($e, $expectedClass)
     {
     {
-        $exception = ExceptionConverter::convertException($e);
+        $exception = ExceptionConverter::toLegacy($e);
         $this->assertInstanceOf($expectedClass, $exception);
         $this->assertInstanceOf($expectedClass, $exception);
         $this->assertSame($e->getMessage(), $exception->getMessage());
         $this->assertSame($e->getMessage(), $exception->getMessage());
         $this->assertSame($e->getCode(), $exception->getCode());
         $this->assertSame($e->getCode(), $exception->getCode());

+ 14 - 1
tests/Alcaeus/MongoDbAdapter/MongoBinDataTest.php

@@ -1,6 +1,7 @@
 <?php
 <?php
 
 
 namespace Alcaeus\MongoDbAdapter\Tests;
 namespace Alcaeus\MongoDbAdapter\Tests;
+use Alcaeus\MongoDbAdapter\TypeInterface;
 
 
 /**
 /**
  * @author alcaeus <alcaeus@alcaeus.org>
  * @author alcaeus <alcaeus@alcaeus.org>
@@ -13,7 +14,17 @@ class MongoBinDataTest extends TestCase
         $this->assertAttributeSame('foo', 'bin', $bin);
         $this->assertAttributeSame('foo', 'bin', $bin);
         $this->assertAttributeSame(\MongoBinData::FUNC, 'type', $bin);
         $this->assertAttributeSame(\MongoBinData::FUNC, 'type', $bin);
 
 
-        $this->assertSame('<Mongo Binary Data>', (string) $bin);
+        $this->assertSame('<Mongo Binary Data>', (string)$bin);
+
+        return $bin;
+    }
+
+    /**
+     * @depends testCreate
+     */
+    public function testConvertToBson(\MongoBinData $bin)
+    {
+        $this->skipTestUnless($bin instanceof TypeInterface);
 
 
         $bsonBinary = $bin->toBSONType();
         $bsonBinary = $bin->toBSONType();
         $this->assertInstanceOf('MongoDB\BSON\Binary', $bsonBinary);
         $this->assertInstanceOf('MongoDB\BSON\Binary', $bsonBinary);
@@ -24,6 +35,8 @@ class MongoBinDataTest extends TestCase
 
 
     public function testCreateWithBsonBinary()
     public function testCreateWithBsonBinary()
     {
     {
+        $this->skipTestUnless(in_array(TypeInterface::class, class_implements('MongoBinData')));
+
         $bsonBinary = new \MongoDB\BSON\Binary('foo', \MongoDB\BSON\Binary::TYPE_UUID);
         $bsonBinary = new \MongoDB\BSON\Binary('foo', \MongoDB\BSON\Binary::TYPE_UUID);
         $bin = new \MongoBinData($bsonBinary);
         $bin = new \MongoBinData($bsonBinary);
 
 

+ 18 - 21
tests/Alcaeus/MongoDbAdapter/MongoClientTest.php

@@ -7,21 +7,6 @@ namespace Alcaeus\MongoDbAdapter\Tests;
  */
  */
 class MongoClientTest extends TestCase
 class MongoClientTest extends TestCase
 {
 {
-    public function testConnectAndDisconnect()
-    {
-        $client = $this->getClient();
-        $this->assertTrue($client->connected);
-
-        $client->close();
-        $this->assertFalse($client->connected);
-    }
-
-    public function testClientWithoutAutomaticConnect()
-    {
-        $client = $this->getClient([]);
-        $this->assertFalse($client->connected);
-    }
-
     public function testGetDb()
     public function testGetDb()
     {
     {
         $client = $this->getClient();
         $client = $this->getClient();
@@ -63,16 +48,17 @@ class MongoClientTest extends TestCase
     public function testGetHosts()
     public function testGetHosts()
     {
     {
         $client = $this->getClient();
         $client = $this->getClient();
+        $hosts = $client->getHosts();
         $this->assertArraySubset(
         $this->assertArraySubset(
             [
             [
-                'localhost:27017' => [
+                'localhost:27017;-;.;' . getmypid() => [
                     'host' => 'localhost',
                     'host' => 'localhost',
                     'port' => 27017,
                     'port' => 27017,
                     'health' => 1,
                     'health' => 1,
                     'state' => 0,
                     'state' => 0,
                 ],
                 ],
             ],
             ],
-            $client->getHosts()
+            $hosts
         );
         );
     }
     }
 
 
@@ -81,14 +67,13 @@ class MongoClientTest extends TestCase
         $client = $this->getClient();
         $client = $this->getClient();
         $this->assertSame(['type' => \MongoClient::RP_PRIMARY], $client->getReadPreference());
         $this->assertSame(['type' => \MongoClient::RP_PRIMARY], $client->getReadPreference());
 
 
-        $this->assertTrue($client->setReadPreference(\MongoClient::RP_SECONDARY, ['a' => 'b']));
-        $this->assertSame(['type' => \MongoClient::RP_SECONDARY, 'tagsets' => ['a' => 'b']], $client->getReadPreference());
+        $this->assertTrue($client->setReadPreference(\MongoClient::RP_SECONDARY, [['a' => 'b']]));
+        $this->assertSame(['type' => \MongoClient::RP_SECONDARY, 'tagsets' => [['a' => 'b']]], $client->getReadPreference());
     }
     }
 
 
     public function testWriteConcern()
     public function testWriteConcern()
     {
     {
         $client = $this->getClient();
         $client = $this->getClient();
-        $this->assertSame(['w' => 1, 'wtimeout' => 0], $client->getWriteConcern());
 
 
         $this->assertTrue($client->setWriteConcern('majority', 100));
         $this->assertTrue($client->setWriteConcern('majority', 100));
         $this->assertSame(['w' => 'majority', 'wtimeout' => 100], $client->getWriteConcern());
         $this->assertSame(['w' => 'majority', 'wtimeout' => 100], $client->getWriteConcern());
@@ -103,7 +88,19 @@ class MongoClientTest extends TestCase
         $this->assertSame(1.0, $databases['ok']);
         $this->assertSame(1.0, $databases['ok']);
         $this->assertArrayHasKey('totalSize', $databases);
         $this->assertArrayHasKey('totalSize', $databases);
         $this->assertArrayHasKey('databases', $databases);
         $this->assertArrayHasKey('databases', $databases);
-        $this->assertContains('mongo-php-adapter', $databases['databases']);
+
+        foreach ($databases['databases'] as $database) {
+            $this->assertArrayHasKey('name', $database);
+            $this->assertArrayHasKey('empty', $database);
+            $this->assertArrayHasKey('sizeOnDisk', $database);
+
+            if ($database['name'] == 'mongo-php-adapter') {
+                $this->assertFalse($database['empty']);
+                return;
+            }
+        }
+
+        $this->fail('Could not find mongo-php-adapter database in list');
     }
     }
 
 
     public function testNoPrefixUri()
     public function testNoPrefixUri()

+ 14 - 1
tests/Alcaeus/MongoDbAdapter/MongoCodeTest.php

@@ -1,6 +1,7 @@
 <?php
 <?php
 
 
 namespace Alcaeus\MongoDbAdapter\Tests;
 namespace Alcaeus\MongoDbAdapter\Tests;
+use Alcaeus\MongoDbAdapter\TypeInterface;
 
 
 /**
 /**
  * @author alcaeus <alcaeus@alcaeus.org>
  * @author alcaeus <alcaeus@alcaeus.org>
@@ -13,7 +14,17 @@ class MongoCodeTest extends TestCase
         $this->assertAttributeSame('code', 'code', $code);
         $this->assertAttributeSame('code', 'code', $code);
         $this->assertAttributeSame(['scope' => 'bleh'], 'scope', $code);
         $this->assertAttributeSame(['scope' => 'bleh'], 'scope', $code);
 
 
-        $this->assertSame('code', (string) $code);
+        $this->assertSame('code', (string)$code);
+
+        return $code;
+    }
+
+    /**
+     * @depends testCreate
+     */
+    public function testConvertToBson(\MongoCode $code)
+    {
+        $this->skipTestUnless($code instanceof TypeInterface);
 
 
         $bsonCode = $code->toBSONType();
         $bsonCode = $code->toBSONType();
         $this->assertInstanceOf('MongoDB\BSON\Javascript', $bsonCode);
         $this->assertInstanceOf('MongoDB\BSON\Javascript', $bsonCode);
@@ -21,6 +32,8 @@ class MongoCodeTest extends TestCase
 
 
     public function testCreateWithBsonObject()
     public function testCreateWithBsonObject()
     {
     {
+        $this->skipTestUnless(in_array(TypeInterface::class, class_implements('MongoCode')));
+
         $bsonCode = new \MongoDB\BSON\Javascript('code', ['scope' => 'bleh']);
         $bsonCode = new \MongoDB\BSON\Javascript('code', ['scope' => 'bleh']);
         $code = new \MongoCode($bsonCode);
         $code = new \MongoCode($bsonCode);
 
 

+ 127 - 96
tests/Alcaeus/MongoDbAdapter/MongoCollectionTest.php

@@ -43,7 +43,7 @@ class MongoCollectionTest extends TestCase
 
 
     public function testInsertInvalidData()
     public function testInsertInvalidData()
     {
     {
-        $this->setExpectedException('PHPUnit_Framework_Error_Warning', 'ongoCollection::insert expects parameter 1 to be an array or object, integer given');
+        $this->setExpectedException('PHPUnit_Framework_Error_Warning', 'MongoCollection::insert(): expects parameter 1 to be an array or object, integer given');
 
 
         $document = 8;
         $document = 8;
         $this->getCollection()->insert($document);
         $this->getCollection()->insert($document);
@@ -51,33 +51,33 @@ class MongoCollectionTest extends TestCase
 
 
     public function testInsertEmptyArray()
     public function testInsertEmptyArray()
     {
     {
-        $this->setExpectedException('MongoException', 'document cannot be empty');
-
         $document = [];
         $document = [];
         $this->getCollection()->insert($document);
         $this->getCollection()->insert($document);
+
+        $this->assertSame(1, $this->getCollection()->count());
     }
     }
 
 
     public function testInsertArrayWithNumericKeys()
     public function testInsertArrayWithNumericKeys()
     {
     {
-        $this->setExpectedException('MongoException', 'document contain invalid key');
-
         $document = [1 => 'foo'];
         $document = [1 => 'foo'];
         $this->getCollection()->insert($document);
         $this->getCollection()->insert($document);
+
+        $this->assertSame(1, $this->getCollection()->count(['_id' => $document['_id']]));
     }
     }
 
 
     public function testInsertEmptyObject()
     public function testInsertEmptyObject()
     {
     {
-        $this->setExpectedException('MongoException', 'document cannot be empty');
-
         $document = (object) [];
         $document = (object) [];
         $this->getCollection()->insert($document);
         $this->getCollection()->insert($document);
+
+        $this->assertSame(1, $this->getCollection()->count());
     }
     }
 
 
     public function testInsertObjectWithPrivateProperties()
     public function testInsertObjectWithPrivateProperties()
     {
     {
-        $this->setExpectedException('MongoException', 'document contain invalid key');
+        $this->setExpectedException('MongoException', 'zero-length keys are not allowed, did you use $ with double quotes?');
 
 
-        $document = $this->getCollection();
+        $document = new PrivatePropertiesStub();
         $this->getCollection()->insert($document);
         $this->getCollection()->insert($document);
     }
     }
 
 
@@ -91,14 +91,8 @@ class MongoCollectionTest extends TestCase
         $collection->insert($document);
         $collection->insert($document);
 
 
         unset($document['_id']);
         unset($document['_id']);
-        $this->assertArraySubset(
-            [
-                'ok' => 0.0,
-                'n' => 0,
-                'err' => 11000,
-            ],
-            $collection->insert($document)
-        );
+        $this->setExpectedExceptionRegExp('MongoDuplicateKeyException', '/E11000 duplicate key error .* mongo-php-adapter\.test/');
+        $collection->insert($document);
     }
     }
 
 
     public function testUnacknowledgedWrite()
     public function testUnacknowledgedWrite()
@@ -109,7 +103,10 @@ class MongoCollectionTest extends TestCase
 
 
     public function testInsertWriteConcernException()
     public function testInsertWriteConcernException()
     {
     {
-        $this->setExpectedException('MongoConnectionException');
+        $this->setExpectedException(
+            'MongoWriteConcernException',
+            "cannot use 'w' > 1 when a host is not replicated"
+        );
 
 
         $document = ['foo' => 'bar'];
         $document = ['foo' => 'bar'];
         $this->getCollection()->insert($document, ['w' => 2]);
         $this->getCollection()->insert($document, ['w' => 2]);
@@ -118,19 +115,18 @@ class MongoCollectionTest extends TestCase
     public function testInsertMany()
     public function testInsertMany()
     {
     {
         $expected = [
         $expected = [
-            'connectionId' => 0,
+            'ok' => 1.0,
             'n' => 0,
             'n' => 0,
             'syncMillis' => 0,
             'syncMillis' => 0,
             'writtenTo' => null,
             'writtenTo' => null,
             'err' => null,
             'err' => null,
-            'errmsg' => null
         ];
         ];
 
 
         $documents = [
         $documents = [
             ['foo' => 'bar'],
             ['foo' => 'bar'],
             ['bar' => 'foo']
             ['bar' => 'foo']
         ];
         ];
-        $this->assertSame($expected, $this->getCollection()->batchInsert($documents));
+        $this->assertArraySubset($expected, $this->getCollection()->batchInsert($documents));
 
 
         foreach ($documents as $document) {
         foreach ($documents as $document) {
             $this->assertInstanceOf('MongoId', $document['_id']);
             $this->assertInstanceOf('MongoId', $document['_id']);
@@ -141,19 +137,18 @@ class MongoCollectionTest extends TestCase
     public function testInsertManyWithNonNumericKeys()
     public function testInsertManyWithNonNumericKeys()
     {
     {
         $expected = [
         $expected = [
-            'connectionId' => 0,
+            'ok' => 1.0,
             'n' => 0,
             'n' => 0,
             'syncMillis' => 0,
             'syncMillis' => 0,
             'writtenTo' => null,
             'writtenTo' => null,
             'err' => null,
             'err' => null,
-            'errmsg' => null
         ];
         ];
 
 
         $documents = [
         $documents = [
             'a' => ['foo' => 'bar'],
             'a' => ['foo' => 'bar'],
             'b' => ['bar' => 'foo']
             'b' => ['bar' => 'foo']
         ];
         ];
-        $this->assertSame($expected, $this->getCollection()->batchInsert($documents));
+        $this->assertArraySubset($expected, $this->getCollection()->batchInsert($documents));
 
 
         $newCollection = $this->getCheckDatabase()->selectCollection('test');
         $newCollection = $this->getCheckDatabase()->selectCollection('test');
         $this->assertSame(2, $newCollection->count());
         $this->assertSame(2, $newCollection->count());
@@ -162,19 +157,18 @@ class MongoCollectionTest extends TestCase
     public function testBatchInsertContinuesOnError()
     public function testBatchInsertContinuesOnError()
     {
     {
         $expected = [
         $expected = [
-            'connectionId' => 0,
+            'ok' => 1.0,
             'n' => 0,
             'n' => 0,
             'syncMillis' => 0,
             'syncMillis' => 0,
             'writtenTo' => null,
             'writtenTo' => null,
             'err' => null,
             'err' => null,
-            'errmsg' => null
         ];
         ];
 
 
         $documents = [
         $documents = [
             8,
             8,
             'b' => ['bar' => 'foo']
             'b' => ['bar' => 'foo']
         ];
         ];
-        $this->assertSame($expected, $this->getCollection()->batchInsert($documents, ['continueOnError' => true]));
+        $this->assertArraySubset($expected, $this->getCollection()->batchInsert($documents, ['continueOnError' => true]));
 
 
         $newCollection = $this->getCheckDatabase()->selectCollection('test');
         $newCollection = $this->getCheckDatabase()->selectCollection('test');
         $this->assertSame(1, $newCollection->count());
         $this->assertSame(1, $newCollection->count());
@@ -182,10 +176,11 @@ class MongoCollectionTest extends TestCase
 
 
     public function testBatchInsertException()
     public function testBatchInsertException()
     {
     {
-        $this->setExpectedException('MongoConnectionException');
+        $this->setExpectedException('MongoDuplicateKeyException', 'E11000 duplicate key error index: mongo-php-adapter.test.$_id_');
 
 
-        $documents = [['foo' => 'bar']];
-        $this->getCollection()->batchInsert($documents, ['w' => 2]);
+        $id = new \MongoId();
+        $documents = [['_id' => $id, 'foo' => 'bar'], ['_id' => $id, 'foo' => 'bleh']];
+        $this->getCollection()->batchInsert($documents);
     }
     }
 
 
     public function testBatchInsertEmptyBatchException()
     public function testBatchInsertEmptyBatchException()
@@ -198,7 +193,7 @@ class MongoCollectionTest extends TestCase
 
 
     public function testUpdateWriteConcern()
     public function testUpdateWriteConcern()
     {
     {
-        $this->setExpectedException('MongoConnectionException'); // does not match driver
+        $this->setExpectedException('MongoWriteConcernException', "cannot use 'w' > 1 when a host is not replicated");
 
 
         $this->getCollection()->update([], ['$set' => ['foo' => 'bar']], ['w' => 2]);
         $this->getCollection()->update([], ['$set' => ['foo' => 'bar']], ['w' => 2]);
     }
     }
@@ -227,7 +222,7 @@ class MongoCollectionTest extends TestCase
         $this->assertSame(1, $this->getCheckDatabase()->selectCollection('test')->count(['foo' => 'foo']));
         $this->assertSame(1, $this->getCheckDatabase()->selectCollection('test')->count(['foo' => 'foo']));
     }
     }
 
 
-    public function testUpdateFail()
+    public function testUpdateDuplicate()
     {
     {
         $collection = $this->getCollection();
         $collection = $this->getCollection();
         $collection->createIndex(['foo' => 1], ['unique' => 1]);
         $collection->createIndex(['foo' => 1], ['unique' => 1]);
@@ -237,16 +232,8 @@ class MongoCollectionTest extends TestCase
         $document = ['foo' => 'foo'];
         $document = ['foo' => 'foo'];
         $collection->insert($document);
         $collection->insert($document);
 
 
-        $this->assertArraySubset(
-            [
-                'ok' => 0.0,
-                'nModified' => 0,
-                'n' => 0,
-                'err' => 11000,
-                'updatedExisting' => true,
-            ],
-            $collection->update(['foo' => 'bar'], ['$set' => ['foo' => 'foo']])
-        );
+        $this->setExpectedException('MongoDuplicateKeyException');
+        $collection->update(['foo' => 'bar'], ['$set' => ['foo' => 'foo']]);
     }
     }
 
 
     public function testUpdateMany()
     public function testUpdateMany()
@@ -377,11 +364,11 @@ class MongoCollectionTest extends TestCase
 
 
     public function testFindOneConnectionIssue()
     public function testFindOneConnectionIssue()
     {
     {
+        $this->setExpectedException('MongoConnectionException');
+
         $client = $this->getClient([], 'mongodb://localhost:28888?connectTimeoutMS=1');
         $client = $this->getClient([], 'mongodb://localhost:28888?connectTimeoutMS=1');
         $collection = $client->selectCollection('mongo-php-adapter', 'test');
         $collection = $client->selectCollection('mongo-php-adapter', 'test');
 
 
-        $this->setExpectedException('MongoConnectionException');
-
         $collection->findOne();
         $collection->findOne();
     }
     }
 
 
@@ -407,6 +394,7 @@ class MongoCollectionTest extends TestCase
 
 
     public function testAggregate()
     public function testAggregate()
     {
     {
+        $this->skipTestUnless(extension_loaded('mongo'));
         $collection = $this->getCollection();
         $collection = $this->getCollection();
 
 
         $this->prepareData();
         $this->prepareData();
@@ -435,6 +423,7 @@ class MongoCollectionTest extends TestCase
 
 
     public function testAggregateTimeoutException()
     public function testAggregateTimeoutException()
     {
     {
+        $this->skipTestUnless(extension_loaded('mongo'));
         $collection = $this->getCollection();
         $collection = $this->getCollection();
 
 
         $this->failMaxTimeMS();
         $this->failMaxTimeMS();
@@ -489,53 +478,61 @@ class MongoCollectionTest extends TestCase
         $this->assertSame(['type' => \MongoClient::RP_PRIMARY], $collection->getReadPreference());
         $this->assertSame(['type' => \MongoClient::RP_PRIMARY], $collection->getReadPreference());
         $this->assertFalse($collection->getSlaveOkay());
         $this->assertFalse($collection->getSlaveOkay());
 
 
-        $this->assertTrue($collection->setReadPreference(\MongoClient::RP_SECONDARY, ['a' => 'b']));
-        $this->assertSame(['type' => \MongoClient::RP_SECONDARY, 'tagsets' => ['a' => 'b']], $collection->getReadPreference());
+        $this->assertTrue($collection->setReadPreference(\MongoClient::RP_SECONDARY, [['a' => 'b']]));
+        $this->assertSame(['type' => \MongoClient::RP_SECONDARY, 'tagsets' => [['a' => 'b']]], $collection->getReadPreference());
         $this->assertTrue($collection->getSlaveOkay());
         $this->assertTrue($collection->getSlaveOkay());
 
 
-        // Only way to check whether options are passed down is through debugInfo
-        $writeConcern = $collection->getCollection()->__debugInfo()['readPreference'];
-
-        $this->assertSame(ReadPreference::RP_SECONDARY, $writeConcern->getMode());
-        $this->assertSame(['a' => 'b'], $writeConcern->getTagSets());
-
         $this->assertTrue($collection->setSlaveOkay(true));
         $this->assertTrue($collection->setSlaveOkay(true));
-        $this->assertSame(['type' => \MongoClient::RP_SECONDARY_PREFERRED, 'tagsets' => ['a' => 'b']], $collection->getReadPreference());
+        $this->assertSame(['type' => \MongoClient::RP_SECONDARY_PREFERRED, 'tagsets' => [['a' => 'b']]], $collection->getReadPreference());
 
 
         $this->assertTrue($collection->setSlaveOkay(false));
         $this->assertTrue($collection->setSlaveOkay(false));
-        $this->assertSame(['type' => \MongoClient::RP_PRIMARY], $collection->getReadPreference());
+        $this->assertArraySubset(['type' => \MongoClient::RP_PRIMARY], $collection->getReadPreference());
+    }
+
+    public function testReadPreferenceIsSetInDriver()
+    {
+        $this->skipTestIf(extension_loaded('mongo'));
+
+        $collection = $this->getCollection();
+
+        $this->assertTrue($collection->setReadPreference(\MongoClient::RP_SECONDARY, [['a' => 'b']]));
+
+        // Only way to check whether options are passed down is through debugInfo
+        $readPreference = $collection->getCollection()->__debugInfo()['readPreference'];
+
+        $this->assertSame(ReadPreference::RP_SECONDARY, $readPreference->getMode());
+        $this->assertSame([['a' => 'b']], $readPreference->getTagSets());
     }
     }
 
 
     public function testReadPreferenceIsInherited()
     public function testReadPreferenceIsInherited()
     {
     {
         $database = $this->getDatabase();
         $database = $this->getDatabase();
-        $database->setReadPreference(\MongoClient::RP_SECONDARY, ['a' => 'b']);
+        $database->setReadPreference(\MongoClient::RP_SECONDARY, [['a' => 'b']]);
 
 
         $collection = $database->selectCollection('test');
         $collection = $database->selectCollection('test');
-        $this->assertSame(['type' => \MongoClient::RP_SECONDARY, 'tagsets' => ['a' => 'b']], $collection->getReadPreference());
+        $this->assertSame(['type' => \MongoClient::RP_SECONDARY, 'tagsets' => [['a' => 'b']]], $collection->getReadPreference());
     }
     }
 
 
     public function testWriteConcern()
     public function testWriteConcern()
     {
     {
         $collection = $this->getCollection();
         $collection = $this->getCollection();
-        $this->assertSame(['w' => 1, 'wtimeout' => 0], $collection->getWriteConcern());
-        $this->assertSame(1, $collection->w);
-        $this->assertSame(0, $collection->wtimeout);
 
 
         $this->assertTrue($collection->setWriteConcern('majority', 100));
         $this->assertTrue($collection->setWriteConcern('majority', 100));
         $this->assertSame(['w' => 'majority', 'wtimeout' => 100], $collection->getWriteConcern());
         $this->assertSame(['w' => 'majority', 'wtimeout' => 100], $collection->getWriteConcern());
+    }
 
 
-        $collection->w = 2;
-        $this->assertSame(['w' => 2, 'wtimeout' => 100], $collection->getWriteConcern());
+    public function testWriteConcernIsSetInDriver()
+    {
+        $this->skipTestIf(extension_loaded('mongo'));
 
 
-        $collection->wtimeout = -1;
-        $this->assertSame(['w' => 2, 'wtimeout' => 0], $collection->getWriteConcern());
+        $collection = $this->getCollection();
+        $this->assertTrue($collection->setWriteConcern(2, 100));
 
 
         // Only way to check whether options are passed down is through debugInfo
         // Only way to check whether options are passed down is through debugInfo
         $writeConcern = $collection->getCollection()->__debugInfo()['writeConcern'];
         $writeConcern = $collection->getCollection()->__debugInfo()['writeConcern'];
 
 
         $this->assertSame(2, $writeConcern->getW());
         $this->assertSame(2, $writeConcern->getW());
-        $this->assertSame(0, $writeConcern->getWtimeout());
+        $this->assertSame(100, $writeConcern->getWtimeout());
     }
     }
 
 
     public function testWriteConcernIsInherited()
     public function testWriteConcernIsInherited()
@@ -552,17 +549,19 @@ class MongoCollectionTest extends TestCase
         $id = '54203e08d51d4a1f868b456e';
         $id = '54203e08d51d4a1f868b456e';
         $collection = $this->getCollection();
         $collection = $this->getCollection();
 
 
+        $objectId = new \MongoId($id);
         $expected = [
         $expected = [
             'ok' => 1.0,
             'ok' => 1.0,
             'nModified' => 0,
             'nModified' => 0,
-            'n' => 0,
+            'n' => 1,
             'err' => null,
             'err' => null,
             'errmsg' => null,
             'errmsg' => null,
+            'upserted' => $objectId,
             'updatedExisting' => false,
             'updatedExisting' => false,
         ];
         ];
 
 
-        $document = ['_id' => new \MongoId($id), 'foo' => 'bar'];
-        $this->assertSame($expected, $collection->save($document));
+        $document = ['_id' => $objectId, 'foo' => 'bar'];
+        $this->assertEquals($expected, $collection->save($document));
 
 
         $newCollection = $this->getCheckDatabase()->selectCollection('test');
         $newCollection = $this->getCheckDatabase()->selectCollection('test');
         $this->assertSame(1, $newCollection->count());
         $this->assertSame(1, $newCollection->count());
@@ -645,43 +644,26 @@ class MongoCollectionTest extends TestCase
         $document = ['foo' => 'bar'];
         $document = ['foo' => 'bar'];
         $collection->save($document);
         $collection->save($document);
 
 
-        $this->setExpectedException('MongoCursorException');
+        $this->setExpectedException('MongoDuplicateKeyException');
 
 
         unset($document['_id']);
         unset($document['_id']);
-        $this->assertArraySubset(
-            [
-                'ok' => 0.0,
-                'nModified' => 0,
-                'n' => 0,
-                'err' => 11000,
-                'updatedExisting' => true,
-            ],
-            $collection->save($document)
-        );
+        $collection->save($document);
     }
     }
 
 
     public function testSaveEmptyKeys()
     public function testSaveEmptyKeys()
     {
     {
-        $this->setExpectedException('MongoException');
-
         $document = [];
         $document = [];
         $this->getCollection()->save($document);
         $this->getCollection()->save($document);
+
+        $this->assertSame(1, $this->getCollection()->count());
     }
     }
 
 
     public function testSaveEmptyObject()
     public function testSaveEmptyObject()
     {
     {
-        $this->setExpectedException('MongoException');
-
         $document = (object) [];
         $document = (object) [];
         $this->getCollection()->save($document);
         $this->getCollection()->save($document);
-    }
 
 
-    public function testSaveWrite()
-    {
-        $this->setExpectedException('MongoConnectionException'); // should be MongoCursorException
-
-        $document = ['foo' => 'bar'];
-        $this->getCollection()->save($document, ['w' => 2, 'wtimeout' => 1000]);
+        $this->assertSame(1, $this->getCollection()->count());
     }
     }
 
 
     public function testGetDBRef()
     public function testGetDBRef()
@@ -734,7 +716,7 @@ class MongoCollectionTest extends TestCase
 
 
     public function testCreateIndexInvalid()
     public function testCreateIndexInvalid()
     {
     {
-        $this->setExpectedException('MongoException', 'keys cannot be empty');
+        $this->setExpectedException('MongoException', 'index specification has no elements');
 
 
         $this->getCollection()->createIndex([]);
         $this->getCollection()->createIndex([]);
     }
     }
@@ -745,8 +727,8 @@ class MongoCollectionTest extends TestCase
 
 
         $expected = [
         $expected = [
             'createdCollectionAutomatically' => false,
             'createdCollectionAutomatically' => false,
-            'numIndexesBefore' => 1,
-            'numIndexesAfter' => 1,
+            'numIndexesBefore' => 2,
+            'numIndexesAfter' => 2,
             'note' => 'all indexes already exist',
             'note' => 'all indexes already exist',
             'ok' => 1.0
             'ok' => 1.0
         ];
         ];
@@ -773,8 +755,15 @@ class MongoCollectionTest extends TestCase
 
 
     public function testEnsureIndex()
     public function testEnsureIndex()
     {
     {
+        $expected = [
+            'createdCollectionAutomatically' => true,
+            'numIndexesBefore' => 1,
+            'numIndexesAfter' => 2,
+            'ok' => 1.0
+        ];
+
         $collection = $this->getCollection();
         $collection = $this->getCollection();
-        $this->assertTrue($collection->ensureIndex(['bar' => 1], ['unique' => true]));
+        $this->assertEquals($expected, $collection->ensureIndex(['bar' => 1], ['unique' => true]));
 
 
         $newCollection = $this->getCheckDatabase()->selectCollection('test');
         $newCollection = $this->getCheckDatabase()->selectCollection('test');
         $indexes = iterator_to_array($newCollection->listIndexes());
         $indexes = iterator_to_array($newCollection->listIndexes());
@@ -792,6 +781,22 @@ class MongoCollectionTest extends TestCase
 
 
         $expected = [
         $expected = [
             'nIndexesWas' => 2,
             'nIndexesWas' => 2,
+            'errmsg' => 'index not found with name [bar_1]',
+            'ok' => 0.0,
+            'code' => 27,
+        ];
+        $this->assertEquals($expected, $this->getCollection()->deleteIndex('bar'));
+
+        $this->assertCount(2, iterator_to_array($newCollection->listIndexes()));
+    }
+
+    public function testDeleteIndexUsingField()
+    {
+        $newCollection = $this->getCheckDatabase()->selectCollection('test');
+        $newCollection->createIndex(['bar' => 1]);
+
+        $expected = [
+            'nIndexesWas' => 2,
             'ok' => 1.0,
             'ok' => 1.0,
         ];
         ];
         $this->assertSame($expected, $this->getCollection()->deleteIndex('bar'));
         $this->assertSame($expected, $this->getCollection()->deleteIndex('bar'));
@@ -875,6 +880,28 @@ class MongoCollectionTest extends TestCase
         $this->assertAttributeSame('foo', 'foo', $object);
         $this->assertAttributeSame('foo', 'foo', $object);
     }
     }
 
 
+    public function testFindAndModifyUpdateReplace()
+    {
+        $id = '54203e08d51d4a1f868b456e';
+        $collection = $this->getCollection();
+
+        $document = ['_id' => new \MongoId($id), 'foo' => 'bar'];
+        $collection->insert($document);
+        $document = $collection->findAndModify(
+            ['_id' => new \MongoId($id)],
+            ['_id' => new \MongoId($id), 'foo' => 'boo']
+        );
+        $this->assertSame('bar', $document['foo']);
+
+        $newCollection = $this->getCheckDatabase()->selectCollection('test');
+        $this->assertSame(1, $newCollection->count());
+        $object = $newCollection->findOne();
+
+        $this->assertNotNull($object);
+        $this->assertAttributeSame('boo', 'foo', $object);
+        $this->assertObjectNotHasAttribute('bar', $object);
+    }
+
     public function testFindAndModifyUpdateReturnNew()
     public function testFindAndModifyUpdateReturnNew()
     {
     {
         $id = '54203e08d51d4a1f868b456e';
         $id = '54203e08d51d4a1f868b456e';
@@ -1043,8 +1070,12 @@ class MongoCollectionTest extends TestCase
     {
     {
         $collection = $this->getCollection();
         $collection = $this->getCollection();
 
 
-        $this->setExpectedException('Exception', 'Collection name cannot contain null bytes');
-
-        $collection->{'foo' . chr(0)};
+        $this->assertInstanceOf('MongoCollection', $collection->{'foo' . chr(0)});
+        $this->assertSame('test', $collection->getName());
     }
     }
 }
 }
+
+class PrivatePropertiesStub
+{
+    private $foo = 'bar';
+}

+ 10 - 9
tests/Alcaeus/MongoDbAdapter/MongoCommandCursorTest.php

@@ -17,7 +17,7 @@ class MongoCommandCursorTest extends TestCase
         $expected = [
         $expected = [
             'ns' => 'mongo-php-adapter.test',
             'ns' => 'mongo-php-adapter.test',
             'limit' => 0,
             'limit' => 0,
-            'batchSize' => null,
+            'batchSize' => 0,
             'skip' => 0,
             'skip' => 0,
             'flags' => 0,
             'flags' => 0,
             'query' => [
             'query' => [
@@ -27,28 +27,29 @@ class MongoCommandCursorTest extends TestCase
                         '$match' => ['foo' => 'bar']
                         '$match' => ['foo' => 'bar']
                     ]
                     ]
                 ],
                 ],
-                'cursor' => true,
+                'cursor' => new \stdClass(),
             ],
             ],
             'fields' => null,
             'fields' => null,
             'started_iterating' => false,
             'started_iterating' => false,
         ];
         ];
-        $this->assertEquals($expected, $cursor->info());
+        $info = $cursor->info();
+        $this->assertEquals($expected, $info);
 
 
         // Ensure cursor started iterating
         // Ensure cursor started iterating
         $array = iterator_to_array($cursor);
         $array = iterator_to_array($cursor);
 
 
         $expected['started_iterating'] = true;
         $expected['started_iterating'] = true;
         $expected += [
         $expected += [
-            'id' => '0',
-            'at' => null,
-            'numReturned' => null,
-            'server' => null,
+            'id' => 0,
+            'at' => 0,
+            'numReturned' => 0,
+            'server' => 'localhost:27017;-;.;' . getmypid(),
             'host' => 'localhost',
             'host' => 'localhost',
             'port' => 27017,
             'port' => 27017,
-            'connection_type_desc' => 'STANDALONE'
+            'connection_type_desc' => 'STANDALONE',
         ];
         ];
 
 
-        $this->assertEquals($expected, $cursor->info());
+        $this->assertArraySubset($expected, $cursor->info());
 
 
         $i = 0;
         $i = 0;
         foreach ($array as $key => $value) {
         foreach ($array as $key => $value) {

+ 19 - 11
tests/Alcaeus/MongoDbAdapter/MongoCursorTest.php

@@ -20,10 +20,11 @@ class MongoCursorTest extends TestCase
 
 
         $iterated = 0;
         $iterated = 0;
         foreach ($cursor as $key => $item) {
         foreach ($cursor as $key => $item) {
-            $iterated++;
+            $this->assertSame($iterated, $cursor->info()['at']);
             $this->assertInstanceOf('MongoId', $item['_id']);
             $this->assertInstanceOf('MongoId', $item['_id']);
             $this->assertEquals($key, (string) $item['_id']);
             $this->assertEquals($key, (string) $item['_id']);
             $this->assertSame('bar', $item['foo']);
             $this->assertSame('bar', $item['foo']);
+            $iterated++;
         }
         }
 
 
         $this->assertSame(2, $iterated);
         $this->assertSame(2, $iterated);
@@ -42,7 +43,7 @@ class MongoCursorTest extends TestCase
 
 
     public function testCountCannotConnect()
     public function testCountCannotConnect()
     {
     {
-        $client = $this->getClient([], 'mongodb://localhost:28888');
+        $client = $this->getClient(['connect' => false], 'mongodb://localhost:28888');
         $cursor = $client->selectCollection('mongo-php-adapter', 'test')->find();
         $cursor = $client->selectCollection('mongo-php-adapter', 'test')->find();
 
 
         $this->setExpectedException('MongoConnectionException');
         $this->setExpectedException('MongoConnectionException');
@@ -90,6 +91,11 @@ class MongoCursorTest extends TestCase
         $collection = $this->getCollection();
         $collection = $this->getCollection();
         $cursor = $collection->find(['foo' => 'bar']);
         $cursor = $collection->find(['foo' => 'bar']);
 
 
+        $this->assertFalse($cursor->valid(), 'Cursor should be invalid to start with');
+        $this->assertNull($cursor->current(), 'Cursor should be invalid to start with');
+        $this->assertNull($cursor->key(), 'Cursor should be invalid to start with');
+
+        $cursor->next();
         $this->assertTrue($cursor->valid(), 'Cursor should be valid');
         $this->assertTrue($cursor->valid(), 'Cursor should be valid');
 
 
         $item = $cursor->current();
         $item = $cursor->current();
@@ -122,6 +128,8 @@ class MongoCursorTest extends TestCase
      */
      */
     public function testCursorAppliesOptions($checkOptionCallback, \Closure $applyOptionCallback = null)
     public function testCursorAppliesOptions($checkOptionCallback, \Closure $applyOptionCallback = null)
     {
     {
+        $this->skipTestIf(extension_loaded('mongo'));
+
         $query = ['foo' => 'bar'];
         $query = ['foo' => 'bar'];
         $projection = ['_id' => false, 'foo' => true];
         $projection = ['_id' => false, 'foo' => true];
 
 
@@ -290,7 +298,7 @@ class MongoCursorTest extends TestCase
         $expected = [
         $expected = [
             'ns' => 'mongo-php-adapter.test',
             'ns' => 'mongo-php-adapter.test',
             'limit' => 3,
             'limit' => 3,
-            'batchSize' => null,
+            'batchSize' => 0,
             'skip' => 1,
             'skip' => 1,
             'flags' => 0,
             'flags' => 0,
             'query' => ['foo' => 'bar'],
             'query' => ['foo' => 'bar'],
@@ -298,32 +306,32 @@ class MongoCursorTest extends TestCase
             'started_iterating' => false,
             'started_iterating' => false,
         ];
         ];
 
 
-        $this->assertSame($expected, $cursor->info());
+        $this->assertEquals($expected, $cursor->info());
 
 
         // Ensure cursor started iterating
         // Ensure cursor started iterating
         iterator_to_array($cursor);
         iterator_to_array($cursor);
 
 
         $expected['started_iterating'] = true;
         $expected['started_iterating'] = true;
         $expected += [
         $expected += [
-            'id' => '0',
-            'at' => null,
-            'numReturned' => null,
-            'server' => null,
+            'id' => 0,
+            'at' => 1,
+            'numReturned' => 1,
+            'server' => 'localhost:27017;-;.;' . getmypid(),
             'host' => 'localhost',
             'host' => 'localhost',
             'port' => 27017,
             'port' => 27017,
             'connection_type_desc' => 'STANDALONE'
             'connection_type_desc' => 'STANDALONE'
         ];
         ];
 
 
-        $this->assertSame($expected, $cursor->info());
+        $this->assertEquals($expected, $cursor->info());
     }
     }
 
 
     public function testReadPreferenceIsInherited()
     public function testReadPreferenceIsInherited()
     {
     {
         $collection = $this->getCollection();
         $collection = $this->getCollection();
-        $collection->setReadPreference(\MongoClient::RP_SECONDARY, ['a' => 'b']);
+        $collection->setReadPreference(\MongoClient::RP_SECONDARY, [['a' => 'b']]);
 
 
         $cursor = $collection->find(['foo' => 'bar']);
         $cursor = $collection->find(['foo' => 'bar']);
-        $this->assertSame(['type' => \MongoClient::RP_SECONDARY, 'tagsets' => ['a' => 'b']], $cursor->getReadPreference());
+        $this->assertSame(['type' => \MongoClient::RP_SECONDARY, 'tagsets' => [['a' => 'b']]], $cursor->getReadPreference());
     }
     }
 
 
     /**
     /**

+ 3 - 2
tests/Alcaeus/MongoDbAdapter/MongoDBRefTest.php

@@ -34,17 +34,18 @@ class MongoDBRefTest extends TestCase
     public static function dataCreateThroughMongoDB()
     public static function dataCreateThroughMongoDB()
     {
     {
         $id = new \MongoId();
         $id = new \MongoId();
-        $validRef = ['$ref' => 'test', '$id' => $id, '$db' => 'mongo-php-adapter'];
+        $validRef = ['$ref' => 'test', '$id' => $id];
 
 
         $object = new \stdClass();
         $object = new \stdClass();
         $object->_id = $id;
         $object->_id = $id;
 
 
+        $objectWithoutId = new \stdClass();
         return [
         return [
             'simpleId' => [$validRef, $id],
             'simpleId' => [$validRef, $id],
             'arrayWithIdProperty' => [$validRef, ['_id' => $id]],
             'arrayWithIdProperty' => [$validRef, ['_id' => $id]],
             'objectWithIdProperty' => [$validRef, $object],
             'objectWithIdProperty' => [$validRef, $object],
             'arrayWithoutId' => [null, []],
             'arrayWithoutId' => [null, []],
-            'objectWithoutId' => [null, new \stdClass()],
+            'objectWithoutId' => [['$ref' => 'test', '$id' => $objectWithoutId], $objectWithoutId],
         ];
         ];
     }
     }
 
 

+ 41 - 27
tests/Alcaeus/MongoDbAdapter/MongoDBTest.php

@@ -71,7 +71,7 @@ class MongoDBTest extends TestCase
     {
     {
         $database = $this->getDatabase();
         $database = $this->getDatabase();
 
 
-        $this->assertFalse($database->createCollection('test', ['capped' => 2, 'size' => 100]));
+        $this->assertInstanceOf('MongoCollection', $database->createCollection('test', ['capped' => 2, 'size' => 100]));
     }
     }
 
 
     public function testGetCollectionProperty()
     public function testGetCollectionProperty()
@@ -106,13 +106,17 @@ class MongoDBTest extends TestCase
 
 
         $this->failMaxTimeMS();
         $this->failMaxTimeMS();
 
 
-        $this->setExpectedException('MongoCursorTimeoutException');
-
-        $database->command([
+        $result = $database->command([
             "count" => "test",
             "count" => "test",
             "query" => array("a" => 1),
             "query" => array("a" => 1),
             "maxTimeMS" => 100,
             "maxTimeMS" => 100,
         ]);
         ]);
+
+        $this->assertSame([
+            'ok' => 0.0,
+            'errmsg' => 'operation exceeded time limit',
+            'code' => 50,
+        ], $result);
     }
     }
 
 
     public function testReadPreference()
     public function testReadPreference()
@@ -121,62 +125,72 @@ class MongoDBTest extends TestCase
         $this->assertSame(['type' => \MongoClient::RP_PRIMARY], $database->getReadPreference());
         $this->assertSame(['type' => \MongoClient::RP_PRIMARY], $database->getReadPreference());
         $this->assertFalse($database->getSlaveOkay());
         $this->assertFalse($database->getSlaveOkay());
 
 
-        $this->assertTrue($database->setReadPreference(\MongoClient::RP_SECONDARY, ['a' => 'b']));
-        $this->assertSame(['type' => \MongoClient::RP_SECONDARY, 'tagsets' => ['a' => 'b']], $database->getReadPreference());
+        $this->assertTrue($database->setReadPreference(\MongoClient::RP_SECONDARY, [['a' => 'b']]));
+        $this->assertSame(['type' => \MongoClient::RP_SECONDARY, 'tagsets' => [['a' => 'b']]], $database->getReadPreference());
         $this->assertTrue($database->getSlaveOkay());
         $this->assertTrue($database->getSlaveOkay());
 
 
-        // Only way to check whether options are passed down is through debugInfo
-        $writeConcern = $database->getDb()->__debugInfo()['readPreference'];
-
-        $this->assertSame(ReadPreference::RP_SECONDARY, $writeConcern->getMode());
-        $this->assertSame(['a' => 'b'], $writeConcern->getTagSets());
-
         $this->assertTrue($database->setSlaveOkay(true));
         $this->assertTrue($database->setSlaveOkay(true));
-        $this->assertSame(['type' => \MongoClient::RP_SECONDARY_PREFERRED, 'tagsets' => ['a' => 'b']], $database->getReadPreference());
+        $this->assertSame(['type' => \MongoClient::RP_SECONDARY_PREFERRED, 'tagsets' => [['a' => 'b']]], $database->getReadPreference());
 
 
         $this->assertTrue($database->setSlaveOkay(false));
         $this->assertTrue($database->setSlaveOkay(false));
-        $this->assertSame(['type' => \MongoClient::RP_PRIMARY], $database->getReadPreference());
+        // Only test a subset since we don't keep tagsets around for RP_PRIMARY
+        $this->assertArraySubset(['type' => \MongoClient::RP_PRIMARY], $database->getReadPreference());
+    }
+
+    public function testReadPreferenceIsSetInDriver()
+    {
+        $this->skipTestIf(extension_loaded('mongo'));
+
+        $database = $this->getDatabase();
+
+        $this->assertTrue($database->setReadPreference(\MongoClient::RP_SECONDARY, [['a' => 'b']]));
+
+        // Only way to check whether options are passed down is through debugInfo
+        $readPreference = $database->getDb()->__debugInfo()['readPreference'];
+
+        $this->assertSame(ReadPreference::RP_SECONDARY, $readPreference->getMode());
+        $this->assertSame([['a' => 'b']], $readPreference->getTagSets());
+
     }
     }
 
 
     public function testReadPreferenceIsInherited()
     public function testReadPreferenceIsInherited()
     {
     {
         $client = $this->getClient();
         $client = $this->getClient();
-        $client->setReadPreference(\MongoClient::RP_SECONDARY, ['a' => 'b']);
+        $client->setReadPreference(\MongoClient::RP_SECONDARY, [['a' => 'b']]);
 
 
         $database = $client->selectDB('test');
         $database = $client->selectDB('test');
-        $this->assertSame(['type' => \MongoClient::RP_SECONDARY, 'tagsets' => ['a' => 'b']], $database->getReadPreference());
+        $this->assertSame(['type' => \MongoClient::RP_SECONDARY, 'tagsets' => [['a' => 'b']]], $database->getReadPreference());
     }
     }
 
 
     public function testWriteConcern()
     public function testWriteConcern()
     {
     {
         $database = $this->getDatabase();
         $database = $this->getDatabase();
-        $this->assertSame(['w' => 1, 'wtimeout' => 0], $database->getWriteConcern());
-        $this->assertSame(1, $database->w);
-        $this->assertSame(0, $database->wtimeout);
 
 
         $this->assertTrue($database->setWriteConcern('majority', 100));
         $this->assertTrue($database->setWriteConcern('majority', 100));
         $this->assertSame(['w' => 'majority', 'wtimeout' => 100], $database->getWriteConcern());
         $this->assertSame(['w' => 'majority', 'wtimeout' => 100], $database->getWriteConcern());
+    }
 
 
-        $database->w = 2;
-        $this->assertSame(['w' => 2, 'wtimeout' => 100], $database->getWriteConcern());
+    public function testWriteConcernIsSetInDriver()
+    {
+        $this->skipTestIf(extension_loaded('mongo'));
 
 
-        $database->wtimeout = -1;
-        $this->assertSame(['w' => 2, 'wtimeout' => 0], $database->getWriteConcern());
+        $database = $this->getDatabase();
+        $this->assertTrue($database->setWriteConcern(2, 100));
 
 
         // Only way to check whether options are passed down is through debugInfo
         // Only way to check whether options are passed down is through debugInfo
         $writeConcern = $database->getDb()->__debugInfo()['writeConcern'];
         $writeConcern = $database->getDb()->__debugInfo()['writeConcern'];
 
 
         $this->assertSame(2, $writeConcern->getW());
         $this->assertSame(2, $writeConcern->getW());
-        $this->assertSame(0, $writeConcern->getWtimeout());
+        $this->assertSame(100, $writeConcern->getWtimeout());
     }
     }
 
 
     public function testWriteConcernIsInherited()
     public function testWriteConcernIsInherited()
     {
     {
         $client = $this->getClient();
         $client = $this->getClient();
-        $client->setWriteConcern('majority', 100);
+        $client->setWriteConcern(2, 100);
 
 
         $database = $client->selectDB('test');
         $database = $client->selectDB('test');
-        $this->assertSame(['w' => 'majority', 'wtimeout' => 100], $database->getWriteConcern());
+        $this->assertSame(['w' => 2, 'wtimeout' => 100], $database->getWriteConcern());
     }
     }
 
 
     public function testProfilingLevel()
     public function testProfilingLevel()
@@ -192,7 +206,7 @@ class MongoDBTest extends TestCase
     public function testForceError()
     public function testForceError()
     {
     {
         $result = $this->getDatabase()->forceError();
         $result = $this->getDatabase()->forceError();
-        $this->assertSame(0, $result['ok']);
+        $this->assertSame(0.0, $result['ok']);
     }
     }
 
 
     public function testExecute()
     public function testExecute()

+ 15 - 0
tests/Alcaeus/MongoDbAdapter/MongoDateTest.php

@@ -1,6 +1,7 @@
 <?php
 <?php
 
 
 namespace Alcaeus\MongoDbAdapter\Tests;
 namespace Alcaeus\MongoDbAdapter\Tests;
+use Alcaeus\MongoDbAdapter\TypeInterface;
 
 
 /**
 /**
  * @author alcaeus <alcaeus@alcaeus.org>
  * @author alcaeus <alcaeus@alcaeus.org>
@@ -19,6 +20,18 @@ class MongoDateTest extends TestCase
         $this->assertSame(1234567890, $dateTime->getTimestamp());
         $this->assertSame(1234567890, $dateTime->getTimestamp());
         $this->assertSame('123000', $dateTime->format('u'));
         $this->assertSame('123000', $dateTime->format('u'));
 
 
+        return $date;
+    }
+
+    /**
+     * @depends testCreate
+     */
+    public function testConvertToBson(\MongoDate $date)
+    {
+        $this->skipTestUnless($date instanceof TypeInterface);
+
+        $dateTime = $date->toDateTime();
+
         $bsonDate = $date->toBSONType();
         $bsonDate = $date->toBSONType();
         $this->assertInstanceOf('MongoDB\BSON\UTCDateTime', $bsonDate);
         $this->assertInstanceOf('MongoDB\BSON\UTCDateTime', $bsonDate);
         $this->assertSame('1234567890123', (string) $bsonDate);
         $this->assertSame('1234567890123', (string) $bsonDate);
@@ -28,6 +41,8 @@ class MongoDateTest extends TestCase
 
 
     public function testCreateWithBsonDate()
     public function testCreateWithBsonDate()
     {
     {
+        $this->skipTestUnless(in_array(TypeInterface::class, class_implements('MongoDate')));
+
         $bsonDate = new \MongoDB\BSON\UTCDateTime(1234567890123);
         $bsonDate = new \MongoDB\BSON\UTCDateTime(1234567890123);
         $date = new \MongoDate($bsonDate);
         $date = new \MongoDate($bsonDate);
 
 

+ 3 - 11
tests/Alcaeus/MongoDbAdapter/MongoDeleteBatchTest.php

@@ -17,12 +17,8 @@ class MongoDeleteBatchTest extends TestCase
         $this->assertTrue($batch->add(['q' => ['foo' => 'bar'], 'limit' => 1]));
         $this->assertTrue($batch->add(['q' => ['foo' => 'bar'], 'limit' => 1]));
 
 
         $expected = [
         $expected = [
-            'ok' => 1.0,
-            'nInserted' => 0,
-            'nMatched' => 0,
-            'nModified' => 0,
-            'nUpserted' => 0,
             'nRemoved' => 1,
             'nRemoved' => 1,
+            'ok' => true,
         ];
         ];
 
 
         $this->assertSame($expected, $batch->execute());
         $this->assertSame($expected, $batch->execute());
@@ -44,12 +40,8 @@ class MongoDeleteBatchTest extends TestCase
         $this->assertTrue($batch->add(['q' => ['foo' => 'bar'], 'limit' => 0]));
         $this->assertTrue($batch->add(['q' => ['foo' => 'bar'], 'limit' => 0]));
 
 
         $expected = [
         $expected = [
-            'ok' => 1.0,
-            'nInserted' => 0,
-            'nMatched' => 0,
-            'nModified' => 0,
-            'nUpserted' => 0,
             'nRemoved' => 2,
             'nRemoved' => 2,
+            'ok' => true,
         ];
         ];
 
 
         $this->assertSame($expected, $batch->execute());
         $this->assertSame($expected, $batch->execute());
@@ -64,7 +56,7 @@ class MongoDeleteBatchTest extends TestCase
         $collection = $this->getCollection();
         $collection = $this->getCollection();
         $batch = new \MongoDeleteBatch($collection);
         $batch = new \MongoDeleteBatch($collection);
 
 
-        $this->setExpectedException('Exception', 'invalid item');
+        $this->setExpectedException('Exception', "Expected \$item to contain 'q' key");
 
 
         $batch->add([]);
         $batch->add([]);
     }
     }

+ 1 - 1
tests/Alcaeus/MongoDbAdapter/MongoGridFSCursorTest.php

@@ -19,7 +19,7 @@ class MongoGridFSCursorTest extends TestCase
 
 
             $this->assertArraySubset([
             $this->assertArraySubset([
                 'filename' => 'foo.txt',
                 'filename' => 'foo.txt',
-                'chunkSize' => \MongoGridFS::DEFAULT_CHUNK_SIZE,
+                'chunkSize' => 261120,
                 'length' => 3,
                 'length' => 3,
                 'md5' => 'acbd18db4cc2f85cedef654fccc4a4d8'
                 'md5' => 'acbd18db4cc2f85cedef654fccc4a4d8'
             ], $value->file);
             ], $value->file);

+ 10 - 9
tests/Alcaeus/MongoDbAdapter/MongoGridFSFileTest.php

@@ -32,10 +32,11 @@ class MongoGridFSFileTest extends TestCase
 
 
     public function testWrite()
     public function testWrite()
     {
     {
-        $id = $this->prepareFile();
         $filename = '/tmp/test-mongo-grid-fs-file';
         $filename = '/tmp/test-mongo-grid-fs-file';
+        $id = $this->prepareFile('abcd', ['filename' => $filename]);
         @unlink($filename);
         @unlink($filename);
-        $file = $this->getFile(['_id' => $id, 'length' => 4, 'filename' => $filename]);
+        $file = $this->getGridFS()->findOne(['_id' => $id]);
+        $this->assertInstanceOf(\MongoGridFSFile::class, $file);
 
 
         $file->write();
         $file->write();
 
 
@@ -49,7 +50,8 @@ class MongoGridFSFileTest extends TestCase
         $id = $this->prepareFile();
         $id = $this->prepareFile();
         $filename = '/tmp/test-mongo-grid-fs-file';
         $filename = '/tmp/test-mongo-grid-fs-file';
         @unlink($filename);
         @unlink($filename);
-        $file = $this->getFile(['_id' => $id, 'length' => 4]);
+        $file = $this->getGridFS()->findOne(['_id' => $id]);
+        $this->assertInstanceOf(\MongoGridFSFile::class, $file);
 
 
         $file->write($filename);
         $file->write($filename);
 
 
@@ -70,13 +72,15 @@ class MongoGridFSFileTest extends TestCase
 
 
     public function testGetResource()
     public function testGetResource()
     {
     {
-        $id = $this->prepareFile();
-        $file = $this->getFile(['_id' => $id, 'length' => 4]);
+        $data = str_repeat('a', 500 * 1024);
+        $id = $this->prepareFile($data);
+        $file = $this->getGridFS()->findOne(['_id' => $id]);
+        $this->assertInstanceOf(\MongoGridFSFile::class, $file);
 
 
         $result = $file->getResource();
         $result = $file->getResource();
 
 
         $this->assertTrue(is_resource($result));
         $this->assertTrue(is_resource($result));
-        $this->assertSame('abcd', stream_get_contents($result));
+        $this->assertSame($data, stream_get_contents($result));
     }
     }
 
 
     /**
     /**
@@ -101,9 +105,6 @@ class MongoGridFSFileTest extends TestCase
     {
     {
         $collection = $this->getGridFS();
         $collection = $this->getGridFS();
 
 
-        // to make sure we have multiple chunks
-        $extra += ['chunkSize' => 2];
-
         return $collection->storeBytes($data, $extra);
         return $collection->storeBytes($data, $extra);
     }
     }
 
 

+ 6 - 5
tests/Alcaeus/MongoDbAdapter/MongoGridFSTest.php

@@ -230,6 +230,7 @@ class MongoGridFSTest extends TestCase
 
 
     public function testStoreUpload()
     public function testStoreUpload()
     {
     {
+        $this->skipTestIf(extension_loaded('mongo'));
         $collection = $this->getGridFS();
         $collection = $this->getGridFS();
 
 
         $_FILES['foo'] = [
         $_FILES['foo'] = [
@@ -303,7 +304,7 @@ class MongoGridFSTest extends TestCase
         $document = ['_id' => $id];
         $document = ['_id' => $id];
         $collection->insert($document);
         $collection->insert($document);
 
 
-        $this->setExpectedException('MongoGridFSException', 'Cannot insert file record');
+        $this->setExpectedExceptionRegExp('MongoGridFSException', '/Could not store file:.* E11000 duplicate key error .* mongo-php-adapter\.fs\.files/');
 
 
         $collection->storeBytes('foo', ['_id' => $id]);
         $collection->storeBytes('foo', ['_id' => $id]);
     }
     }
@@ -316,7 +317,7 @@ class MongoGridFSTest extends TestCase
         $document = ['n' => 0];
         $document = ['n' => 0];
         $collection->chunks->insert($document);
         $collection->chunks->insert($document);
 
 
-        $this->setExpectedException('MongoGridFSException', 'Error while inserting chunks');
+        $this->setExpectedExceptionRegExp('MongoGridFSException', '/Could not store file:.* E11000 duplicate key error .* mongo-php-adapter\.fs\.chunks/');
 
 
         $collection->storeBytes('foo');
         $collection->storeBytes('foo');
     }
     }
@@ -329,7 +330,7 @@ class MongoGridFSTest extends TestCase
         $document = ['_id' => $id];
         $document = ['_id' => $id];
         $collection->insert($document);
         $collection->insert($document);
 
 
-        $this->setExpectedException('MongoGridFSException', 'Cannot insert file record');
+        $this->setExpectedExceptionRegExp('MongoGridFSException', '/Could not store file:.* E11000 duplicate key error .* mongo-php-adapter\.fs\.files/');
 
 
         $collection->storeFile(__FILE__, ['_id' => $id]);
         $collection->storeFile(__FILE__, ['_id' => $id]);
     }
     }
@@ -342,7 +343,7 @@ class MongoGridFSTest extends TestCase
         $document = ['n' => 0];
         $document = ['n' => 0];
         $collection->chunks->insert($document);
         $collection->chunks->insert($document);
 
 
-        $this->setExpectedException('MongoGridFSException', 'Error while inserting chunks');
+        $this->setExpectedExceptionRegExp('MongoGridFSException', '/Could not store file:.* E11000 duplicate key error .* mongo-php-adapter\.fs\.chunks/');
 
 
         $collection->storeFile(__FILE__);
         $collection->storeFile(__FILE__);
     }
     }
@@ -355,7 +356,7 @@ class MongoGridFSTest extends TestCase
         $document = ['length' => filesize(__FILE__)];
         $document = ['length' => filesize(__FILE__)];
         $collection->insert($document);
         $collection->insert($document);
 
 
-        $this->setExpectedException('MongoGridFSException', 'Error updating file record');
+        $this->setExpectedExceptionRegExp('MongoGridFSException', '/Could not store file:.* E11000 duplicate key error .* mongo-php-adapter\.fs\.files/');
 
 
         $collection->storeFile(fopen(__FILE__, 'r'));
         $collection->storeFile(fopen(__FILE__, 'r'));
     }
     }

+ 7 - 3
tests/Alcaeus/MongoDbAdapter/MongoIdTest.php

@@ -1,6 +1,7 @@
 <?php
 <?php
 
 
 namespace Alcaeus\MongoDbAdapter\Tests;
 namespace Alcaeus\MongoDbAdapter\Tests;
+use MongoDB\BSON\ObjectID;
 
 
 /**
 /**
  * @author alcaeus <alcaeus@alcaeus.org>
  * @author alcaeus <alcaeus@alcaeus.org>
@@ -43,10 +44,12 @@ class MongoIdTest extends TestCase
         new \MongoId('invalid');
         new \MongoId('invalid');
     }
     }
 
 
-    public function testCreateWithObjetId()
+    public function testCreateWithObjectId()
     {
     {
+        $this->skipTestIf(extension_loaded('mongo'));
+
         $original = '54203e08d51d4a1f868b456e';
         $original = '54203e08d51d4a1f868b456e';
-        $objectId = new \MongoDB\BSON\ObjectID($original);
+        $objectId = new ObjectID($original);
 
 
         $id = new \MongoId($objectId);
         $id = new \MongoId($objectId);
         $this->assertSame($original, (string) $id);
         $this->assertSame($original, (string) $id);
@@ -59,6 +62,7 @@ class MongoIdTest extends TestCase
      */
      */
     public function testIsValid($expected, $value)
     public function testIsValid($expected, $value)
     {
     {
+        $this->skipTestIf($value instanceof ObjectID && extension_loaded('mongo'));
         $this->assertSame($expected, \MongoId::isValid($value));
         $this->assertSame($expected, \MongoId::isValid($value));
     }
     }
 
 
@@ -69,7 +73,7 @@ class MongoIdTest extends TestCase
         return [
         return [
             'validId' => [true, '' . $original . ''],
             'validId' => [true, '' . $original . ''],
             'MongoId' => [true, new \MongoId($original)],
             'MongoId' => [true, new \MongoId($original)],
-            'ObjectID' => [true, new \MongoDB\BSON\ObjectID($original)],
+            'ObjectID' => [true, new ObjectID($original)],
             'invalidString' => [false, 'abc'],
             'invalidString' => [false, 'abc'],
         ];
         ];
     }
     }

+ 14 - 11
tests/Alcaeus/MongoDbAdapter/MongoInsertBatchTest.php

@@ -12,12 +12,8 @@ class MongoInsertBatchTest extends TestCase
         $this->assertTrue($batch->add(['bar' => 'foo']));
         $this->assertTrue($batch->add(['bar' => 'foo']));
 
 
         $expected = [
         $expected = [
-            'ok' => 1.0,
             'nInserted' => 2,
             'nInserted' => 2,
-            'nMatched' => 0,
-            'nModified' => 0,
-            'nUpserted' => 0,
-            'nRemoved' => 0,
+            'ok' => true,
         ];
         ];
 
 
         $this->assertSame($expected, $batch->execute());
         $this->assertSame($expected, $batch->execute());
@@ -40,14 +36,21 @@ class MongoInsertBatchTest extends TestCase
         $this->assertTrue($batch->add(['foo' => 'bar']));
         $this->assertTrue($batch->add(['foo' => 'bar']));
 
 
         $expected = [
         $expected = [
-            'ok' => 0.0,
+            'writeErrors' => [
+                [
+                    'index' => 1,
+                    'code' => 11000,
+                ]
+            ],
             'nInserted' => 1,
             'nInserted' => 1,
-            'nMatched' => 0,
-            'nModified' => 0,
-            'nUpserted' => 0,
-            'nRemoved' => 0,
+            'ok' => true,
         ];
         ];
 
 
-        $this->assertSame($expected, $batch->execute());
+        try {
+            $batch->execute();
+        } catch (\MongoWriteConcernException $e) {
+            $this->assertSame('Failed write', $e->getMessage());
+            $this->assertArraySubset($expected, $e->getDocument());
+        }
     }
     }
 }
 }

+ 4 - 2
tests/Alcaeus/MongoDbAdapter/MongoMaxKeyTest.php

@@ -1,6 +1,7 @@
 <?php
 <?php
 
 
 namespace Alcaeus\MongoDbAdapter\Tests;
 namespace Alcaeus\MongoDbAdapter\Tests;
+use Alcaeus\MongoDbAdapter\TypeInterface;
 
 
 /**
 /**
  * @author alcaeus <alcaeus@alcaeus.org>
  * @author alcaeus <alcaeus@alcaeus.org>
@@ -9,7 +10,8 @@ class MongoMaxKeyTest extends TestCase
 {
 {
     public function testConvert()
     public function testConvert()
     {
     {
-        $MaxKey = new \MongoMaxKey();
-        $this->assertInstanceOf('MongoDB\BSON\MaxKey', $MaxKey->toBSONType());
+        $maxKey = new \MongoMaxKey();
+        $this->skipTestUnless($maxKey instanceof TypeInterface);
+        $this->assertInstanceOf('MongoDB\BSON\MaxKey', $maxKey->toBSONType());
     }
     }
 }
 }

+ 2 - 0
tests/Alcaeus/MongoDbAdapter/MongoMinKeyTest.php

@@ -1,6 +1,7 @@
 <?php
 <?php
 
 
 namespace Alcaeus\MongoDbAdapter\Tests;
 namespace Alcaeus\MongoDbAdapter\Tests;
+use Alcaeus\MongoDbAdapter\TypeInterface;
 
 
 /**
 /**
  * @author alcaeus <alcaeus@alcaeus.org>
  * @author alcaeus <alcaeus@alcaeus.org>
@@ -10,6 +11,7 @@ class MongoMinKeyTest extends TestCase
     public function testConvert()
     public function testConvert()
     {
     {
         $minKey = new \MongoMinKey();
         $minKey = new \MongoMinKey();
+        $this->skipTestUnless($minKey instanceof TypeInterface);
         $this->assertInstanceOf('MongoDB\BSON\MinKey', $minKey->toBSONType());
         $this->assertInstanceOf('MongoDB\BSON\MinKey', $minKey->toBSONType());
     }
     }
 }
 }

+ 13 - 0
tests/Alcaeus/MongoDbAdapter/MongoRegexTest.php

@@ -1,6 +1,7 @@
 <?php
 <?php
 
 
 namespace Alcaeus\MongoDbAdapter\Tests;
 namespace Alcaeus\MongoDbAdapter\Tests;
+use Alcaeus\MongoDbAdapter\TypeInterface;
 
 
 /**
 /**
  * @author alcaeus <alcaeus@alcaeus.org>
  * @author alcaeus <alcaeus@alcaeus.org>
@@ -15,6 +16,16 @@ class MongoRegexTest extends TestCase
 
 
         $this->assertSame('/abc/i', (string) $regex);
         $this->assertSame('/abc/i', (string) $regex);
 
 
+        return $regex;
+    }
+
+    /**
+     * @depends testCreate
+     */
+    public function testConvertToBson(\MongoRegex $regex)
+    {
+        $this->skipTestUnless($regex instanceof TypeInterface);
+
         $bsonRegex = $regex->toBSONType();
         $bsonRegex = $regex->toBSONType();
         $this->assertInstanceOf('MongoDB\BSON\Regex', $bsonRegex);
         $this->assertInstanceOf('MongoDB\BSON\Regex', $bsonRegex);
         $this->assertSame('abc', $bsonRegex->getPattern());
         $this->assertSame('abc', $bsonRegex->getPattern());
@@ -23,6 +34,8 @@ class MongoRegexTest extends TestCase
 
 
     public function testCreateWithBsonType()
     public function testCreateWithBsonType()
     {
     {
+        $this->skipTestUnless(in_array(TypeInterface::class, class_implements('MongoRegex')));
+
         $bsonRegex = new \MongoDB\BSON\Regex('abc', 'i');
         $bsonRegex = new \MongoDB\BSON\Regex('abc', 'i');
         $regex = new \MongoRegex($bsonRegex);
         $regex = new \MongoRegex($bsonRegex);
 
 

+ 13 - 0
tests/Alcaeus/MongoDbAdapter/MongoTimestampTest.php

@@ -1,6 +1,7 @@
 <?php
 <?php
 
 
 namespace Alcaeus\MongoDbAdapter\Tests;
 namespace Alcaeus\MongoDbAdapter\Tests;
+use Alcaeus\MongoDbAdapter\TypeInterface;
 
 
 /**
 /**
  * @author alcaeus <alcaeus@alcaeus.org>
  * @author alcaeus <alcaeus@alcaeus.org>
@@ -15,6 +16,16 @@ class MongoTimestampTest extends TestCase
 
 
         $this->assertSame('1234567890', (string) $timestamp);
         $this->assertSame('1234567890', (string) $timestamp);
 
 
+        return $timestamp;
+    }
+
+    /**
+     * @depends testCreate
+     */
+    public function testConvertToBson(\MongoTimestamp $timestamp)
+    {
+        $this->skipTestUnless($timestamp instanceof TypeInterface);
+
         $bsonTimestamp = $timestamp->toBSONType();
         $bsonTimestamp = $timestamp->toBSONType();
         $this->assertInstanceOf('MongoDB\BSON\Timestamp', $bsonTimestamp);
         $this->assertInstanceOf('MongoDB\BSON\Timestamp', $bsonTimestamp);
         $this->assertSame('[1234567890:987654321]', (string) $bsonTimestamp);
         $this->assertSame('[1234567890:987654321]', (string) $bsonTimestamp);
@@ -31,6 +42,8 @@ class MongoTimestampTest extends TestCase
 
 
     public function testCreateWithBsonTimestamp()
     public function testCreateWithBsonTimestamp()
     {
     {
+        $this->skipTestUnless(in_array(TypeInterface::class, class_implements('MongoTimestamp')));
+
         $bsonTimestamp = new \MongoDB\BSON\Timestamp(1234567890, 987654321);
         $bsonTimestamp = new \MongoDB\BSON\Timestamp(1234567890, 987654321);
         $timestamp = new \MongoTimestamp($bsonTimestamp);
         $timestamp = new \MongoTimestamp($bsonTimestamp);
 
 

+ 21 - 15
tests/Alcaeus/MongoDbAdapter/MongoUpdateBatchTest.php

@@ -17,12 +17,10 @@ class MongoUpdateBatchTest extends TestCase
         $this->assertTrue($batch->add(['q' => ['foo' => 'bar'], 'u' => ['$set' => ['foo' => 'foo']]]));
         $this->assertTrue($batch->add(['q' => ['foo' => 'bar'], 'u' => ['$set' => ['foo' => 'foo']]]));
 
 
         $expected = [
         $expected = [
-            'ok' => 1.0,
-            'nInserted' => 0,
             'nMatched' => 1,
             'nMatched' => 1,
             'nModified' => 1,
             'nModified' => 1,
             'nUpserted' => 0,
             'nUpserted' => 0,
-            'nRemoved' => 0,
+            'ok' => true,
         ];
         ];
 
 
         $this->assertSame($expected, $batch->execute());
         $this->assertSame($expected, $batch->execute());
@@ -49,12 +47,10 @@ class MongoUpdateBatchTest extends TestCase
         $this->assertTrue($batch->add(['q' => ['foo' => 'bar'], 'u' => ['$set' => ['foo' => 'foo']], 'multi' => true]));
         $this->assertTrue($batch->add(['q' => ['foo' => 'bar'], 'u' => ['$set' => ['foo' => 'foo']], 'multi' => true]));
 
 
         $expected = [
         $expected = [
-            'ok' => 1.0,
-            'nInserted' => 0,
             'nMatched' => 2,
             'nMatched' => 2,
             'nModified' => 2,
             'nModified' => 2,
             'nUpserted' => 0,
             'nUpserted' => 0,
-            'nRemoved' => 0,
+            'ok' => true,
         ];
         ];
 
 
         $this->assertSame($expected, $batch->execute());
         $this->assertSame($expected, $batch->execute());
@@ -69,23 +65,33 @@ class MongoUpdateBatchTest extends TestCase
 
 
     public function testUpsert()
     public function testUpsert()
     {
     {
+        $document = ['foo' => 'foo'];
+        $this->getCollection()->insert($document);
         $batch = new \MongoUpdateBatch($this->getCollection());
         $batch = new \MongoUpdateBatch($this->getCollection());
 
 
-        $this->assertTrue($batch->add(['q' => [], 'u' => ['$set' => ['foo' => 'bar']], 'upsert' => true]));
+        $this->assertTrue($batch->add(['q' => ['foo' => 'foo'], 'u' => ['$set' => ['foo' => 'bar']], 'upsert' => true]));
+        $this->assertTrue($batch->add(['q' => ['bar' => 'foo'], 'u' => ['$set' => ['foo' => 'bar']], 'upsert' => true]));
 
 
         $expected = [
         $expected = [
-            'ok' => 1.0,
-            'nInserted' => 0,
-            'nMatched' => 0,
-            'nModified' => 0,
+            'upserted' => [
+                [
+                    'index' => 1,
+                ]
+            ],
+            'nMatched' => 1,
+            'nModified' => 1,
             'nUpserted' => 1,
             'nUpserted' => 1,
-            'nRemoved' => 0,
+            'ok' => true,
         ];
         ];
 
 
-        $this->assertSame($expected, $batch->execute());
+        $result = $batch->execute();
+        $this->assertArraySubset($expected, $result);
+
+        $this->assertInstanceOf('MongoId', $result['upserted'][0]['_id']);
 
 
         $newCollection = $this->getCheckDatabase()->selectCollection('test');
         $newCollection = $this->getCheckDatabase()->selectCollection('test');
-        $this->assertSame(1, $newCollection->count());
+        $this->assertSame(0, $newCollection->count(['foo' => 'foo']));
+        $this->assertSame(2, $newCollection->count());
         $record = $newCollection->findOne();
         $record = $newCollection->findOne();
         $this->assertNotNull($record);
         $this->assertNotNull($record);
         $this->assertObjectHasAttribute('foo', $record);
         $this->assertObjectHasAttribute('foo', $record);
@@ -97,7 +103,7 @@ class MongoUpdateBatchTest extends TestCase
         $collection = $this->getCollection();
         $collection = $this->getCollection();
         $batch = new \MongoUpdateBatch($collection);
         $batch = new \MongoUpdateBatch($collection);
 
 
-        $this->setExpectedException('Exception', 'invalid item');
+        $this->setExpectedException('Exception', "Expected \$item to contain 'q' key");
 
 
         $batch->add([]);
         $batch->add([]);
     }
     }

+ 18 - 0
tests/Alcaeus/MongoDbAdapter/TestCase.php

@@ -145,4 +145,22 @@ abstract class TestCase extends \PHPUnit_Framework_TestCase
     {
     {
         return $this->configureFailPoint("maxTimeAlwaysTimeOut", array("times" => 1));
         return $this->configureFailPoint("maxTimeAlwaysTimeOut", array("times" => 1));
     }
     }
+
+    /**
+     * @param bool $condition
+     */
+    protected function skipTestUnless($condition)
+    {
+        $this->skipTestIf(! $condition);
+    }
+
+    /**
+     * @param bool $condition
+     */
+    protected function skipTestIf($condition)
+    {
+        if ($condition) {
+            $this->markTestSkipped('Test only applies when running against mongo-php-adapter');
+        }
+    }
 }
 }