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

Reply via email to