-
Notifications
You must be signed in to change notification settings - Fork 265
PHPLIB-493: Unified test runner POC #783
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
41 commits
Select commit
Hold shift + click to select a range
735f256
Reduce isShardedCluster to a one-liner
jmikola 1845c2e
wip
jmikola 37da353
wip
jmikola 28c2ba5
wip
jmikola a589d5f
wip
jmikola 236a031
Clean up constraints and add test coverage
jmikola 7883988
Code fixes and fall back to assertInternalType for PHPUnit 6.x
jmikola e8c609f
phpcs fixes
jmikola 1d8e7be
Use Matches for CollectionData and validate operator syntax
jmikola 7045abf
Fix var usage
jmikola e7c58b2
Fix static method call and allow empty string in $$matchesHexBytes
jmikola a6007cd
CS fixes
jmikola a9f80c7
Include PHPUnit functions via autoload-dev
jmikola 3f21c01
CR feedback
jmikola 67e21ad
IsBsonType helpers and new IsStream constraint
jmikola dfc2b5a
Changes to get CRUD POC tests running
jmikola f88f2ca
Implement expectEvents and change stream tests
jmikola 82c95f5
Implement GridFS, transactions, and collate events by client
jmikola 9455db5
Remove stream entities and update GridFS operations
jmikola 032cb4d
Todo items for Matches constraint
jmikola e548e8b
Assert basic structure of test files in data provider
jmikola 2609297
Use assertion for testFailingTests
jmikola 0fb5829
Implement session tests
jmikola f0f70ae
Consider PHPUnit Warnings for expectError in GridFS tests
jmikola 2e0d254
Disable fail points after tests
jmikola 6932497
Fix targetedFailPoint operation
jmikola e78cbb8
Fix MatchesTest and make EntityMap optional
jmikola 8853503
Note cycling references and killAllSessions before each test
jmikola 2434700
Sync unified spec tests
jmikola dbf4228
Fix phpcs validation
jmikola 59f4c72
assertHasOnlyKeys requires array or stdClass
jmikola c49b411
Add missing valid-fail test
jmikola 64b4118
Fix phpcs error
jmikola e544081
Remove nullable return type hint for PHP 7.0
jmikola 97c9f39
Update session test
jmikola b572a4c
Sync returnDocument-enum-invalid.json
jmikola abc7426
Handle issues from code review
alcaeus 4bb37be
fix phpcs
alcaeus 9a89118
fix tests
alcaeus ea4a99b
Fix wrong continue statement
alcaeus f21efac
Handle readPreferenceTags in URI options
alcaeus File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,90 @@ | ||
<?php | ||
|
||
namespace MongoDB\Tests\UnifiedSpecTests; | ||
|
||
use ArrayIterator; | ||
use IteratorIterator; | ||
use MongoDB\Client; | ||
use MongoDB\Driver\ReadConcern; | ||
use MongoDB\Driver\ReadPreference; | ||
use MongoDB\Driver\WriteConcern; | ||
use MongoDB\Tests\UnifiedSpecTests\Constraint\Matches; | ||
use MultipleIterator; | ||
use stdClass; | ||
use function assertContainsOnly; | ||
use function assertInternalType; | ||
use function assertNotNull; | ||
use function assertThat; | ||
use function sprintf; | ||
|
||
class CollectionData | ||
{ | ||
/** @var string */ | ||
private $collectionName; | ||
|
||
/** @var string */ | ||
private $databaseName; | ||
|
||
/** @var array */ | ||
private $documents; | ||
|
||
public function __construct(stdClass $o) | ||
{ | ||
assertInternalType('string', $o->collectionName); | ||
jmikola marked this conversation as resolved.
Show resolved
Hide resolved
|
||
$this->collectionName = $o->collectionName; | ||
|
||
assertInternalType('string', $o->databaseName); | ||
$this->databaseName = $o->databaseName; | ||
|
||
assertInternalType('array', $o->documents); | ||
assertContainsOnly('object', $o->documents); | ||
$this->documents = $o->documents; | ||
} | ||
|
||
public function prepareInitialData(Client $client) | ||
{ | ||
$database = $client->selectDatabase( | ||
$this->databaseName, | ||
['writeConcern' => new WriteConcern(WriteConcern::MAJORITY)] | ||
); | ||
|
||
$database->dropCollection($this->collectionName); | ||
alcaeus marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
if (empty($this->documents)) { | ||
$database->createCollection($this->collectionName); | ||
|
||
return; | ||
} | ||
|
||
$database->selectCollection($this->collectionName)->insertMany($this->documents); | ||
} | ||
|
||
public function assertOutcome(Client $client) | ||
{ | ||
$collection = $client->selectCollection( | ||
$this->databaseName, | ||
$this->collectionName, | ||
[ | ||
'readConcern' => new ReadConcern(ReadConcern::LOCAL), | ||
'readPreference' => new ReadPreference(ReadPreference::PRIMARY), | ||
] | ||
); | ||
|
||
$cursor = $collection->find([], ['sort' => ['_id' => 1]]); | ||
|
||
$mi = new MultipleIterator(MultipleIterator::MIT_NEED_ANY); | ||
$mi->attachIterator(new ArrayIterator($this->documents)); | ||
$mi->attachIterator(new IteratorIterator($cursor)); | ||
|
||
foreach ($mi as $i => $documents) { | ||
jmikola marked this conversation as resolved.
Show resolved
Hide resolved
|
||
list($expectedDocument, $actualDocument) = $documents; | ||
assertNotNull($expectedDocument); | ||
assertNotNull($actualDocument); | ||
|
||
/* Prohibit extra root keys and disable operators to enforce exact | ||
* matching of documents. Key order variation is still allowed. */ | ||
$constraint = new Matches($expectedDocument, null, false, false); | ||
assertThat($actualDocument, $constraint, sprintf('documents[%d] match', $i)); | ||
} | ||
} | ||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,210 @@ | ||
<?php | ||
|
||
namespace MongoDB\Tests\UnifiedSpecTests\Constraint; | ||
|
||
use LogicException; | ||
use MongoDB\BSON\BinaryInterface; | ||
use MongoDB\BSON\DBPointer; | ||
use MongoDB\BSON\Decimal128Interface; | ||
use MongoDB\BSON\Int64; | ||
use MongoDB\BSON\JavascriptInterface; | ||
use MongoDB\BSON\MaxKeyInterface; | ||
use MongoDB\BSON\MinKeyInterface; | ||
use MongoDB\BSON\ObjectIdInterface; | ||
use MongoDB\BSON\RegexInterface; | ||
use MongoDB\BSON\Serializable; | ||
use MongoDB\BSON\Symbol; | ||
use MongoDB\BSON\TimestampInterface; | ||
use MongoDB\BSON\Type; | ||
use MongoDB\BSON\Undefined; | ||
use MongoDB\BSON\UTCDateTimeInterface; | ||
use MongoDB\Model\BSONArray; | ||
use MongoDB\Model\BSONDocument; | ||
use PHPUnit\Framework\Constraint\Constraint; | ||
use PHPUnit\Framework\Constraint\LogicalOr; | ||
use RuntimeException; | ||
use Symfony\Bridge\PhpUnit\ConstraintTrait; | ||
use function array_keys; | ||
use function array_map; | ||
use function count; | ||
use function in_array; | ||
use function is_array; | ||
use function is_bool; | ||
use function is_float; | ||
use function is_int; | ||
use function is_object; | ||
use function is_string; | ||
use function range; | ||
use function sprintf; | ||
use const PHP_INT_SIZE; | ||
|
||
final class IsBsonType extends Constraint | ||
{ | ||
use ConstraintTrait; | ||
|
||
/** @var array */ | ||
private static $types = [ | ||
'double', | ||
'string', | ||
'object', | ||
'array', | ||
'binData', | ||
'undefined', | ||
'objectId', | ||
'bool', | ||
'date', | ||
'null', | ||
'regex', | ||
'dbPointer', | ||
'javascript', | ||
'symbol', | ||
'javascriptWithScope', | ||
'int', | ||
'timestamp', | ||
'long', | ||
'decimal', | ||
'minKey', | ||
'maxKey', | ||
]; | ||
|
||
/** @var string */ | ||
private $type; | ||
|
||
public function __construct(string $type) | ||
{ | ||
if (! in_array($type, self::$types)) { | ||
throw new RuntimeException(sprintf('Type specified for %s <%s> is not a valid type', self::class, $type)); | ||
} | ||
|
||
$this->type = $type; | ||
} | ||
|
||
public static function any() : LogicalOr | ||
{ | ||
return self::anyOf(...self::$types); | ||
} | ||
|
||
public static function anyOf(string ...$types) : Constraint | ||
{ | ||
if (count($types) === 1) { | ||
return new self(...$types); | ||
alcaeus marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} | ||
|
||
return LogicalOr::fromConstraints(...array_map(function ($type) { | ||
return new self($type); | ||
}, $types)); | ||
} | ||
|
||
private function doMatches($other) : bool | ||
{ | ||
switch ($this->type) { | ||
case 'double': | ||
return is_float($other); | ||
case 'string': | ||
return is_string($other); | ||
case 'object': | ||
return self::isObject($other); | ||
case 'array': | ||
return self::isArray($other); | ||
case 'binData': | ||
return $other instanceof BinaryInterface; | ||
case 'undefined': | ||
return $other instanceof Undefined; | ||
case 'objectId': | ||
return $other instanceof ObjectIdInterface; | ||
case 'bool': | ||
return is_bool($other); | ||
case 'date': | ||
return $other instanceof UTCDateTimeInterface; | ||
case 'null': | ||
return $other === null; | ||
case 'regex': | ||
return $other instanceof RegexInterface; | ||
case 'dbPointer': | ||
return $other instanceof DBPointer; | ||
case 'javascript': | ||
return $other instanceof JavascriptInterface && $other->getScope() === null; | ||
case 'symbol': | ||
return $other instanceof Symbol; | ||
case 'javascriptWithScope': | ||
return $other instanceof JavascriptInterface && $other->getScope() !== null; | ||
case 'int': | ||
return is_int($other); | ||
case 'timestamp': | ||
return $other instanceof TimestampInterface; | ||
case 'long': | ||
if (PHP_INT_SIZE == 4) { | ||
return $other instanceof Int64; | ||
} | ||
|
||
return is_int($other); | ||
case 'decimal': | ||
return $other instanceof Decimal128Interface; | ||
case 'minKey': | ||
return $other instanceof MinKeyInterface; | ||
case 'maxKey': | ||
return $other instanceof MaxKeyInterface; | ||
default: | ||
// This should already have been caught in the constructor | ||
throw new LogicException('Unsupported type: ' . $this->type); | ||
} | ||
} | ||
|
||
private function doToString() : string | ||
{ | ||
return sprintf('is of BSON type "%s"', $this->type); | ||
} | ||
|
||
private static function isArray($other) : bool | ||
{ | ||
if ($other instanceof BSONArray) { | ||
return true; | ||
} | ||
|
||
// Serializable can produce an array or object, so recurse on its output | ||
if ($other instanceof Serializable) { | ||
return self::isArray($other->bsonSerialize()); | ||
} | ||
|
||
if (! is_array($other)) { | ||
return false; | ||
} | ||
|
||
// Empty and indexed arrays serialize as BSON arrays | ||
return self::isArrayEmptyOrIndexed($other); | ||
} | ||
|
||
private static function isObject($other) : bool | ||
{ | ||
if ($other instanceof BSONDocument) { | ||
return true; | ||
} | ||
|
||
// Serializable can produce an array or object, so recurse on its output | ||
if ($other instanceof Serializable) { | ||
return self::isObject($other->bsonSerialize()); | ||
} | ||
|
||
// Non-empty, associative arrays serialize as BSON objects | ||
if (is_array($other)) { | ||
return ! self::isArrayEmptyOrIndexed($other); | ||
} | ||
|
||
if (! is_object($other)) { | ||
return false; | ||
} | ||
|
||
/* Serializable has already been handled, so any remaining instances of | ||
* Type will not serialize as BSON objects */ | ||
return ! $other instanceof Type; | ||
} | ||
|
||
private static function isArrayEmptyOrIndexed(array $a) : bool | ||
{ | ||
if (empty($a)) { | ||
return true; | ||
} | ||
|
||
return array_keys($a) === range(0, count($a) - 1); | ||
alcaeus marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} | ||
} |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.