jenkins-bot has submitted this change and it was merged. ( 
https://gerrit.wikimedia.org/r/363605 )

Change subject: Implement Format constraint with SPARQL
......................................................................


Implement Format constraint with SPARQL

SparqlHelper gains a method to test if a string matches a regular
expression using SPARQL’s REGEX() function, and FormatChecker is
injected an optional SparqlHelper which it uses to check the constraint
if the SparqlHelper available.

The violation message gains parameters for the property, value and
regular expression.

The query service returns a different result for an invalid query, so we
can detect this case and report it as an invalid constraint parameter.
The SparqlHelper throws a ConstraintParameterException in that case, and
a new message is added for it.

ConstraintParameterRenderer’s formatByRole method is made public and
static, so that SparqlHelper can use it without needing a
ConstraintParameterRenderer injected.

The tests for the format checker are updated to no longer assert a TODO
status; the mock matchesRegularExpression implementation is the old
FormatChecker implementation which was removed in commit b325c00e99
(change I28d2850f89). A test for the behavior without SPARQL (TODO, as
before) is added.

Two tests for SparqlHelper are added, which test that it constructs the
query correctly (including correct input escaping) and that it correctly
detects the case of an invalid regex and throws an exception.

Bug: T102752
Change-Id: I920c6eedbfad9a75b39e01da595294c13c0b8cfc
---
M i18n/en.json
M i18n/qqq.json
M includes/ConstraintCheck/Checker/FormatChecker.php
M includes/ConstraintCheck/Helper/SparqlHelper.php
M includes/ConstraintParameterRenderer.php
M includes/ConstraintReportFactory.php
M tests/phpunit/Checker/FormatChecker/FormatCheckerTest.php
M tests/phpunit/Helper/SparqlHelperTest.php
8 files changed, 214 insertions(+), 47 deletions(-)

Approvals:
  Jonas Kress (WMDE): Looks good to me, approved
  jenkins-bot: Verified



diff --git a/i18n/en.json b/i18n/en.json
index 3ae6ce2..15168f7 100644
--- a/i18n/en.json
+++ b/i18n/en.json
@@ -64,6 +64,7 @@
        "wbqc-violation-message-parameter-string": "The value for the parameter 
\"$1\" must be a string, not \"$2\".",
        "wbqc-violation-message-parameter-single": "The parameter \"$1\" must 
only have a single value.",
        "wbqc-violation-message-parameter-oneof": "The parameter \"$1\" must be 
{{PLURAL:$2|1=$4.|2=either $4 or $5.|one of the following:$3}}",
+       "wbqc-violation-message-parameter-regex": "$1 is not a valid regular 
expression.",
        "wbqc-violation-message-sparql-error": "The SPARQL query resulted in an 
error.",
 
        "wbqc-violation-message-commons-link-no-existent": "Commons link should 
exist.",
@@ -77,7 +78,7 @@
        "wbqc-violation-message-diff-within-range-property-must-exist": "The 
property defined in the parameters must exist.",
        "wbqc-violation-message-diff-within-range-property-needs value": "The 
property defined in the parameters needs to have a value.",
        "wbqc-violation-message-diff-within-range-must-have-equal-types": "The 
property defined in the parameters must have a value of the same type as this 
property.",
-       "wbqc-violation-message-format": "The property's value must match the 
pattern defined in the parameters.",
+       "wbqc-violation-message-format": "The value for $1 ($2) should match 
the pattern $3.",
        "wbqc-violation-message-inverse": "$1 should also have the inverse 
statement $2 $3.",
        "wbqc-violation-message-item": "An entity with $1 should also have 
{{PLURAL:$3|0=a statement $2.|1=a statement $2 $5.|a statement for $2 with one 
of the following values:$4}}",
        "wbqc-violation-message-mandatory-qualifier": "This $1 statement is 
missing a qualifier $2.",
diff --git a/i18n/qqq.json b/i18n/qqq.json
index 51f6970..8c72113 100644
--- a/i18n/qqq.json
+++ b/i18n/qqq.json
@@ -60,6 +60,7 @@
        "wbqc-violation-message-parameter-string": "Message for when the value 
of a constraint parameter must be a string, but is some other value type. $1 
contains the parameter, $2 the value.",
        "wbqc-violation-message-parameter-single": "Message for when a 
constraint parameter has multiple values but only supports one. $1 contains the 
parameter.",
        "wbqc-violation-message-parameter-oneof": "Message for when a 
constraint parameter must be one of several values, but is something different. 
$1 contains the parameter, $2 the number of allowed values (possibly 1), $3 an 
HTML list of all allowed values, and $4, $5 etc. are the individual allowed 
values.{{Related|wbqc-violation-message-one-of}}",
+       "wbqc-violation-message-parameter-regex": "Message for when a 
constraint parameter must be a valid regular expression, but has an error. $1 
contains the parameter value.",
        "wbqc-violation-message-sparql-error": "Message for when a constraint 
checker runs a SPARQL query but the SPARQL endpoint returns an error instead of 
query results.",
        "wbqc-violation-message-commons-link-no-existent": "Message for 
violation of Commons link constraint. When linked commons page does not exist.",
        "wbqc-violation-message-commons-link-not-well-formed": "Message for 
violation of Commons link constraint. When link contains invalid characters.",
@@ -72,7 +73,7 @@
        "wbqc-violation-message-diff-within-range-property-must-exist": 
"Message for violation of Diff within range constraint. When given property 
does not exist (any more).",
        "wbqc-violation-message-diff-within-range-property-needs value": 
"Message for violation of Diff within range constraint. When given property 
does not have a value.",
        "wbqc-violation-message-diff-within-range-must-have-equal-types": 
"Message for violation of Diff within range constraint. When this and the given 
property differ in value type.",
-       "wbqc-violation-message-format": "Message for violation of format 
constraint. When string does not match given pattern.",
+       "wbqc-violation-message-format": "Message for a violation of the 
“Format” constraint, when the value of a statement does not match a certain 
pattern. Parameters:\n* $1 is the property of the statement that has the 
constraint.\n* $2 is the text value of the statement.\n* $3 is the pattern.",
        "wbqc-violation-message-inverse": "Message for a violation of the 
“Inverse” constraint, when the inverse statement of a statement does not exist. 
$1, $2 and $3 contain the expected subject entity, property, and target entity 
of the missing inverse statement.",
        "wbqc-violation-message-item": "Message for a violation of the “Item” 
constraint, when the subject entity of a statement is missing another 
statement. Parameters:\n* $1 is the property of the statement that has the 
constraint.\n* $2 is the property of the missing statement.\n* $3 is the number 
of values permitted for the missing statement (or 0, in which case the 
constraint only specifies that there should be a statement but not the values 
it should have).\n* $4 is an HTML list of all values permitted for the missing 
statement.\n* $5, $6 etc. are the individual values permitted for the missing 
statement.\n{{Related|wbqc-violation-message-target-required-claim}}",
        "wbqc-violation-message-mandatory-qualifier": "Message for a violation 
of the “Mandatory qualifier” constraint, when a mandatory qualifier is missing 
on a statement. Parameters:\n* $1 is the property of the statement.\n* $2 is 
the missing qualifier.",
diff --git a/includes/ConstraintCheck/Checker/FormatChecker.php 
b/includes/ConstraintCheck/Checker/FormatChecker.php
index 7a6d6c2..cabeaa1 100644
--- a/includes/ConstraintCheck/Checker/FormatChecker.php
+++ b/includes/ConstraintCheck/Checker/FormatChecker.php
@@ -2,12 +2,14 @@
 
 namespace WikibaseQuality\ConstraintReport\ConstraintCheck\Checker;
 
+use DataValues\StringValue;
 use Wikibase\DataModel\Entity\EntityDocument;
 use Wikibase\DataModel\Snak\PropertyValueSnak;
 use Wikibase\DataModel\Statement\StatementListProvider;
 use WikibaseQuality\ConstraintReport\Constraint;
 use WikibaseQuality\ConstraintReport\ConstraintCheck\ConstraintChecker;
 use 
WikibaseQuality\ConstraintReport\ConstraintCheck\Helper\ConstraintParameterParser;
+use WikibaseQuality\ConstraintReport\ConstraintCheck\Helper\SparqlHelper;
 use WikibaseQuality\ConstraintReport\ConstraintCheck\Result\CheckResult;
 use WikibaseQuality\ConstraintReport\ConstraintParameterRenderer;
 use Wikibase\DataModel\Statement\Statement;
@@ -30,15 +32,23 @@
        private $constraintParameterRenderer;
 
        /**
+        * @var SparqlHelper
+        */
+       private $sparqlHelper;
+
+       /**
         * @param ConstraintParameterParser $constraintParameterParser
         * @param ConstraintParameterRenderer $constraintParameterRenderer
+        * @param SparqlHelper|null $sparqlHelper
         */
        public function __construct(
                ConstraintParameterParser $constraintParameterParser,
-               ConstraintParameterRenderer $constraintParameterRenderer
+               ConstraintParameterRenderer $constraintParameterRenderer,
+               SparqlHelper $sparqlHelper = null
        ) {
                $this->constraintParameterParser = $constraintParameterParser;
                $this->constraintParameterRenderer = 
$constraintParameterRenderer;
+               $this->sparqlHelper = $sparqlHelper;
        }
 
        /**
@@ -76,22 +86,46 @@
                 * error handling:
                 *   type of $dataValue for properties with 'Format' constraint 
has to be 'string' or 'monolingualtext'
                 */
-               $type = $dataValue->getType();
-               if ( $type !== 'string' && $type !== 'monolingualtext' ) {
-                       $message = wfMessage( 
"wbqc-violation-message-value-needed-of-type" )
-                                        ->rawParams(
-                                                
$this->constraintParameterRenderer->formatItemId( 
$constraint->getConstraintTypeItemId(), 
ConstraintParameterRenderer::ROLE_CONSTRAINT_TYPE_ITEM ),
-                                                wfMessage( 
'datatypes-type-string' )->escaped(),
-                                                wfMessage( 
'datatypes-type-monolingualtext' )->escaped()
-                                        )
-                                        ->escaped();
-                       return new CheckResult( $entity->getId(), $statement, 
$constraint, $parameters, CheckResult::STATUS_VIOLATION, $message );
+               switch ( $dataValue->getType() ) {
+                       case 'string':
+                               $text = $dataValue->getValue();
+                               break;
+                       case 'monolingualtext':
+                               $text = $dataValue->getText();
+                               break;
+                       default:
+                               $message = wfMessage( 
"wbqc-violation-message-value-needed-of-type" )
+                                                ->rawParams(
+                                                        
$this->constraintParameterRenderer->formatItemId( 
$constraint->getConstraintTypeItemId(), 
ConstraintParameterRenderer::ROLE_CONSTRAINT_TYPE_ITEM ),
+                                                        wfMessage( 
'datatypes-type-string' )->escaped(),
+                                                        wfMessage( 
'datatypes-type-monolingualtext' )->escaped()
+                                                )
+                                                ->escaped();
+                               return new CheckResult( $entity->getId(), 
$statement, $constraint, $parameters, CheckResult::STATUS_VIOLATION, $message );
                }
 
-               $message = wfMessage( "wbqc-violation-message-security-reason" )
-                                ->rawParams( 
$this->constraintParameterRenderer->formatItemId( 
$constraint->getConstraintTypeItemId(), 
ConstraintParameterRenderer::ROLE_CONSTRAINT_TYPE_ITEM ) )
-                                ->escaped();
-               return new CheckResult( $entity->getId(), $statement, 
$constraint, $parameters, CheckResult::STATUS_TODO, $message );
+               if ( $this->sparqlHelper !== null ) {
+                       if ( $this->sparqlHelper->matchesRegularExpression( 
$text, $format ) ) {
+                               $message = '';
+                               $status = CheckResult::STATUS_COMPLIANCE;
+                       } else {
+                               $message = wfMessage( 
'wbqc-violation-message-format' )
+                                                ->rawParams(
+                                                       
$this->constraintParameterRenderer->formatEntityId( 
$statement->getPropertyId(), 
ConstraintParameterRenderer::ROLE_CONSTRAINT_PROPERTY ),
+                                                       
$this->constraintParameterRenderer->formatDataValue( new StringValue( $text ), 
ConstraintParameterRenderer::ROLE_OBJECT ),
+                                                       
$this->constraintParameterRenderer->formatByRole( 
ConstraintParameterRenderer::ROLE_CONSTRAINT_PARAMETER_VALUE,
+                                                               
'<code><nowiki>' . htmlspecialchars( $format ) . '</nowiki></code>' )
+                                                )
+                                                ->escaped();
+                               $status = CheckResult::STATUS_VIOLATION;
+                       }
+               } else {
+                       $message = wfMessage( 
"wbqc-violation-message-security-reason" )
+                                        ->rawParams( 
$this->constraintParameterRenderer->formatItemId( 
$constraint->getConstraintTypeItemId(), 
ConstraintParameterRenderer::ROLE_CONSTRAINT_TYPE_ITEM ) )
+                                        ->escaped();
+                       $status = CheckResult::STATUS_TODO;
+               }
+               return new CheckResult( $entity->getId(), $statement, 
$constraint, $parameters, $status, $message );
        }
 
 }
diff --git a/includes/ConstraintCheck/Helper/SparqlHelper.php 
b/includes/ConstraintCheck/Helper/SparqlHelper.php
index 54e63b6..2fe3589 100644
--- a/includes/ConstraintCheck/Helper/SparqlHelper.php
+++ b/includes/ConstraintCheck/Helper/SparqlHelper.php
@@ -9,6 +9,7 @@
 use Wikibase\DataModel\Entity\EntityIdParser;
 use Wikibase\DataModel\Statement\Statement;
 use Wikibase\Rdf\RdfVocabulary;
+use WikibaseQuality\ConstraintReport\ConstraintParameterRenderer;
 
 /**
  * Class for running a SPARQL query on some endpoint and getting the results.
@@ -146,6 +147,47 @@
        }
 
        /**
+        * Return SPARQL code for a string literal with $text as content.
+        * @param string $text
+        * @return string
+        */
+       private function stringLiteral( $text ) {
+               return '"' . strtr( $text, [ '"' => '\\"', '\\' => '\\\\' ] ) . 
'"';
+       }
+
+       /**
+        * @param string $text
+        * @param string $regex
+        * @return boolean
+        * @throws SparqlHelperException if the query times out or some other 
error occurs
+        * @throws ConstraintParameterException if the $regex is invalid
+        */
+       public function matchesRegularExpression( $text, $regex ) {
+               $textStringLiteral = $this->stringLiteral( $text );
+               $regexStringLiteral = $this->stringLiteral( '^' . $regex . '$' 
);
+
+               $query = <<<EOF
+SELECT (REGEX($textStringLiteral, $regexStringLiteral) AS ?matches) {}
+EOF;
+
+               $result = $this->runQuery( $query );
+
+               $vars = $result['results']['bindings'][0];
+               if ( array_key_exists( 'matches', $vars ) ) {
+                       // true or false ⇒ regex okay, text matches or not
+                       return $vars['matches']['value'] === 'true';
+               } else {
+                       // empty result: regex broken
+                       throw new ConstraintParameterException(
+                               wfMessage( 
'wbqc-violation-message-parameter-regex' )
+                                       ->rawParams( 
ConstraintParameterRenderer::formatByRole( 
ConstraintParameterRenderer::ROLE_CONSTRAINT_PARAMETER_VALUE,
+                                               '<code><nowiki>' . 
htmlspecialchars( $regex ) . '</nowiki></code>' ) )
+                                       ->escaped()
+                       );
+               }
+       }
+
+       /**
         * Runs a query against the configured endpoint and returns the results.
         *
         * @param string $query The query, unencoded (plain string).
diff --git a/includes/ConstraintParameterRenderer.php 
b/includes/ConstraintParameterRenderer.php
index 6d3b74a..9a0a795 100644
--- a/includes/ConstraintParameterRenderer.php
+++ b/includes/ConstraintParameterRenderer.php
@@ -181,7 +181,7 @@
         * @param string $value HTML
         * @return string HTML
         */
-       private function formatByRole( $role, $value ) {
+       public static function formatByRole( $role, $value ) {
                if ( $role === null ) {
                        return $value;
                }
@@ -197,7 +197,7 @@
         * @return string HTML
         */
        public function formatDataValue( DataValue $value, $role = null ) {
-               return $this->formatByRole( $role,
+               return self::formatByRole( $role,
                        $this->dataValueFormatter->format( $value ) );
        }
 
@@ -207,7 +207,7 @@
         * @return string HTML
         */
        public function formatEntityId( EntityId $entityId, $role = null ) {
-               return $this->formatByRole( $role,
+               return self::formatByRole( $role,
                        $this->entityIdLabelFormatter->formatEntityId( 
$entityId ) );
        }
 
@@ -227,7 +227,7 @@
                        try {
                                return $this->formatEntityId( new PropertyId( 
$propertyId ), $role );
                        } catch ( InvalidArgumentException $e ) {
-                               return $this->formatByRole( $role,
+                               return self::formatByRole( $role,
                                        htmlspecialchars( $propertyId ) );
                        }
                } else {
@@ -251,7 +251,7 @@
                        try {
                                return $this->formatEntityId( new ItemId( 
$itemId ), $role );
                        } catch ( InvalidArgumentException $e ) {
-                               return $this->formatByRole( $role,
+                               return self::formatByRole( $role,
                                        htmlspecialchars( $itemId ) );
                        }
                } else {
@@ -271,12 +271,12 @@
                        case $value->isValue():
                                return $this->formatEntityId( 
$value->getItemId(), $role );
                        case $value->isSomeValue():
-                               return $this->formatByRole( $role,
+                               return self::formatByRole( $role,
                                        '<span 
class="wikibase-snakview-variation-somevaluesnak">'
                                                . wfMessage( 
'wikibase-snakview-snaktypeselector-somevalue' )->escaped()
                                                . '</span>' );
                        case $value->isNoValue():
-                               return $this->formatByRole( $role,
+                               return self::formatByRole( $role,
                                        '<span 
class="wikibase-snakview-variation-novaluesnak">'
                                                . wfMessage( 
'wikibase-snakview-snaktypeselector-novalue' )->escaped()
                                                . '</span>' );
diff --git a/includes/ConstraintReportFactory.php 
b/includes/ConstraintReportFactory.php
index 96cd99a..e6f101a 100644
--- a/includes/ConstraintReportFactory.php
+++ b/includes/ConstraintReportFactory.php
@@ -222,7 +222,7 @@
                                'Single value' => new SingleValueChecker(),
                                'Multi value' => new MultiValueChecker(),
                                'Unique value' => new UniqueValueChecker( 
$this->constraintParameterRenderer, $sparqlHelper ),
-                               'Format' => new FormatChecker( 
$this->constraintParameterParser, $this->constraintParameterRenderer ),
+                               'Format' => new FormatChecker( 
$this->constraintParameterParser, $this->constraintParameterRenderer, 
$sparqlHelper ),
                                'Commons link' => new CommonsLinkChecker( 
$this->constraintParameterParser, $this->constraintParameterRenderer, 
$this->titleParser ),
                                'One of' => new OneOfChecker( 
$this->constraintParameterParser, $this->constraintParameterRenderer ),
                        ];
diff --git a/tests/phpunit/Checker/FormatChecker/FormatCheckerTest.php 
b/tests/phpunit/Checker/FormatChecker/FormatCheckerTest.php
index 4770feb..cf817d6 100644
--- a/tests/phpunit/Checker/FormatChecker/FormatCheckerTest.php
+++ b/tests/phpunit/Checker/FormatChecker/FormatCheckerTest.php
@@ -13,6 +13,7 @@
 use Wikibase\DataModel\Entity\PropertyId;
 use WikibaseQuality\ConstraintReport\Constraint;
 use WikibaseQuality\ConstraintReport\ConstraintCheck\Checker\FormatChecker;
+use WikibaseQuality\ConstraintReport\ConstraintCheck\Helper\SparqlHelper;
 use WikibaseQuality\ConstraintReport\Tests\ConstraintParameters;
 use WikibaseQuality\ConstraintReport\Tests\ResultAssertions;
 
@@ -38,7 +39,21 @@
 
        protected function setUp() {
                parent::setUp();
-               $this->formatChecker = new FormatChecker( 
$this->getConstraintParameterParser(), $this->getConstraintParameterRenderer() 
);
+               $sparqlHelper = $this->getMockBuilder( SparqlHelper::class )
+                                         ->disableOriginalConstructor()
+                                         ->setMethods( [ 
'matchesRegularExpression' ] )
+                                         ->getMock();
+               $sparqlHelper->method( 'matchesRegularExpression' )
+                       ->will( $this->returnCallback(
+                               function( $text, $pattern ) {
+                                       return preg_match( '/^' . str_replace( 
'/', '\/', $pattern ) . '$/', $text );
+                               }
+                       ) );
+               $this->formatChecker = new FormatChecker(
+                       $this->getConstraintParameterParser(),
+                       $this->getConstraintParameterRenderer(),
+                       $sparqlHelper
+               );
        }
 
        public function testFormatConstraintImdb() {
@@ -71,70 +86,70 @@
                        $this->getConstraintMock( $this->formatParameter( 
$pattern ) ),
                        $this->getEntity()
                );
-               $this->assertTodoCompliance( $result );
+               $this->assertCompliance( $result );
 
                $result = $this->formatChecker->checkConstraint(
                        $statement2,
                        $this->getConstraintMock( $this->formatParameter( 
$pattern ) ),
                        $this->getEntity()
                );
-               $this->assertTodoCompliance( $result );
+               $this->assertCompliance( $result );
 
                $result = $this->formatChecker->checkConstraint(
                        $statement3,
                        $this->getConstraintMock( $this->formatParameter( 
$pattern ) ),
                        $this->getEntity()
                );
-               $this->assertTodoCompliance( $result );
+               $this->assertCompliance( $result );
 
                $result = $this->formatChecker->checkConstraint(
                        $statement4,
                        $this->getConstraintMock( $this->formatParameter( 
$pattern ) ),
                        $this->getEntity()
                );
-               $this->assertTodoCompliance( $result );
+               $this->assertCompliance( $result );
 
                $result = $this->formatChecker->checkConstraint(
                        $statement5,
                        $this->getConstraintMock( $this->formatParameter( 
$pattern ) ),
                        $this->getEntity()
                );
-               $this->assertTodoViolation( $result );
+               $this->assertViolation( $result );
 
                $result = $this->formatChecker->checkConstraint(
                        $statement6,
                        $this->getConstraintMock( $this->formatParameter( 
$pattern ) ),
                        $this->getEntity()
                );
-               $this->assertTodoViolation( $result );
+               $this->assertViolation( $result );
 
                $result = $this->formatChecker->checkConstraint(
                        $statement7,
                        $this->getConstraintMock( $this->formatParameter( 
$pattern ) ),
                        $this->getEntity()
                );
-               $this->assertTodoViolation( $result );
+               $this->assertViolation( $result );
 
                $result = $this->formatChecker->checkConstraint(
                        $statement8,
                        $this->getConstraintMock( $this->formatParameter( 
$pattern ) ),
                        $this->getEntity()
                );
-               $this->assertTodoViolation( $result );
+               $this->assertViolation( $result );
 
                $result = $this->formatChecker->checkConstraint(
                        $statement9,
                        $this->getConstraintMock( $this->formatParameter( 
$pattern ) ),
                        $this->getEntity()
                );
-               $this->assertTodoViolation( $result );
+               $this->assertViolation( $result );
 
                $result = $this->formatChecker->checkConstraint(
                        $statement10,
                        $this->getConstraintMock( $this->formatParameter( 
$pattern ) ),
                        $this->getEntity()
                );
-               $this->assertTodoViolation( $result );
+               $this->assertViolation( $result );
        }
 
        public function testFormatConstraintTaxonName() {
@@ -174,70 +189,70 @@
                        $this->getConstraintMock( $this->formatParameter( 
$pattern ) ),
                        $this->getEntity()
                );
-               $this->assertTodoCompliance( $result );
+               $this->assertCompliance( $result );
 
                $result = $this->formatChecker->checkConstraint(
                        $statement2,
                        $this->getConstraintMock( $this->formatParameter( 
$pattern ) ),
                        $this->getEntity()
                );
-               $this->assertTodoCompliance( $result );
+               $this->assertCompliance( $result );
 
                $result = $this->formatChecker->checkConstraint(
                        $statement3,
                        $this->getConstraintMock( $this->formatParameter( 
$pattern ) ),
                        $this->getEntity()
                );
-               $this->assertTodoCompliance( $result );
+               $this->assertCompliance( $result );
 
                $result = $this->formatChecker->checkConstraint(
                        $statement4,
                        $this->getConstraintMock( $this->formatParameter( 
$pattern ) ),
                        $this->getEntity()
                );
-               $this->assertTodoCompliance( $result );
+               $this->assertCompliance( $result );
 
                $result = $this->formatChecker->checkConstraint(
                        $statement5,
                        $this->getConstraintMock( $this->formatParameter( 
$pattern ) ),
                        $this->getEntity()
                );
-               $this->assertTodoViolation( $result );
+               $this->assertViolation( $result );
 
                $result = $this->formatChecker->checkConstraint(
                        $statement6,
                        $this->getConstraintMock( $this->formatParameter( 
$pattern ) ),
                        $this->getEntity()
                );
-               $this->assertTodoViolation( $result );
+               $this->assertViolation( $result );
 
                $result = $this->formatChecker->checkConstraint(
                        $statement7,
                        $this->getConstraintMock( $this->formatParameter( 
$pattern ) ),
                        $this->getEntity()
                );
-               $this->assertTodoViolation( $result );
+               $this->assertViolation( $result );
 
                $result = $this->formatChecker->checkConstraint(
                        $statement8,
                        $this->getConstraintMock( $this->formatParameter( 
$pattern ) ),
                        $this->getEntity()
                );
-               $this->assertTodoViolation( $result );
+               $this->assertViolation( $result );
 
                $result = $this->formatChecker->checkConstraint(
                        $statement9,
                        $this->getConstraintMock( $this->formatParameter( 
$pattern ) ),
                        $this->getEntity()
                );
-               $this->assertTodoViolation( $result );
+               $this->assertViolation( $result );
 
                $result = $this->formatChecker->checkConstraint(
                        $statement10,
                        $this->getConstraintMock( $this->formatParameter( 
$pattern ) ),
                        $this->getEntity()
                );
-               $this->assertTodoViolation( $result );
+               $this->assertViolation( $result );
        }
 
        public function testFormatConstraintNoStringValue() {
@@ -273,6 +288,24 @@
                $this->assertEquals( 'violation', $result->getStatus(), 'check 
should not comply' );
        }
 
+       public function testFormatConstraintWithoutSparql() {
+               $statement = new Statement( new PropertyValueSnak( new 
PropertyId( 'P1' ), new StringValue( '' ) ) );
+               $constraint = $this->getConstraintMock( $this->formatParameter( 
'.' ) );
+               $checker = new FormatChecker(
+                       $this->getConstraintParameterParser(),
+                       $this->getConstraintParameterRenderer(),
+                       null
+               );
+
+               $result = $checker->checkConstraint(
+                       $statement,
+                       $constraint,
+                       $this->getEntity()
+               );
+
+               $this->assertTodoViolation( $result );
+       }
+
        /**
         * @param string[] $parameters
         *
diff --git a/tests/phpunit/Helper/SparqlHelperTest.php 
b/tests/phpunit/Helper/SparqlHelperTest.php
index d604b47..0b87748 100644
--- a/tests/phpunit/Helper/SparqlHelperTest.php
+++ b/tests/phpunit/Helper/SparqlHelperTest.php
@@ -6,11 +6,16 @@
 use Wikibase\DataModel\Entity\ItemId;
 use Wikibase\DataModel\Entity\ItemIdParser;
 use Wikibase\DataModel\Entity\PropertyId;
+use Wikibase\DataModel\Snak\PropertyNoValueSnak;
 use Wikibase\DataModel\Snak\PropertyValueSnak;
 use Wikibase\DataModel\Statement\Statement;
 use Wikibase\Rdf\RdfVocabulary;
+use WikibaseQuality\ConstraintReport\Constraint;
+use 
WikibaseQuality\ConstraintReport\ConstraintCheck\Helper\ConstraintParameterException;
 use WikibaseQuality\ConstraintReport\ConstraintCheck\Helper\SparqlHelper;
+use WikibaseQuality\ConstraintReport\ConstraintCheck\Result\CheckResult;
 use WikibaseQuality\ConstraintReport\Tests\DefaultConfig;
+use WikibaseQuality\ConstraintReport\Tests\ResultAssertions;
 
 /**
  * @covers 
\WikibaseQuality\ConstraintReport\ConstraintCheck\Helper\SparqlHelper
@@ -22,7 +27,7 @@
  */
 class SparqlHelperTest extends \PHPUnit_Framework_TestCase {
 
-       use DefaultConfig;
+       use DefaultConfig, ResultAssertions;
 
        public function testHasType() {
                $sparqlHelper = $this->getMockBuilder( SparqlHelper::class )
@@ -102,4 +107,55 @@
                );
        }
 
+       public function testMatchesRegularExpression() {
+               $text = '"&quot;\'\\\\"<&lt;'; // "&quot;'\\"<&lt;
+               $regex = '\\"\\\\"\\\\\\"'; // \"\\"\\\"
+               $query = 'SELECT (REGEX("\\"&quot;\'\\\\\\\\\\"<&lt;", 
"^\\\\\\"\\\\\\\\\\"\\\\\\\\\\\\\\"$") AS ?matches) {}';
+               $sparqlHelper = $this->getMockBuilder( SparqlHelper::class )
+                                         ->disableOriginalConstructor()
+                                         ->setMethods( [ 'runQuery' ] )
+                                         ->getMock();
+
+               $sparqlHelper->expects( $this->once() )
+                       ->method( 'runQuery' )
+                       ->with( $this->equalTo( $query ) )
+                       ->willReturn( [ 'results' => [ 'bindings' => [ [ 
'matches' => [ 'value' => 'false' ] ] ] ] ] );
+
+               $result = $sparqlHelper->matchesRegularExpression( $text, 
$regex );
+
+               $this->assertFalse( $result );
+       }
+
+       public function testMatchesRegularExpressionBadRegex() {
+               $text = '';
+               $regex = '(.{2,5)?';
+               $query = 'SELECT (REGEX("", "^(.{2,5)?$") AS ?matches) {}';
+               $sparqlHelper = $this->getMockBuilder( SparqlHelper::class )
+                                         ->disableOriginalConstructor()
+                                         ->setMethods( [ 'runQuery' ] )
+                                         ->getMock();
+               $messageKey = 'wbqc-violation-message-parameter-regex';
+
+               $sparqlHelper->expects( $this->once() )
+                       ->method( 'runQuery' )
+                       ->with( $this->equalTo( $query ) )
+                       ->willReturn( [ 'results' => [ 'bindings' => [ [] ] ] ] 
);
+
+               try {
+                       call_user_func_array( [ $sparqlHelper, 
'matchesRegularExpression' ], [ $text, $regex ] );
+                       $this->assertTrue( false,
+                               "matchesRegularExpression should have thrown a 
ConstraintParameterException with message ⧼$messageKey⧽." );
+               } catch ( ConstraintParameterException $exception ) {
+                       $checkResult = new CheckResult(
+                               new ItemId( 'Q1' ),
+                               new Statement( new PropertyNoValueSnak( new 
PropertyId( 'P1' ) ) ),
+                               $this->getMockBuilder( Constraint::class 
)->disableOriginalConstructor()->getMock(),
+                               [],
+                               CheckResult::STATUS_VIOLATION,
+                               $exception->getMessage()
+                       );
+                       $this->assertViolation( $checkResult, $messageKey );
+               }
+       }
+
 }

-- 
To view, visit https://gerrit.wikimedia.org/r/363605
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings

Gerrit-MessageType: merged
Gerrit-Change-Id: I920c6eedbfad9a75b39e01da595294c13c0b8cfc
Gerrit-PatchSet: 4
Gerrit-Project: mediawiki/extensions/WikibaseQualityConstraints
Gerrit-Branch: master
Gerrit-Owner: Lucas Werkmeister (WMDE) <[email protected]>
Gerrit-Reviewer: Daniel Kinzler <[email protected]>
Gerrit-Reviewer: Jonas Kress (WMDE) <[email protected]>
Gerrit-Reviewer: Lucas Werkmeister (WMDE) <[email protected]>
Gerrit-Reviewer: Lydia Pintscher (WMDE) <[email protected]>
Gerrit-Reviewer: Smalyshev <[email protected]>
Gerrit-Reviewer: Thiemo Mättig (WMDE) <[email protected]>
Gerrit-Reviewer: jenkins-bot <>

_______________________________________________
MediaWiki-commits mailing list
[email protected]
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits

Reply via email to