Jeroen De Dauw has uploaded a new change for review. https://gerrit.wikimedia.org/r/72731
Change subject: Added QueryDeserializer ...................................................................... Added QueryDeserializer * Added QueryDeserializer * Added QueryDeserializerTest * Added QueryRountripTest (integration) * Fixed some misalignments between serialization and deserialization code Change-Id: I82d6fac3af661c44c1550e949521ea9f61654785 --- R Tests/Integration/Serialization/DescriptionRoundtripTest.php A Tests/Integration/Serialization/QueryRoundtripTest.php R Tests/Integration/Serialization/QuerySerialializationTest.php A Tests/Phpunit/Deserializers/QueryDeserializerTest.php M Tests/Phpunit/Serializers/Exceptions/UnsupportedObjectExceptionTest.php M Tests/Phpunit/Serializers/QueryOptionsSerializerTest.php M Tests/Phpunit/Serializers/QuerySerializerTest.php M Tests/Phpunit/Serializers/SelectionRequestSerializerTest.php M src/Ask/Deserializers/DispatchingDeserializer.php A src/Ask/Deserializers/QueryDeserializer.php M src/Ask/Deserializers/Strategies/SelectionRequestDeserializationStrategy.php M src/Ask/Deserializers/Strategies/SortExpressionDeserializationStrategy.php M src/Ask/Deserializers/TypedObjectDeserializer.php M src/Ask/Serializers/QueryOptionsSerializer.php M src/Ask/Serializers/QuerySerializer.php M src/Ask/Serializers/SelectionRequestSerializer.php 16 files changed, 412 insertions(+), 30 deletions(-) git pull ssh://gerrit.wikimedia.org:29418/mediawiki/extensions/Ask refs/changes/31/72731/1 diff --git a/Tests/Integration/Serializers/DescriptionRoundtripTest.php b/Tests/Integration/Serialization/DescriptionRoundtripTest.php similarity index 97% rename from Tests/Integration/Serializers/DescriptionRoundtripTest.php rename to Tests/Integration/Serialization/DescriptionRoundtripTest.php index 8f75421..b0d81f6 100644 --- a/Tests/Integration/Serializers/DescriptionRoundtripTest.php +++ b/Tests/Integration/Serialization/DescriptionRoundtripTest.php @@ -1,6 +1,6 @@ <?php -namespace Ask\Tests\Integration\Serializers; +namespace Ask\Tests\Integration\Serialization; use Ask\Deserializers\DescriptionDeserializer; use Ask\Language\Description\AnyValue; diff --git a/Tests/Integration/Serialization/QueryRoundtripTest.php b/Tests/Integration/Serialization/QueryRoundtripTest.php new file mode 100644 index 0000000..0fbd453 --- /dev/null +++ b/Tests/Integration/Serialization/QueryRoundtripTest.php @@ -0,0 +1,119 @@ +<?php + +namespace Ask\Tests\Integration\Serialization; + +use Ask\Deserializers\DescriptionDeserializer; +use Ask\Deserializers\DispatchingDeserializer; +use Ask\Deserializers\QueryDeserializer; +use Ask\Deserializers\QueryOptionsDeserializer; +use Ask\Deserializers\SelectionRequestDeserializer; +use Ask\Deserializers\SortExpressionDeserializer; +use Ask\Language\Description\AnyValue; +use Ask\Language\Description\Conjunction; +use Ask\Language\Description\SomeProperty; +use Ask\Language\Description\ValueDescription; +use Ask\Language\Option\PropertyValueSortExpression; +use Ask\Language\Option\QueryOptions; +use Ask\Language\Option\SortExpression; +use Ask\Language\Option\SortOptions; +use Ask\Language\Query; +use Ask\Language\Selection\PropertySelection; +use Ask\Language\Selection\SubjectSelection; +use Ask\Serializers\DescriptionSerializer; +use Ask\Serializers\DispatchingSerializer; +use Ask\Serializers\QueryOptionsSerializer; +use Ask\Serializers\QuerySerializer; +use Ask\Serializers\SelectionRequestSerializer; +use Ask\Serializers\SortExpressionSerializer; +use DataValues\DataValueFactory; +use DataValues\StringValue; + +/** + * @file + * @since 0.1 + * + * @ingroup Ask + * @group Ask + * + * @licence GNU GPL v2+ + * @author Jeroen De Dauw < [email protected] > + */ +class QueryRoundtripTest extends \PHPUnit_Framework_TestCase { + + protected function newQueryDeserializer() { + $componentDeserializer = new DispatchingDeserializer(); + + $componentDeserializer->addDeserializer( new QueryOptionsDeserializer( $componentDeserializer ) ); + + $dvFactory = new DataValueFactory(); + $dvFactory->registerDataValue( 'string', 'DataValues\StringValue' ); + + $componentDeserializer->addDeserializer( new DescriptionDeserializer( $dvFactory ) ); + $componentDeserializer->addDeserializer( new SortExpressionDeserializer( $dvFactory ) ); + $componentDeserializer->addDeserializer( new SelectionRequestDeserializer( $dvFactory ) ); + + return new QueryDeserializer( $componentDeserializer ); + } + + protected function newQuerySerializer() { + $dispatchingSerializer = new DispatchingSerializer(); + + $dispatchingSerializer->addSerializer( new DescriptionSerializer() ); + $dispatchingSerializer->addSerializer( new SelectionRequestSerializer() ); + $dispatchingSerializer->addSerializer( new QueryOptionsSerializer( $dispatchingSerializer ) ); + $dispatchingSerializer->addSerializer( new SortExpressionSerializer() ); + + return new QuerySerializer( $dispatchingSerializer ); + } + + /** + * @dataProvider queryProvider + * @param Query $query + */ + public function testCanRoundtripQueryThroughSerialization( Query $query ) { + $serialization = $this->newQuerySerializer()->serialize( $query ); + $deserialization = $this->newQueryDeserializer()->deserialize( $serialization ); + + $this->assertEquals( $query, $deserialization ); + } + + public function queryProvider() { + $p42 = new StringValue( 'p42' ); + $p9001 = new StringValue( 'p9001' ); + $foo = new StringValue( 'foo' ); + + $argLists = array(); + + $argLists[] = array( new Query( + new AnyValue(), + array( + ), + new QueryOptions( + 10, + 0 + ) + ) ); + + $argLists[] = array( new Query( + new Conjunction( array( + new SomeProperty( $p42, new AnyValue() ), + new SomeProperty( $p9001, new ValueDescription( $foo ) ), + ) ), + array( + new SubjectSelection(), + new PropertySelection( $p42 ), + new PropertySelection( $p9001 ), + ), + new QueryOptions( + 100, + 42, + new SortOptions( array( + new PropertyValueSortExpression( $p42, SortExpression::DIRECTION_ASCENDING ) + ) ) + ) + ) ); + + return $argLists; + } + +} diff --git a/Tests/Integration/Serializers/QuerySerialializationTest.php b/Tests/Integration/Serialization/QuerySerialializationTest.php similarity index 94% rename from Tests/Integration/Serializers/QuerySerialializationTest.php rename to Tests/Integration/Serialization/QuerySerialializationTest.php index 187606f..a381ef0 100644 --- a/Tests/Integration/Serializers/QuerySerialializationTest.php +++ b/Tests/Integration/Serialization/QuerySerialializationTest.php @@ -1,6 +1,6 @@ <?php -namespace Ask\Tests\Integration\Serializers; +namespace Ask\Tests\Integration\Serialization; use Ask\Language\Description\AnyValue; use Ask\Language\Description\Conjunction; @@ -117,7 +117,7 @@ 'limit' => 100, 'offset' => 42, 'sort' => array( - 'expressions' => (object)array( + 'expressions' => array( array( 'objectType' => 'sortExpression', 'sortExpressionType' => 'propertyValue', @@ -129,22 +129,22 @@ ), ), ), - 'selectionRequests' => (object)array( + 'selectionRequests' => array( array( 'objectType' => 'selectionRequest', - 'selectionType' => 'subject', - 'value' => null, + 'selectionRequestType' => 'subject', + 'value' => array(), ), array( 'objectType' => 'selectionRequest', - 'selectionType' => 'property', + 'selectionRequestType' => 'property', 'value' => array( 'property' => $p42->toArray(), ), ), array( 'objectType' => 'selectionRequest', - 'selectionType' => 'property', + 'selectionRequestType' => 'property', 'value' => array( 'property' => $p9001->toArray(), ), diff --git a/Tests/Phpunit/Deserializers/QueryDeserializerTest.php b/Tests/Phpunit/Deserializers/QueryDeserializerTest.php new file mode 100644 index 0000000..1d9dd6d --- /dev/null +++ b/Tests/Phpunit/Deserializers/QueryDeserializerTest.php @@ -0,0 +1,215 @@ +<?php + +namespace Ask\Tests\Phpunit\Serialization; + +use Ask\Deserializers\DescriptionDeserializer; +use Ask\Deserializers\DispatchingDeserializer; +use Ask\Deserializers\QueryDeserializer; +use Ask\Deserializers\QueryOptionsDeserializer; +use Ask\Deserializers\SortExpressionDeserializer; +use DataValues\DataValueFactory; + +/** + * @covers Ask\Deserializers\QueryDeserializer + * + * @file + * @since 0.1 + * + * @ingroup Ask + * @group Ask + * + * @licence GNU GPL v2+ + * @author Jeroen De Dauw < [email protected] > + */ +class QueryDeserializerTest extends \PHPUnit_Framework_TestCase { + + protected function newQueryDeserializer() { + $componentDeserializer = new DispatchingDeserializer(); + + $componentDeserializer->addDeserializer( new QueryOptionsDeserializer( $componentDeserializer ) ); + + $dvFactory = new DataValueFactory(); + $dvFactory->registerDataValue( 'string', 'DataValues\StringValue' ); + + $componentDeserializer->addDeserializer( new DescriptionDeserializer( $dvFactory ) ); + $componentDeserializer->addDeserializer( new SortExpressionDeserializer( $dvFactory ) ); + + return new QueryDeserializer( $componentDeserializer ); + } + + /** + * @dataProvider invalidObjectTypeProvider + */ + public function testCannotDeserializeWithInvalidObjectType( $notAQuery ) { + $serializer = $this->newQueryDeserializer(); + + $this->assertFalse( $serializer->canDeserialize( $notAQuery ) ); + + $this->setExpectedException( 'Ask\Deserializers\Exceptions\UnsupportedTypeException' ); + $serializer->deserialize( $notAQuery ); + } + + public function invalidObjectTypeProvider() { + $argLists = array(); + + $argLists[] = array( array( + 'objectType' => 'foobar', + ) ); + + $argLists[] = array( array( + 'objectType' => 'QUERY', + ) ); + + $argLists[] = array( array( + 'objectType' => null, + ) ); + + $argLists[] = array( array( + 'objectType' => array(), + ) ); + + $argLists[] = array( array( + 'objectType' => 42, + ) ); + + return $argLists; + } + + /** + * @dataProvider missingObjectTypeProvider + */ + public function testCannotDeserilaizeWithoutObjectType( $notAQuery ) { + $serializer = $this->newQueryDeserializer(); + + $this->assertFalse( $serializer->canDeserialize( $notAQuery ) ); + + $this->setExpectedException( 'Ask\Deserializers\Exceptions\MissingTypeException' ); + $serializer->deserialize( $notAQuery ); + } + + public function missingObjectTypeProvider() { + $argLists = array(); + + $argLists[] = array( null ); + $argLists[] = array( array() ); + $argLists[] = array( 'foo bar' ); + + $argLists[] = array( array( + 'ObjectType' => 'query', + ) ); + + $argLists[] = array( array( + 'OBJECTTYPE' => 'query', + ) ); + + return $argLists; + } + + /** + * @dataProvider optionsWithMissingAttributeProvider + */ + public function testPropertySelectionRequiresAllAttributes( array $incompleteSerialization ) { + $this->setExpectedException( 'Ask\Deserializers\Exceptions\MissingAttributeException' ); + $this->newQueryDeserializer()->deserialize( $incompleteSerialization ); + } + + public function optionsWithMissingAttributeProvider() { + $argLists = array(); + + $argLists[] = array( array( + 'objectType' => 'query', + 'description' => array( + 'objectType' => 'description', + 'descriptionType' => 'anyValue', + 'value' => array() + ), + 'options' => array( + 'limit' => 10, + 'offset' => 1, + 'sort' => array( + 'expressions' => array() + ) + ), + ) ); + + $argLists[] = array( array( + 'objectType' => 'query', + 'options' => array( + 'limit' => 10, + 'offset' => 1, + 'sort' => array( + 'expressions' => array() + ) + ), + 'selectionRequests' => array() + ) ); + + $argLists[] = array( array( + 'objectType' => 'query', + 'description' => array( + 'objectType' => 'description', + 'descriptionType' => 'anyValue', + 'value' => array() + ), + 'selectionRequests' => array() + ) ); + + return $argLists; + } + + /** + * @dataProvider optionsWithInvalidAttributeProvider + */ + public function testPropertySelectionRequiresValidAttributes( array $invalidSerialization ) { + $this->setExpectedException( 'Ask\Deserializers\Exceptions\DeserializationException' ); + $this->newQueryDeserializer()->deserialize( $invalidSerialization ); + } + + public function optionsWithInvalidAttributeProvider() { + $argLists = array(); + + $argLists[] = array( array( + 'objectType' => 'query', + 'description' => 'hax', + 'options' => array( + 'limit' => 10, + 'offset' => 1, + 'sort' => array( + 'expressions' => array() + ) + ), + 'selectionRequests' => array() + ) ); + + $argLists[] = array( array( + 'objectType' => 'query', + 'description' => array( + 'objectType' => 'description', + 'descriptionType' => 'anyValue', + 'value' => array() + ), + 'options' => 'hax', + 'selectionRequests' => array() + ) ); + + $argLists[] = array( array( + 'objectType' => 'query', + 'description' => array( + 'objectType' => 'description', + 'descriptionType' => 'anyValue', + 'value' => array() + ), + 'options' => array( + 'limit' => 10, + 'offset' => 1, + 'sort' => array( + 'expressions' => array() + ) + ), + 'selectionRequests' => 'hax' + ) ); + + return $argLists; + } + +} diff --git a/Tests/Phpunit/Serializers/Exceptions/UnsupportedObjectExceptionTest.php b/Tests/Phpunit/Serializers/Exceptions/UnsupportedObjectExceptionTest.php index 5b4a3f1..c1bf4a6 100644 --- a/Tests/Phpunit/Serializers/Exceptions/UnsupportedObjectExceptionTest.php +++ b/Tests/Phpunit/Serializers/Exceptions/UnsupportedObjectExceptionTest.php @@ -19,7 +19,7 @@ class UnsupportedObjectExceptionTest extends \PHPUnit_Framework_TestCase { public function testConstructorWithOnlyRequiredArguments() { - $object = (object)array( 'the' => 'game' ); + $object = array( 'the' => 'game' ); $exception = new UnsupportedObjectException( $object ); @@ -27,7 +27,7 @@ } public function testConstructorWithAllArguments() { - $object = (object)array( 'the' => 'game' ); + $object = array( 'the' => 'game' ); $message = 'NyanData all the way across the sky!'; $previous = new \Exception( 'Onoez!' ); diff --git a/Tests/Phpunit/Serializers/QueryOptionsSerializerTest.php b/Tests/Phpunit/Serializers/QueryOptionsSerializerTest.php index 24fd9c1..79373ee 100644 --- a/Tests/Phpunit/Serializers/QueryOptionsSerializerTest.php +++ b/Tests/Phpunit/Serializers/QueryOptionsSerializerTest.php @@ -34,7 +34,7 @@ 'limit' => $options->getLimit(), 'offset' => $options->getOffset(), 'sort' => array( - 'expressions' => (object)array_map( + 'expressions' => array_map( function( SortExpression $expression ) use ( $sortExpressionSerializer ) { return $sortExpressionSerializer->serialize( $expression ); }, diff --git a/Tests/Phpunit/Serializers/QuerySerializerTest.php b/Tests/Phpunit/Serializers/QuerySerializerTest.php index 31a1f29..652b957 100644 --- a/Tests/Phpunit/Serializers/QuerySerializerTest.php +++ b/Tests/Phpunit/Serializers/QuerySerializerTest.php @@ -50,7 +50,7 @@ 'objectType' => 'query', 'description' => 'foo bar baz', 'options' => 'foo bar baz', - 'selectionRequests' => (object)array(), + 'selectionRequests' => array(), ); $this->assertEquals( $expectedSerialization, $actualSerialization ); diff --git a/Tests/Phpunit/Serializers/SelectionRequestSerializerTest.php b/Tests/Phpunit/Serializers/SelectionRequestSerializerTest.php index 67e17a3..09c093f 100644 --- a/Tests/Phpunit/Serializers/SelectionRequestSerializerTest.php +++ b/Tests/Phpunit/Serializers/SelectionRequestSerializerTest.php @@ -50,8 +50,8 @@ new SubjectSelection(), array( 'objectType' => 'selectionRequest', - 'selectionType' => 'subject', - 'value' => null, + 'selectionRequestType' => 'subject', + 'value' => array(), ) ); @@ -61,7 +61,7 @@ new PropertySelection( $stringValue ), array( 'objectType' => 'selectionRequest', - 'selectionType' => 'property', + 'selectionRequestType' => 'property', 'value' => array( 'property' => $stringValue->toArray(), ), diff --git a/src/Ask/Deserializers/DispatchingDeserializer.php b/src/Ask/Deserializers/DispatchingDeserializer.php index 1c974bf..ac61c13 100644 --- a/src/Ask/Deserializers/DispatchingDeserializer.php +++ b/src/Ask/Deserializers/DispatchingDeserializer.php @@ -24,7 +24,7 @@ /** * @param Deserializer[] $deserializers */ - public function __construct( array $deserializers ) { + public function __construct( array $deserializers = array() ) { $this->assertAreDeserializers( $deserializers ); $this->deserializers = $deserializers; } diff --git a/src/Ask/Deserializers/QueryDeserializer.php b/src/Ask/Deserializers/QueryDeserializer.php new file mode 100644 index 0000000..adbd3db --- /dev/null +++ b/src/Ask/Deserializers/QueryDeserializer.php @@ -0,0 +1,49 @@ +<?php + +namespace Ask\Deserializers; + +use Ask\Language\Query; + +/** + * @since 0.1 + * + * @file + * @ingroup Ask + * + * @licence GNU GPL v2+ + * @author Jeroen De Dauw < [email protected] > + */ +class QueryDeserializer extends TypedObjectDeserializer { + + protected $componentDeserializer; + + public function __construct( Deserializer $componentDeserializer ) { + $this->componentDeserializer = $componentDeserializer; + + parent::__construct( 'query' ); + } + + public function deserialize( $serialization ) { + $this->assertCanDeserialize( $serialization ); + return $this->getDeserialization( $serialization ); + } + + protected function getDeserialization( array $serialization ) { + $this->requireAttributes( $serialization, 'description', 'options', 'selectionRequests' ); + + $this->assertAttributeIsArray( $serialization, 'selectionRequests' ); + + $selectionRequests = array(); + + foreach ( $serialization['selectionRequests'] as $selectionRequestSerialization ) { + $selectionRequests[] = $this->componentDeserializer->deserialize( $selectionRequestSerialization ); + } + + return new Query( + $this->componentDeserializer->deserialize( $serialization['description'] ), + $selectionRequests, + $this->componentDeserializer->deserialize( $serialization['options'] ) + ); + } + +} diff --git a/src/Ask/Deserializers/Strategies/SelectionRequestDeserializationStrategy.php b/src/Ask/Deserializers/Strategies/SelectionRequestDeserializationStrategy.php index 91a2110..8504ee6 100644 --- a/src/Ask/Deserializers/Strategies/SelectionRequestDeserializationStrategy.php +++ b/src/Ask/Deserializers/Strategies/SelectionRequestDeserializationStrategy.php @@ -32,14 +32,14 @@ * * @since 0.1 * - * @param string $sortExpressionType + * @param string $selectionRequestType * @param array $valueSerialization * * @return object * @throws DeserializationException */ - public function getDeserializedValue( $sortExpressionType, array $valueSerialization ) { - switch ( $sortExpressionType ) { + public function getDeserializedValue( $selectionRequestType, array $valueSerialization ) { + switch ( $selectionRequestType ) { case 'property': return $this->newPropertySelectionRequest( $valueSerialization ); break; @@ -48,7 +48,7 @@ break; } - throw new InvalidAttributeException( 'selectionRequestType', $sortExpressionType ); + throw new InvalidAttributeException( 'selectionRequestType', $selectionRequestType ); } protected function newPropertySelectionRequest( array $value ) { diff --git a/src/Ask/Deserializers/Strategies/SortExpressionDeserializationStrategy.php b/src/Ask/Deserializers/Strategies/SortExpressionDeserializationStrategy.php index 3497f07..2494660 100644 --- a/src/Ask/Deserializers/Strategies/SortExpressionDeserializationStrategy.php +++ b/src/Ask/Deserializers/Strategies/SortExpressionDeserializationStrategy.php @@ -31,15 +31,15 @@ * * @since 0.1 * - * @param string $sortExpressionType + * @param string $selectionRequestType * @param array $valueSerialization * * @return object * @throws DeserializationException */ - public function getDeserializedValue( $sortExpressionType, array $valueSerialization ) { - if ( $sortExpressionType !== 'propertyValue' ) { - throw new InvalidAttributeException( 'sortExpressionType', $sortExpressionType ); + public function getDeserializedValue( $selectionRequestType, array $valueSerialization ) { + if ( $selectionRequestType !== 'propertyValue' ) { + throw new InvalidAttributeException( 'sortExpressionType', $selectionRequestType ); } $this->requireAttribute( $valueSerialization, 'direction' ); diff --git a/src/Ask/Deserializers/TypedObjectDeserializer.php b/src/Ask/Deserializers/TypedObjectDeserializer.php index 462161b..0841f8b 100644 --- a/src/Ask/Deserializers/TypedObjectDeserializer.php +++ b/src/Ask/Deserializers/TypedObjectDeserializer.php @@ -6,7 +6,6 @@ use Ask\Deserializers\Exceptions\MissingAttributeException; use Ask\Deserializers\Exceptions\MissingTypeException; use Ask\Deserializers\Exceptions\UnsupportedTypeException; -use Ask\Deserializers\Strategies\TypedDeserializationStrategy; /** * @since 0.1 @@ -74,7 +73,7 @@ throw new InvalidAttributeException( $attributeName, $array[$attributeName], - "The internal type of this attribute needs to be '$internalType'" + "The internal type of attribute '$attributeName' needs to be '$internalType'" ); } } diff --git a/src/Ask/Serializers/QueryOptionsSerializer.php b/src/Ask/Serializers/QueryOptionsSerializer.php index 34a79f1..1c0e37b 100644 --- a/src/Ask/Serializers/QueryOptionsSerializer.php +++ b/src/Ask/Serializers/QueryOptionsSerializer.php @@ -48,7 +48,7 @@ // TODO: create a dedicated serializer for sort options 'sort' => array( - 'expressions' => (object)array_map( + 'expressions' => array_map( function( SortExpression $expression ) use ( $expressionSerializer ) { return $expressionSerializer->serialize( $expression ); }, diff --git a/src/Ask/Serializers/QuerySerializer.php b/src/Ask/Serializers/QuerySerializer.php index 4672ea8..19995e8 100644 --- a/src/Ask/Serializers/QuerySerializer.php +++ b/src/Ask/Serializers/QuerySerializer.php @@ -53,7 +53,7 @@ 'objectType' => 'query', 'description' => $this->componentSerializer->serialize( $query->getDescription() ), 'options' => $this->componentSerializer->serialize( $query->getOptions() ), - 'selectionRequests' => (object)$selectionRequests, + 'selectionRequests' => $selectionRequests, ); } diff --git a/src/Ask/Serializers/SelectionRequestSerializer.php b/src/Ask/Serializers/SelectionRequestSerializer.php index 352303f..76fe759 100644 --- a/src/Ask/Serializers/SelectionRequestSerializer.php +++ b/src/Ask/Serializers/SelectionRequestSerializer.php @@ -32,7 +32,7 @@ protected function getSerializedSelectionRequest( SelectionRequest $request ) { return array( 'objectType' => 'selectionRequest', - 'selectionType' => $request->getType(), + 'selectionRequestType' => $request->getType(), 'value' => $this->getValueSerialization( $request ), ); } @@ -45,7 +45,7 @@ } if ( $request instanceof SubjectSelection ) { - return null; + return array(); } throw new UnsupportedObjectException( $request ); -- To view, visit https://gerrit.wikimedia.org/r/72731 To unsubscribe, visit https://gerrit.wikimedia.org/r/settings Gerrit-MessageType: newchange Gerrit-Change-Id: I82d6fac3af661c44c1550e949521ea9f61654785 Gerrit-PatchSet: 1 Gerrit-Project: mediawiki/extensions/Ask Gerrit-Branch: master Gerrit-Owner: Jeroen De Dauw <[email protected]> _______________________________________________ MediaWiki-commits mailing list [email protected] https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits
