jenkins-bot has submitted this change and it was merged. Change subject: Handle DVs that mismatch a prop's expected type ......................................................................
Handle DVs that mismatch a prop's expected type If a Snak contains a DataValue of a type different from the value type expected by the property's data type, don't fail, but handle gracefully. Bug: 63299 Change-Id: I41b489ff5b5ea474861922a8c50b214e67851319 --- M client/includes/WikibaseClient.php M lib/i18n/en.json M lib/i18n/qqq.json M lib/includes/formatters/PropertyValueSnakFormatter.php M lib/includes/formatters/SnakFormatter.php M lib/includes/formatters/WikibaseSnakFormatterBuilders.php M lib/tests/phpunit/formatters/PropertyValueSnakFormatterTest.php M lib/tests/phpunit/formatters/WikibaseSnakFormatterBuildersTest.php M repo/includes/WikibaseRepo.php 9 files changed, 458 insertions(+), 64 deletions(-) Approvals: Aude: Looks good to me, approved WikidataJenkins: Verified jenkins-bot: Verified diff --git a/client/includes/WikibaseClient.php b/client/includes/WikibaseClient.php index b293e8a..ee994c5 100644 --- a/client/includes/WikibaseClient.php +++ b/client/includes/WikibaseClient.php @@ -481,7 +481,8 @@ $builders = new WikibaseSnakFormatterBuilders( $valueFormatterBuilders, - $this->getPropertyDataTypeLookup() + $this->getPropertyDataTypeLookup(), + $this->getDataTypeFactory() ); $factory = new OutputFormatSnakFormatterFactory( $builders->getSnakFormatterBuildersForFormats() ); diff --git a/lib/i18n/en.json b/lib/i18n/en.json index d12792e..b84b9af 100644 --- a/lib/i18n/en.json +++ b/lib/i18n/en.json @@ -102,5 +102,7 @@ "wikibase-time-precision-BCE-millennium": "$1. millennium BCE", "wikibase-time-precision-BCE-century": "$1. century BCE", "wikibase-time-precision-BCE-10annum": "$1s BCE", - "wikibase-monolingualtext": "<span lang=\"$2\" class=\"wb-monolingualtext-value\">$1</span> <span class=\"wb-monolingualtext-language-name\">($3)</span>" + "wikibase-monolingualtext": "<span lang=\"$2\" class=\"wb-monolingualtext-value\">$1</span> <span class=\"wb-monolingualtext-language-name\">($3)</span>", + "wikibase-snakformatter-valuetype-mismatch": "The value's type \"$1\" does not match property's type \"$2\".", + "wikibase-snakformatter-property-not-found": "Property $1 not found, cannot determine the data type to use." } diff --git a/lib/i18n/qqq.json b/lib/i18n/qqq.json index a79fc27..d6b196a 100644 --- a/lib/i18n/qqq.json +++ b/lib/i18n/qqq.json @@ -110,5 +110,7 @@ "wikibase-time-precision-BCE-millennium": "!!DO NOT TRANSLATE!! Used to present a point in time BCE (before current era) with the precession of 1000 years\n{{Related|Wikibase-time-precision}}", "wikibase-time-precision-BCE-century": "!!DO NOT TRANSLATE!! Used to present a point in time BCE (before current era) with the precession of 100 years\n{{Related|Wikibase-time-precision}}", "wikibase-time-precision-BCE-10annum": "!!DO NOT TRANSLATE!! Used to present a point in time BCE (before current era) with the precession of 10 years\n{{Related|Wikibase-time-precision}}", - "wikibase-monolingualtext": "Format for displaying monolingual text (along with a language name).\n\nParameters:\n* $1 - the text\n* $2 - the code of the language of the text\n* $3 - the name of the language of the text, in the user's language." + "wikibase-monolingualtext": "Format for displaying monolingual text (along with a language name).\n\nParameters:\n* $1 - the text\n* $2 - the code of the language of the text\n* $3 - the name of the language of the text, in the user's language.", + "wikibase-snakformatter-valuetype-mismatch": "Warning shown when if the data value type used by a Snak's (see [[d:Wikidata:Glossary]]) property's data type is different from the type of the Snak's actual value.\n\nParameters:\n* $1 - data value type of the property-value Snak's value.\n* $2 - data value type used by the data type used in the property which is used by the property-value Snak", + "wikibase-snakformatter-property-not-found": "Warning shown when a Snak's (see [[d:Wikidata:Glossary]]) data type could not be determined based on the Snak's property ID." } diff --git a/lib/includes/formatters/PropertyValueSnakFormatter.php b/lib/includes/formatters/PropertyValueSnakFormatter.php index 53089d0..913116e 100644 --- a/lib/includes/formatters/PropertyValueSnakFormatter.php +++ b/lib/includes/formatters/PropertyValueSnakFormatter.php @@ -1,7 +1,15 @@ <?php namespace Wikibase\Lib; + +use DataTypes\DataTypeFactory; use DataValues\DataValue; +use DataValues\UnDeserializableValue; +use Html; use InvalidArgumentException; +use Message; +use ValueFormatters\Exceptions\MismatchingDataValueTypeException; +use ValueFormatters\FormatterOptions; +use ValueFormatters\FormattingException; use Wikibase\PropertyValueSnak; use Wikibase\Snak; @@ -15,9 +23,37 @@ class PropertyValueSnakFormatter implements SnakFormatter, TypedValueFormatter { /** + * Options key for controlling error handling. + */ + const OPT_ON_ERROR = 'on-error'; + + /** + * Value for the OPT_ON_ERROR option indicating that recoverable + * errors should be ignored. + */ + const ON_ERROR_IGNORE = 'ignore'; + + /** + * Value for the OPT_ON_ERROR option indicating that recoverable + * errors should cause a warning to be show to the user. + */ + const ON_ERROR_WARN = 'warn'; + + /** + * Value for the OPT_ON_ERROR option indicating that recoverable + * errors should cause the formatting to fail with an exception + */ + const ON_ERROR_FAIL = 'fail'; + + /** * @var string */ private $format; + + /** + * @var FormatterOptions + */ + private $options; /** * @var DispatchingValueFormatter @@ -30,21 +66,56 @@ private $typeLookup; /** + * @var DataTypeFactory + */ + private $dataTypeFactory; + + /** * @param string $format The name of this formatter's output format. - * Use the FORMAT_XXX constants defined in OutputFormatSnakFormatterFactory. + * Use the FORMAT_XXX constants defined in SnakFormatter. + * @param FormatterOptions $options * @param DispatchingValueFormatter $valueFormatter * @param PropertyDataTypeLookup $typeLookup + * @param DataTypeFactory $dataTypeFactory * * @throws \InvalidArgumentException */ - public function __construct( $format, DispatchingValueFormatter $valueFormatter, PropertyDataTypeLookup $typeLookup) { + public function __construct( + $format, + FormatterOptions $options, + DispatchingValueFormatter $valueFormatter, + PropertyDataTypeLookup $typeLookup, + DataTypeFactory $dataTypeFactory + ) { if ( !is_string( $format ) ) { throw new InvalidArgumentException( '$format must be a string' ); } + $options->defaultOption( + self::OPT_LANG, + 'en' + ); + + $options->defaultOption( + self::OPT_ON_ERROR, + self::ON_ERROR_WARN + ); + $this->format = $format; + $this->options = $options; $this->valueFormatter = $valueFormatter; $this->typeLookup = $typeLookup; + $this->dataTypeFactory = $dataTypeFactory; + } + + private function failOnErrors() { + return $this->options->getOption( self::OPT_ON_ERROR ) + === self::ON_ERROR_FAIL; + } + + private function ignoreErrors() { + return $this->options->getOption( self::OPT_ON_ERROR ) + === self::ON_ERROR_IGNORE; } /** @@ -53,7 +124,10 @@ * * @param Snak $snak * - * @throws \InvalidArgumentException + * @throws PropertyNotFoundException + * @throws InvalidArgumentException + * @throws MismatchingDataValueTypeException + * @throws FormattingException * @return string */ public function formatSnak( Snak $snak ) { @@ -61,17 +135,132 @@ throw new InvalidArgumentException( "Not a PropertyValueSnak: " . get_class( $snak ) ); } + $value = $snak->getDataValue(); + + list( $propertyType, $warning, $value ) = $this->getEffectivePropertyDataType( $snak, $value ); + + // Format the actual value, unless getEffectivePropertyDataType force the value to be null. + if ( $value ) { + $text = $this->formatValue( $value, $propertyType ); + } else { + $text = ''; + } + + if ( $warning && !$this->ignoreErrors() ) { + if ( $value ) { + $text .= ' '; + } + + $text .= $this->formatWarning( $warning ); + } + + return $text; + } + + /** + * Determines the effective data type. The effective data type will be null + * if the property could not be found, or the value's actual type mismatches + * the data values type. Any warning is included in the return value. + * This method may also override the value object to actually format - + * in particular, it may set $value to null, to suppress rendering. + * + * @param PropertyValueSnak $snak + * @param DataValue $value + * + * @throws PropertyNotFoundException + * @throws MismatchingDataValueTypeException + * @return array list( $propertyType, $warning, $value ) + */ + private function getEffectivePropertyDataType( PropertyValueSnak $snak, DataValue $value ) { + $warning = null; + $expectedDataValueType = null; + + // Find out the expected type for the value try { /* @var PropertyValueSnak $snak */ $propertyType = $this->typeLookup->getDataTypeIdForProperty( $snak->getPropertyId() ); + $expectedDataValueType = $this->getDataValueTypeForPropertyDataType( $propertyType ); } catch ( PropertyNotFoundException $ex ) { - // If the property has been removed, we should still be able to render the snak value, so don't fail here. - wfDebugLog( __CLASS__, __FUNCTION__ . ': Can\'t look up data type for property ' . $snak->getPropertyId()->getPrefixedId() ); + if ( $this->failOnErrors() ) { + throw $ex; + } + + $warning = wfMessage( 'wikibase-snakformatter-property-not-found', + $snak->getPropertyId()->getSerialization() ); + $propertyType = null; } - $text = $this->formatValue( $snak->getDataValue(), $propertyType ); + // Check that the value actually has the expected type. + if ( $expectedDataValueType !== null && $expectedDataValueType !== $value->getType() ) { + if ( $this->failOnErrors() ) { + throw new MismatchingDataValueTypeException( $expectedDataValueType, $value->getType() ); + } + + if ( $value->getType() === UnDeserializableValue::getType() ) { + // Special case: mismatch just because the value could not be unserialized. + // Don't try to actually render the UnDeserializable value. + // This bypasses UnDeserializableValueFormatter. + $value = null; + + $warning = new Message( 'wikibase-undeserializable-value' ); + } else { + if ( $this->failOnErrors() ) { + throw new MismatchingDataValueTypeException( $expectedDataValueType, $value->getType() ); + } + + $warning = new Message( 'wikibase-snakformatter-valuetype-mismatch' ); + $warning->params( $value->getType(), $expectedDataValueType ); + } + + // Don't use property data type based formatting, since our value + // has a type not compatible to that data type. + $propertyType = null; + } + + return array( $propertyType, $warning, $value ); + } + + /** + * @param Message $warning + * + * @return string + */ + private function formatWarning( Message $warning ) { + $attributes = array( 'class' => 'error wb-format-error' ); + + $lang = $this->options->getOption( self::OPT_LANG ); + $warning = $warning->inLanguage( $lang ); + + //NOTE: format identifiers are MIME types, so we can just check the prefix. + if ( strpos( $this->format, SnakFormatter::FORMAT_HTML ) === 0 ) { + $text = $warning->parse(); + $text = Html::rawElement( 'span', $attributes, $text ); + + } elseif ( $this->format === SnakFormatter::FORMAT_WIKI ) { + $text = $warning->text(); + $text = Html::rawElement( 'span', $attributes, $text ); + + } elseif ( $this->format === SnakFormatter::FORMAT_PLAIN ) { + $text = '(' . $warning->text() . ')'; + + } else { + $text = ''; + } + return $text; + } + + /** + * Returns the expected value type for the given property data type + * + * @param string $dataTypeId A property data type id + * + * @return string A value type + */ + private function getDataValueTypeForPropertyDataType( $dataTypeId ) { + $dataType = $this->dataTypeFactory->getType( $dataTypeId ); + return $dataType->getDataValueType(); } /** @@ -112,4 +301,5 @@ public function canFormatSnak( Snak $snak ) { return $snak->getType() === 'value'; } + } diff --git a/lib/includes/formatters/SnakFormatter.php b/lib/includes/formatters/SnakFormatter.php index 9b9ce54..6fd2b89 100644 --- a/lib/includes/formatters/SnakFormatter.php +++ b/lib/includes/formatters/SnakFormatter.php @@ -1,5 +1,6 @@ <?php namespace Wikibase\Lib; +use ValueFormatters\ValueFormatter; use Wikibase\Snak; /** @@ -12,6 +13,11 @@ */ interface SnakFormatter { + /** + * Options key for controlling the output language. + */ + const OPT_LANG = ValueFormatter::OPT_LANG; + const FORMAT_PLAIN = 'text/plain'; const FORMAT_WIKI = 'text/x-wiki'; const FORMAT_HTML = 'text/html'; diff --git a/lib/includes/formatters/WikibaseSnakFormatterBuilders.php b/lib/includes/formatters/WikibaseSnakFormatterBuilders.php index caa6d29..4b86c3a 100644 --- a/lib/includes/formatters/WikibaseSnakFormatterBuilders.php +++ b/lib/includes/formatters/WikibaseSnakFormatterBuilders.php @@ -2,6 +2,7 @@ namespace Wikibase\Lib; +use DataTypes\DataTypeFactory; use ValueFormatters\FormatterOptions; use ValueFormatters\ValueFormatter; @@ -20,24 +21,31 @@ /** * @var WikibaseValueFormatterBuilders */ - protected $valueFormatterBuilders; + private $valueFormatterBuilders; /** * @var PropertyDataTypeLookup */ - protected $propertyDataTypeLookup; + private $propertyDataTypeLookup; + + /** + * @var DataTypeFactory + */ + private $dataTypeFactory; /** * @param WikibaseValueFormatterBuilders $valueFormatterBuilders - 'VT:bad' => 'Wikibase\Lib\UnDeserializableValueFormatter' * @param PropertyDataTypeLookup $propertyDataTypeLookup + * @param DataTypeFactory $dataTypeFactory */ public function __construct( WikibaseValueFormatterBuilders $valueFormatterBuilders, - PropertyDataTypeLookup $propertyDataTypeLookup + PropertyDataTypeLookup $propertyDataTypeLookup, + DataTypeFactory $dataTypeFactory ) { $this->valueFormatterBuilders = $valueFormatterBuilders; $this->propertyDataTypeLookup = $propertyDataTypeLookup; + $this->dataTypeFactory = $dataTypeFactory; } /** @@ -76,7 +84,13 @@ $factory = new OutputFormatValueFormatterFactory( $this->valueFormatterBuilders->getValueFormatterBuildersForFormats() ); $valueFormatter = $this->valueFormatterBuilders->buildDispatchingValueFormatter( $factory, $format, $options ); - $valueSnakFormatter = new PropertyValueSnakFormatter( $format, $valueFormatter, $this->propertyDataTypeLookup ); + $valueSnakFormatter = new PropertyValueSnakFormatter( + $format, + $options, + $valueFormatter, + $this->propertyDataTypeLookup, + $this->dataTypeFactory + ); $formatters = array( 'novalue' => $noValueSnakFormatter, diff --git a/lib/tests/phpunit/formatters/PropertyValueSnakFormatterTest.php b/lib/tests/phpunit/formatters/PropertyValueSnakFormatterTest.php index 618b329..e1020da 100644 --- a/lib/tests/phpunit/formatters/PropertyValueSnakFormatterTest.php +++ b/lib/tests/phpunit/formatters/PropertyValueSnakFormatterTest.php @@ -2,13 +2,20 @@ namespace Wikibase\Lib\Test; +use DataTypes\DataType; +use DataTypes\DataTypeFactory; use DataValues\StringValue; +use DataValues\UnDeserializableValue; +use ValueFormatters\FormatterOptions; use Wikibase\DataModel\Entity\PropertyId; +use Wikibase\DataModel\Snak\PropertySomeValueSnak; +use Wikibase\DataModel\Snak\PropertyValueSnak; use Wikibase\Lib\DispatchingValueFormatter; +use Wikibase\Lib\PropertyDataTypeLookup; +use Wikibase\Lib\PropertyNotFoundException; use Wikibase\Lib\PropertyValueSnakFormatter; use Wikibase\Lib\SnakFormatter; -use Wikibase\PropertySomeValueSnak; -use Wikibase\PropertyValueSnak; +use Wikibase\Lib\UnDeserializableValueFormatter; /** * @covers Wikibase\Lib\PropertyValueSnakFormatter @@ -32,12 +39,7 @@ public function testConstructorErrors( $format, $error ) { $this->setExpectedException( $error ); - $typeLookup = $this->getMock( 'Wikibase\Lib\PropertyDataTypeLookup' ); - $typeLookup->expects( $this->never() )->method( 'getDataTypeIdForProperty' ); - - $valueFormatter = new DispatchingValueFormatter( array() ); - - new PropertyValueSnakFormatter( $format, $valueFormatter, $typeLookup ); + $this->getDummyPropertyValueSnakFormatter( $format ); } public function constructorErrorsProvider() { @@ -50,65 +52,237 @@ } /** - * @dataProvider formatSnakProvider - * @covers PropertyValueSnakFormatter::formatSnak() + * @param string $dataType + * + * @return PropertyDataTypeLookup */ - public function testFormatSnak( $snak, $type, $formatters, $expected ) { + private function getMockDataTypeLookup( $dataType ) { + if ( $dataType !== '' ) { + $getDataTypeIdForPropertyResult = $this->returnValue( $dataType ); + } else { + $getDataTypeIdForPropertyResult = $this->throwException( + new PropertyNotFoundException( new PropertyId( 'P666' ) ) ); + } + $typeLookup = $this->getMock( 'Wikibase\Lib\PropertyDataTypeLookup' ); $typeLookup->expects( $this->atLeastOnce() ) ->method( 'getDataTypeIdForProperty' ) - ->will( $this->returnValue( $type ) ); + ->will( $getDataTypeIdForPropertyResult ); + + return $typeLookup; + } + + /** + * @param string $dataType + * @param string $valueType + * + * @return DataTypeFactory + */ + private function getMockDataTypeFactory( $dataType, $valueType ) { + $typeFactory = $this->getMock( 'DataTypes\DataTypeFactory' ); + $typeFactory->expects( $this->any() ) + ->method( 'getType' ) + ->will( $this->returnValue( new DataType( $dataType, $valueType, array() ) ) ); + + return $typeFactory; + } + + /** + * @dataProvider formatSnakProvider + * @covers PropertyValueSnakFormatter::formatSnak() + */ + public function testFormatSnak( + $snak, $dataType, $valueType, $targetFormat, $formatters, $onError, + $expected, $expectedException = null + ) { + if ( $expectedException !== null ) { + $this->setExpectedException( $expectedException ); + } + + $typeLookup = $this->getMockDataTypeLookup( $dataType ); + $typeFactory = $this->getMockDataTypeFactory( $dataType, $valueType ); + + $options = new FormatterOptions( array( + PropertyValueSnakFormatter::OPT_LANG => 'en', + PropertyValueSnakFormatter::OPT_ON_ERROR => $onError, + ) ); $formatter = new PropertyValueSnakFormatter( - SnakFormatter::FORMAT_PLAIN, + $targetFormat, + $options, new DispatchingValueFormatter( $formatters ), - $typeLookup + $typeLookup, + $typeFactory ); - $this->assertEquals( $expected, $formatter->formatSnak( $snak ) ); + $actual = $formatter->formatSnak( $snak ); + + $this->assertRegExp( $expected, $actual ); + } + + private function getMockFormatter( $value ) { + $formatter = $this->getMock( 'ValueFormatters\ValueFormatter' ); + $formatter->expects( $this->any() ) + ->method( 'format' ) + ->will( $this->returnValue( $value ) ); + + return $formatter; } public function formatSnakProvider() { - $stringFormatter = $this->getMock( 'ValueFormatters\ValueFormatter' ); - $stringFormatter->expects( $this->any() ) - ->method( 'format' ) - ->will( $this->returnValue( 'VT:string' ) ); - - $mediaFormatter = $this->getMock( 'ValueFormatters\ValueFormatter' ); - $mediaFormatter->expects( $this->any() ) - ->method( 'format' ) - ->will( $this->returnValue( 'PT:commonsMedia' ) ); - $formatters = array( - 'VT:string' => $stringFormatter, - 'PT:commonsMedia' => $mediaFormatter, + 'VT:bad' => new UnDeserializableValueFormatter( new FormatterOptions() ), + 'VT:string' => $this->getMockFormatter( 'VT:string' ), + 'PT:commonsMedia' => $this->getMockFormatter( 'PT:commonsMedia' ) ); return array( 'match PT' => array( new PropertyValueSnak( 17, new StringValue( 'Foo.jpg' ) ), 'commonsMedia', + 'string', + SnakFormatter::FORMAT_PLAIN, $formatters, - 'PT:commonsMedia' + PropertyValueSnakFormatter::ON_ERROR_WARN, + '/^PT:commonsMedia$/' ), 'match VT' => array( new PropertyValueSnak( 33, new StringValue( 'something' ) ), 'someStuff', + 'string', + SnakFormatter::FORMAT_WIKI, $formatters, - 'VT:string' + PropertyValueSnakFormatter::ON_ERROR_WARN, + '/^VT:string$/' + ), + + 'UnDeserializableValue' => array( + new PropertyValueSnak( 7, + new UnDeserializableValue( 'cookie', 'globecoordinate', 'cannot understand!' ) + ), + 'globe-coordinate', + 'globecoordinate', + SnakFormatter::FORMAT_HTML, + $formatters, + PropertyValueSnakFormatter::ON_ERROR_WARN, + // message key: wikibase-undeserializable-value + '/value is invalid/' + ), + + 'VT mismatching PT' => array( + new PropertyValueSnak( 7, new StringValue( 'dummy' ) ), + 'url', + 'iri', // url expects an iri, but will get a string + SnakFormatter::FORMAT_WIKI, + $formatters, + PropertyValueSnakFormatter::ON_ERROR_WARN, + // message key: wikibase-snakformatter-valuetype-mismatch + '@^VT:string <span class="error wb-format-error">.*does not match.*</span>$@' + ), + + 'property not found' => array( + new PropertyValueSnak( 7, new StringValue( 'dummy' ) ), + '', // triggers an exception from the mock PropertyDataTypeLookup + 'xxx', // should not be used + SnakFormatter::FORMAT_HTML, + $formatters, + PropertyValueSnakFormatter::ON_ERROR_WARN, + // message key: wikibase-snakformatter-property-not-found + '@^VT:string <span class="error wb-format-error">.*not found.*</span>$@' + ), + + + 'UnDeserializableValue, ignored' => array( + new PropertyValueSnak( 7, + new UnDeserializableValue( 'cookie', 'globecoordinate', 'cannot understand!' ) + ), + 'globe-coordinate', + 'globecoordinate', + SnakFormatter::FORMAT_HTML, + $formatters, + PropertyValueSnakFormatter::ON_ERROR_IGNORE, + '/^$/' + ), + + 'VT mismatching PT, ignored' => array( + new PropertyValueSnak( 7, new StringValue( 'dummy' ) ), + 'url', + 'iri', // url expects an iri, but will get a string + SnakFormatter::FORMAT_WIKI, + $formatters, + PropertyValueSnakFormatter::ON_ERROR_IGNORE, + '@^VT:string$@' + ), + + 'property not found, ignored' => array( + new PropertyValueSnak( 7, new StringValue( 'dummy' ) ), + '', // triggers an exception from the mock PropertyDataTypeLookup + 'xxx', // should not be used + SnakFormatter::FORMAT_HTML, + $formatters, + PropertyValueSnakFormatter::ON_ERROR_IGNORE, + '@^VT:string$@' + ), + + + 'UnDeserializableValue, fail' => array( + new PropertyValueSnak( 7, + new UnDeserializableValue( 'cookie', 'globecoordinate', 'cannot understand!' ) + ), + 'globe-coordinate', + 'globecoordinate', + SnakFormatter::FORMAT_HTML, + $formatters, + PropertyValueSnakFormatter::ON_ERROR_FAIL, + null, + 'ValueFormatters\Exceptions\MismatchingDataValueTypeException' + ), + + 'VT mismatching PT, fail' => array( + new PropertyValueSnak( 7, new StringValue( 'dummy' ) ), + 'url', + 'iri', // url expects an iri, but will get a string + SnakFormatter::FORMAT_WIKI, + $formatters, + PropertyValueSnakFormatter::ON_ERROR_FAIL, + null, + 'ValueFormatters\Exceptions\MismatchingDataValueTypeException' + ), + + 'property not found, fail' => array( + new PropertyValueSnak( 7, new StringValue( 'dummy' ) ), + '', // triggers an exception from the mock PropertyDataTypeLookup + 'xxx', // should not be used + SnakFormatter::FORMAT_HTML, + $formatters, + PropertyValueSnakFormatter::ON_ERROR_FAIL, + null, + 'Wikibase\Lib\PropertyNotFoundException' ), ); + } + + private function getDummyPropertyValueSnakFormatter( $format = 'test' ) { + $typeLookup = $this->getMock( 'Wikibase\Lib\PropertyDataTypeLookup' ); + $typeLookup->expects( $this->never() )->method( 'getDataTypeIdForProperty' ); + + $typeFactory = $this->getMock( 'DataTypes\DataTypeFactory' ); + $typeFactory->expects( $this->never() )->method( 'getType' ); + + $valueFormatter = new DispatchingValueFormatter( array() ); + + $options = new FormatterOptions( array() ); + + $formatter = new PropertyValueSnakFormatter( $format, $options, $valueFormatter, $typeLookup, $typeFactory ); + return $formatter; } /** * @covers PropertyValueSnakFormatter::getFormat() */ public function testGetFormat() { - $typeLookup = $this->getMock( 'Wikibase\Lib\PropertyDataTypeLookup' ); - $typeLookup->expects( $this->never() )->method( 'getDataTypeIdForProperty' ); - - $formatter = new PropertyValueSnakFormatter( 'test', new DispatchingValueFormatter( array() ), $typeLookup ); + $formatter = $this->getDummyPropertyValueSnakFormatter(); $this->assertEquals( 'test', $formatter->getFormat() ); } @@ -116,10 +290,7 @@ * @covers PropertyValueSnakFormatter::canFormatSnak */ public function testCanFormatSnak() { - $typeLookup = $this->getMock( 'Wikibase\Lib\PropertyDataTypeLookup' ); - $typeLookup->expects( $this->never() )->method( 'getDataTypeIdForProperty' ); - - $formatter = new PropertyValueSnakFormatter( 'test', new DispatchingValueFormatter( array() ), $typeLookup ); + $formatter = $this->getDummyPropertyValueSnakFormatter(); $snak = new PropertyValueSnak( new PropertyId( "P23" ), new StringValue( 'test' ) ); $this->assertTrue( $formatter->canFormatSnak( $snak ), $snak->getType() ); diff --git a/lib/tests/phpunit/formatters/WikibaseSnakFormatterBuildersTest.php b/lib/tests/phpunit/formatters/WikibaseSnakFormatterBuildersTest.php index e8d1c62..6bb022a 100644 --- a/lib/tests/phpunit/formatters/WikibaseSnakFormatterBuildersTest.php +++ b/lib/tests/phpunit/formatters/WikibaseSnakFormatterBuildersTest.php @@ -2,6 +2,7 @@ namespace Wikibase\Lib\Test; +use DataTypes\DataType; use DataValues\StringValue; use DataValues\UnDeserializableValue; use Language; @@ -43,6 +44,20 @@ ->method( 'getDataTypeIdForProperty' ) ->will( $this->returnValue( $propertyType ) ); + $typeMap = array( + 'url' => 'string', + 'string' => 'string', + 'wikibase-item' => 'wikibase-entityid', + 'globecoordinate' => 'globecoordinate', + ); + + $typeFactory = $this->getMock( 'DataTypes\DataTypeFactory' ); + $typeFactory->expects( $this->any() ) + ->method( 'getType' ) + ->will( $this->returnCallback( function ( $id ) use ( $typeMap ) { + return new DataType( $id, $typeMap[$id], array() ); + } ) ); + $entity = EntityFactory::singleton()->newEmpty( $entityId->getEntityType() ); $entity->setId( $entityId ); $entity->setLabel( 'en', 'Label for ' . $entityId->getPrefixedId() ); @@ -55,7 +70,7 @@ $lang = Language::factory( 'en' ); $valueFormatterBuilders = new WikibaseValueFormatterBuilders( $entityLookup, $lang ); - return new WikibaseSnakFormatterBuilders( $valueFormatterBuilders, $typeLookup ); + return new WikibaseSnakFormatterBuilders( $valueFormatterBuilders, $typeLookup, $typeFactory ); } /** @@ -146,15 +161,6 @@ 'url', new PropertyValueSnak( 7, new StringValue( 'http://acme.com/' ) ), '<a rel="nofollow" class="external free" href="http://acme.com/">http://acme.com/</a>' - ), - 'bad value' => array( - SnakFormatter::FORMAT_PLAIN, - $options, - 'globecoordinate', - new PropertyValueSnak( 7, - new UnDeserializableValue( 'cookie', 'globecoordinate', 'cannot understand!' ) - ), - $badValueMsg ) ); } diff --git a/repo/includes/WikibaseRepo.php b/repo/includes/WikibaseRepo.php index c0618d6..953360d 100644 --- a/repo/includes/WikibaseRepo.php +++ b/repo/includes/WikibaseRepo.php @@ -436,7 +436,7 @@ /** * @return WikibaseValueFormatterBuilders */ - protected function getValueFormatterBuilders() { + public function getValueFormatterBuilders() { global $wgContLang; return new WikibaseValueFormatterBuilders( @@ -452,7 +452,8 @@ protected function newSnakFormatterFactory() { $builders = new WikibaseSnakFormatterBuilders( $this->getValueFormatterBuilders(), - $this->getPropertyDataTypeLookup() + $this->getPropertyDataTypeLookup(), + $this->getDataTypeFactory() ); $factory = new OutputFormatSnakFormatterFactory( $builders->getSnakFormatterBuildersForFormats() ); @@ -522,7 +523,8 @@ $snakFormatterBuilders = new WikibaseSnakFormatterBuilders( $valueFormatterBuilders, - $this->getPropertyDataTypeLookup() + $this->getPropertyDataTypeLookup(), + $this->getDataTypeFactory() ); $valueFormatterBuilders->setValueFormatter( -- To view, visit https://gerrit.wikimedia.org/r/133286 To unsubscribe, visit https://gerrit.wikimedia.org/r/settings Gerrit-MessageType: merged Gerrit-Change-Id: I41b489ff5b5ea474861922a8c50b214e67851319 Gerrit-PatchSet: 11 Gerrit-Project: mediawiki/extensions/Wikibase Gerrit-Branch: master Gerrit-Owner: Daniel Kinzler <daniel.kinz...@wikimedia.de> Gerrit-Reviewer: Addshore <addshorew...@gmail.com> Gerrit-Reviewer: Adrian Lang <adrian.l...@wikimedia.de> Gerrit-Reviewer: Aude <aude.w...@gmail.com> Gerrit-Reviewer: Daniel Kinzler <daniel.kinz...@wikimedia.de> Gerrit-Reviewer: Hoo man <h...@online.de> Gerrit-Reviewer: Thiemo Mättig (WMDE) <thiemo.maet...@wikimedia.de> Gerrit-Reviewer: Tobias Gritschacher <tobias.gritschac...@wikimedia.de> Gerrit-Reviewer: WikidataJenkins <wikidata-servi...@wikimedia.de> Gerrit-Reviewer: jenkins-bot <> _______________________________________________ MediaWiki-commits mailing list MediaWiki-commits@lists.wikimedia.org https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits