jenkins-bot has submitted this change and it was merged.
Change subject: (Bug 51577) SMW\PropertyDisparityDetector (lift some scope
creep in ParserData)
......................................................................
(Bug 51577) SMW\PropertyDisparityDetector (lift some scope creep in ParserData)
This change helps to eliminate some scope creep in ParserData
which does too may things at once (has currently a CRAP of 2140).
SMW\PropertyDisparityDetector
Comparison is done synchronously but job generation has been moved
into a backgound dispatcher to shield against timeouts when
getAllPropertySubjects() is executed.
Code coverage: 100%
CRAP: 15
SMW\PropertySubjectsUpdateDispatcherJob
Generates UpdateJobs in order to rebuild property-subject parity
Code coverage: 100%
CRAP: 15
Change-Id: I81a4b206e1548e48475c6318ec4728b3cd8cb924
---
M includes/ParserData.php
A includes/PropertyDisparityDetector.php
M includes/Setup.php
A includes/jobs/PropertySubjectsUpdateDispatcherJob.php
A tests/phpunit/includes/PropertyDisparityDetectorTest.php
A tests/phpunit/includes/jobs/PropertySubjectsUpdateDispatcherJobTest.php
6 files changed, 758 insertions(+), 211 deletions(-)
Approvals:
Mwjames: Looks good to me, approved
jenkins-bot: Verified
diff --git a/includes/ParserData.php b/includes/ParserData.php
index a40baf3..be8276c 100644
--- a/includes/ParserData.php
+++ b/includes/ParserData.php
@@ -16,7 +16,6 @@
use SMWDIBlob;
use SMWDIBoolean;
use SMWDITime;
-use SMWUpdateJob;
/**
* Interface handling semantic data storage to a ParserOutput instance
@@ -509,18 +508,24 @@
public function updateStore() {
Profiler::In( __METHOD__, true );
+ // Protect against namespace -1 see Bug 50153
+ if ( $this->title->isSpecialPage() ) {
+ return true;
+ }
+
+ $dispatcherJob = null;
+
+ $namespace = $this->title->getNamespace();
+ $wikiPage = WikiPage::factory( $this->title );
+ $revision = $wikiPage->getRevision();
+ $store = StoreFactory::getStore();
+
// FIXME get rid of globals and use options array instead while
// invoking the constructor
$this->options = array(
'smwgDeclarationProperties' =>
$GLOBALS['smwgDeclarationProperties'],
'smwgPageSpecialProperties' =>
$GLOBALS['smwgPageSpecialProperties']
);
-
- $namespace = $this->title->getNamespace();
- $wikiPage = WikiPage::factory( $this->title );
- $revision = $wikiPage->getRevision();
- $store = smwfGetStore();
- $jobs = array();
// Make sure to have a valid revision (null means delete etc.)
// Check if semantic data should be processed and displayed for
a page in
@@ -535,12 +540,11 @@
$this->semanticData = new SMWSemanticData(
$this->getSubject() );
}
- // Careful: storage access must happen *before* the storage
update;
+ // Comparison must happen *before* the storage update;
// even finding uses of a property fails after its type was
changed.
- if ( $this->updateJobs && ( $namespace === SMW_NS_PROPERTY ) ) {
- $this->getDiffPropertyTypes( $store, $jobs );
- } else if ( $this->updateJobs && ( $namespace === SMW_NS_TYPE )
) {
- $this->getDiffConversionFactors( $store, $jobs );
+ if ( $this->updateJobs ) {
+ $disparityDetector = new PropertyDisparityDetector(
$store, $this->semanticData, Settings::newFromGlobals() );
+ $dispatcherJob =
$disparityDetector->detectDisparity()->getDispatcherJob();
}
// Actually store semantic data, or at least clear it if needed
@@ -552,8 +556,8 @@
// Job::batchInsert was deprecated in MW 1.21
// @see JobQueueGroup::singleton()->push( $job );
- if ( $jobs !== array() ) {
- Job::batchInsert( $jobs );
+ if ( $dispatcherJob !== null ) {
+ Job::batchInsert( $dispatcherJob );
}
Profiler::Out( __METHOD__, true );
@@ -561,199 +565,4 @@
return true;
}
- /**
- * Helper method to handle diff/change in type property pages
- *
- * @note If it is a property, then we need to check if the type or the
- * allowed values have been changed.
- *
- * @since 1.9
- *
- * @param SMWStore $store
- * @param array &$jobs
- */
- protected function getDiffPropertyTypes( SMWStore $store, array &$jobs
) {
- Profiler::In( __METHOD__, true );
-
- $updatejobflag = false;
- $ptype = new SMWDIProperty( SMWDIProperty::TYPE_HAS_TYPE );
-
- // Get values from the store
- $oldtype = $store->getPropertyValues(
- $this->semanticData->getSubject(),
- $ptype
- );
-
- // Get values currently hold by the semantic container
- $newtype = $this->semanticData->getPropertyValues( $ptype );
-
- // Compare old and new type
- if ( !$this->equalDatavalues( $oldtype, $newtype ) ) {
- $updatejobflag = true;
- } else {
- // Compare values (in case of _PVAL (allowed values)
for a
- // property change must be processed again)
- foreach ( $this->options['smwgDeclarationProperties']
as $prop ) {
-
- $dataItem = new SMWDIProperty( $prop );
- $oldValues = $store->getPropertyValues(
- $this->semanticData->getSubject(),
- $dataItem
- );
- $newValues =
$this->semanticData->getPropertyValues( $dataItem );
- $updatejobflag = !$this->equalDatavalues(
$oldValues, $newValues );
- }
- }
-
- // Job generation
- if ( $updatejobflag ) {
- $prop = new SMWDIProperty( $this->title->getDBkey() );
-
- // Array of all subjects that have some value for the
given property
- $subjects = $store->getAllPropertySubjects( $prop );
-
- // Add jobs
- $this->addJobs( $subjects, $jobs );
-
- // Hook
- wfRunHooks( 'smwUpdatePropertySubjects', array( &$jobs
) );
-
- // Fetch all those that have an error property attached
and
- // re-run it through the job-queue
- $subjects = $store->getPropertySubjects(
- new SMWDIProperty( SMWDIProperty::TYPE_ERROR ),
- $this->semanticData->getSubject()
- );
-
- // Add jobs
- $this->addJobs( $subjects, $jobs );
- }
-
- Profiler::Out( __METHOD__, true );
- }
-
- /**
- * Helper method to handle diff/change of conversion related properties
- *
- * @note if it is a type we need to check if the conversion factors
- * have been changed
- *
- * @since 1.9
- *
- * @param SMWStore $store
- * @param array &$jo
- */
- protected function getDiffConversionFactors( SMWStore $store, array
&$jobs ) {
- wfProfileIn( __METHOD__ );
-
- $updatejobflag = false;
- $pconv = new SMWDIProperty( SMWDIProperty::TYPE_CONVERSION );
- $ptype = new SMWDIProperty( SMWDIProperty::TYPE_HAS_TYPE );
-
- $oldfactors = smwfGetStore()->getPropertyValues(
- $this->semanticData->getSubject(),
- $pconv
- );
- $newfactors = $this->semanticData->getPropertyValues( $pconv );
-
- // Compare
- $updatejobflag = !$this->equalDatavalues( $oldfactors,
$newfactors );
-
- // Job generation
- if ( $updatejobflag ) {
-
- /// FIXME: this will kill large wikis! Use incremental
updates!
- $dataValue = SMWDataValueFactory::newTypeIdValue(
'__typ', $title->getDBkey() );
- $propertyPages = $store->getPropertySubjects( $ptype,
$dataValue );
-
- foreach ( $propertyPages as $propertyPage ) {
- // Add jobs
- $this->addJobs( array( $propertyPage ), $jobs );
-
- $prop = new SMWDIProperty(
$propertyPage->getDBkey() );
- $subjects = $store->getAllPropertySubjects(
$prop );
-
- // Add jobs
- $this->addJobs( $subjects, $jobs );
-
- $subjects = $store->getPropertySubjects(
- new SMWDIProperty(
SMWDIProperty::TYPE_ERROR ),
- $prop->getWikiPageValue()
- );
-
- // Add jobs
- $this->addJobs( $subjects, $jobs );
- }
- }
-
- wfProfileOut( __METHOD__ );
- }
-
- /**
- * Helper method to iterate over an array of SMWDIWikiPage and return
and
- * array of SMWUpdateJob jobs
- *
- * Check whether a job with the same getPrefixedDBkey string (prefixed
title,
- * with underscores and any interwiki and namespace prefixes) is already
- * registered and if so don't insert a new job. This is particular
important
- * for pages that include a large amount of subobjects where the same
Title
- * and ParserOutput object is used (subobjects are included using the
same
- * WikiPage which means the resulting ParserOutput object is the same)
- *
- * @since 1.9
- *
- * @param SMWDIWikiPage[] $subjects
- * @param array &$jobs
- */
- protected function addJobs( array $subjects, &$jobs ) {
-
- foreach ( $subjects as $subject ) {
- $duplicate = false;
- $subjectTitle = $subject->getTitle();
-
- if ( $subjectTitle instanceof Title ) {
-
- // Avoid duplicate jobs for the same title
object
- foreach ( $jobs as $job ) {
- if (
$job->getTitle()->getPrefixedDBkey() === $subjectTitle->getPrefixedDBkey() ){
- $duplicate = true;
- break;
- }
- }
- if ( !$duplicate ) {
- $jobs[] = new SMWUpdateJob(
$subjectTitle );
- }
- }
- }
- }
-
- /**
- * Helper function that compares two arrays of data values to check
whether
- * they contain the same content. Returns true if the two arrays
contain the
- * same data values (irrespective of their order), false otherwise.
- *
- * @since 1.9
- */
- protected function equalDatavalues( $oldDataValue, $newDataValue ) {
- // The hashes of all values of both arrays are taken, then
sorted
- // and finally concatenated, thus creating one long hash out of
each
- // of the data value arrays. These are compared.
- $values = array();
- foreach ( $oldDataValue as $v ) {
- $values[] = $v->getHash();
- }
-
- sort( $values );
- $oldDataValueHash = implode( '___', $values );
-
- $values = array();
- foreach ( $newDataValue as $v ) {
- $values[] = $v->getHash();
- }
-
- sort( $values );
- $newDataValueHash = implode( '___', $values );
-
- return ( $oldDataValueHash == $newDataValueHash );
- }
}
diff --git a/includes/PropertyDisparityDetector.php
b/includes/PropertyDisparityDetector.php
new file mode 100644
index 0000000..bed5dc9
--- /dev/null
+++ b/includes/PropertyDisparityDetector.php
@@ -0,0 +1,223 @@
+<?php
+
+namespace SMW;
+
+use Title;
+use Job;
+
+/**
+ * Class that detects a disparity between the property object and its store
data
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @file
+ *
+ * @license GNU GPL v2+
+ * @since 1.9
+ *
+ * @author mwjames
+ * @author Markus Krötzsch
+ */
+
+/**
+ * Class that detects a disparity between the property object and its store
data
+ *
+ * @ingroup SMW
+ */
+class PropertyDisparityDetector {
+
+ /** @var Store */
+ protected $store;
+
+ /** @var SemanticData */
+ protected $semanticData;
+
+ /** @var Settings */
+ protected $settings;
+
+ /** @var Job */
+ protected $dispatcherJob = null;
+
+ /**
+ * @since 1.9
+ *
+ * @param Store $store
+ * @param SemanticData $semanticData
+ * @param Settings $settings
+ */
+ public function __construct( Store $store, SemanticData $semanticData,
Settings $settings ) {
+ $this->store = $store;
+ $this->semanticData = $semanticData;
+ $this->settings = $settings;
+ }
+
+ /**
+ * Returns update jobs as a result of the data comparison
+ *
+ * @since 1.9
+ *
+ * @return PropertyDisparityDispatcherJob|null
+ */
+ public function getDispatcherJob() {
+ return $this->dispatcherJob;
+ }
+
+ /**
+ * Returns if a data disparity exists
+ *
+ * @since 1.9
+ *
+ * @return boolean
+ */
+ public function hasDisparity() {
+ return $this->getDispatcherJob() !== null;
+ }
+
+ /**
+ * Compare and compute the difference between invoked semantic data
+ * and the current store data
+ *
+ * @since 1.9
+ *
+ * @return PropertyDisparityFinder
+ */
+ public function detectDisparity() {
+ Profiler::In( __METHOD__, true );
+
+ if ( $this->semanticData->getSubject()->getNamespace() ===
SMW_NS_PROPERTY ) {
+ $this->comparePropertyTypes();
+ $this->compareConversionFactors();
+ }
+
+ Profiler::Out( __METHOD__, true );
+ return $this;
+ }
+
+ /**
+ * Compare and find change related to the property type
+ *
+ * @since 1.9
+ */
+ protected function comparePropertyTypes() {
+ Profiler::In( __METHOD__, true );
+
+ $update = false;
+ $ptype = new DIProperty( DIProperty::TYPE_HAS_TYPE );
+
+ // Get values from the store
+ $oldtype = $this->store->getPropertyValues(
+ $this->semanticData->getSubject(),
+ $ptype
+ );
+
+ // Get values currently hold by the semantic container
+ $newtype = $this->semanticData->getPropertyValues( $ptype );
+
+ // Compare old and new type
+ if ( !$this->isEqual( $oldtype, $newtype ) ) {
+ $update = true;
+ } else {
+
+ // Compare values (in case of _PVAL (allowed values)
for a
+ // property change must be processed again)
+ foreach ( $this->settings->get(
'smwgDeclarationProperties' ) as $prop ) {
+ $dataItem = new DIProperty( $prop );
+ $oldValues = $this->store->getPropertyValues(
+ $this->semanticData->getSubject(),
+ $dataItem
+ );
+
+ $newValues =
$this->semanticData->getPropertyValues( $dataItem );
+ $update = $update || !$this->isEqual(
$oldValues, $newValues );
+ }
+ }
+
+ $this->addDispatchJob( $update );
+
+ Profiler::Out( __METHOD__, true );
+ }
+
+ /**
+ * Compare and find change related to conversion factor
+ *
+ * @since 1.9
+ */
+ protected function compareConversionFactors() {
+ Profiler::In( __METHOD__, true );
+
+ $pconversion = new DIProperty( DIProperty::TYPE_CONVERSION );
+
+ $oldfactors = $this->store->getPropertyValues(
+ $this->semanticData->getSubject(),
+ $pconversion
+ );
+ $newfactors = $this->semanticData->getPropertyValues(
$pconversion );
+
+ $this->addDispatchJob( !$this->isEqual( $oldfactors,
$newfactors ) );
+
+ Profiler::Out( __METHOD__, true );
+ }
+
+ /**
+ * Adds a Dispatcher job to resolve a disparity asynchronously
+ *
+ * @since 1.9
+ *
+ * @param boolean $addJob
+ */
+ protected function addDispatchJob( $addJob = true ) {
+ if ( $addJob && $this->dispatcherJob === null ) {
+ $this->dispatcherJob[] = new
PropertySubjectsUpdateDispatcherJob(
+ $this->semanticData->getSubject()->getTitle(),
+ array( 'store' => get_class( $this->store ) )
+ );
+ }
+ }
+
+ /**
+ * Helper function that compares two arrays of data values to check
whether
+ * they contain the same content. Returns true if the two arrays
contain the
+ * same data values (irrespective of their order), false otherwise.
+ *
+ * @since 1.9
+ *
+ * @param $oldDataValue
+ * @param $newDataValue
+ */
+ protected function isEqual( $oldDataValue, $newDataValue ) {
+
+ // The hashes of all values of both arrays are taken, then
sorted
+ // and finally concatenated, thus creating one long hash out of
each
+ // of the data value arrays. These are compared.
+ $values = array();
+ foreach ( $oldDataValue as $v ) {
+ $values[] = $v->getHash();
+ }
+
+ sort( $values );
+ $oldDataValueHash = implode( '___', $values );
+
+ $values = array();
+ foreach ( $newDataValue as $v ) {
+ $values[] = $v->getHash();
+ }
+
+ sort( $values );
+ $newDataValueHash = implode( '___', $values );
+
+ return ( $oldDataValueHash == $newDataValueHash );
+ }
+}
diff --git a/includes/Setup.php b/includes/Setup.php
index fe93e10..a14f600 100644
--- a/includes/Setup.php
+++ b/includes/Setup.php
@@ -141,6 +141,7 @@
$wgAutoloadClasses['SMWParseData'] = $incDir .
'SMW_ParseData.php';
$wgAutoloadClasses['SMW\IParserData'] = $incDir .
'ParserData.php';
$wgAutoloadClasses['SMW\ParserData'] = $incDir .
'ParserData.php';
+ $wgAutoloadClasses['SMW\PropertyDisparityDetector'] = $incDir .
'PropertyDisparityDetector.php';
$wgAutoloadClasses['SMW\Subobject'] = $incDir .
'Subobject.php';
$wgAutoloadClasses['SMW\RecurringEvents'] = $incDir .
'RecurringEvents.php';
@@ -173,7 +174,7 @@
$wgAutoloadClasses['SMW\ApiRequestParameterFormatter'] = $incDir .
'formatters/ApiRequestParameterFormatter.php';
// Exceptions
- $wgAutoloadClasses['SMW\InvalidStoreException'] = $incDir .
'/exceptions/InvalidStoreException.php';
+ $wgAutoloadClasses['SMW\InvalidStoreException'] = $incDir .
'/exceptions/InvalidStoreException.php';
$wgAutoloadClasses['SMW\InvalidSemanticDataException'] = $incDir .
'/exceptions/InvalidSemanticDataException.php';
$wgAutoloadClasses['SMW\InvalidNamespaceException'] = $incDir .
'/exceptions/InvalidNamespaceException.php';
$wgAutoloadClasses['SMW\InvalidPropertyException'] = $incDir .
'/exceptions/InvalidPropertyException.php';
@@ -411,10 +412,13 @@
// Jobs
$wgJobClasses['SMWUpdateJob'] = 'SMWUpdateJob';
$wgAutoloadClasses['SMWUpdateJob'] = $smwgIP .
'includes/jobs/SMW_UpdateJob.php';
- $wgAutoloadClasses['SMW\UpdateJob'] = $smwgIP .
'includes/jobs/SMW_UpdateJob.php';
+ $wgAutoloadClasses['SMW\UpdateJob'] = $smwgIP .
'includes/jobs/SMW_UpdateJob.php'; // 1.9
$wgJobClasses['SMWRefreshJob'] = 'SMWRefreshJob';
$wgAutoloadClasses['SMWRefreshJob'] = $smwgIP .
'includes/jobs/SMW_RefreshJob.php';
+ $wgJobClasses['SMW\PropertySubjectsUpdateDispatcherJob'] =
'SMW\PropertySubjectsUpdateDispatcherJob';
+ $wgAutoloadClasses['SMW\PropertySubjectsUpdateDispatcherJob'] = $smwgIP
. 'includes/jobs/PropertySubjectsUpdateDispatcherJob.php';
+
// Store migration job class
$wgJobClasses['SMWMigrationJob'] = 'SMW\MigrationJob';
$wgAutoloadClasses['SMW\MigrationJob'] = $smwgIP .
'includes/jobs/MigrationJob.php';
diff --git a/includes/jobs/PropertySubjectsUpdateDispatcherJob.php
b/includes/jobs/PropertySubjectsUpdateDispatcherJob.php
new file mode 100644
index 0000000..e0d4142
--- /dev/null
+++ b/includes/jobs/PropertySubjectsUpdateDispatcherJob.php
@@ -0,0 +1,191 @@
+<?php
+
+namespace SMW;
+
+use Title;
+use Job;
+
+/**
+ * Background dispatch to generate necessary UpdateJob's in order
+ * to restore the data parity between a property in its attached subjects
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @file
+ *
+ * @license GNU GPL v2+
+ * @since 1.9
+ *
+ * @author mwjames
+ */
+
+/**
+ * Background dispatch to generate necessary UpdateJob's in order
+ * to restore the data parity between a property in its attached subjects
+ *
+ * @ingroup Job
+ * @ingroup Dispatcher
+ */
+class PropertySubjectsUpdateDispatcherJob extends Job {
+
+ /** $var Store */
+ protected $store = null;
+
+ /** $var Job */
+ protected $jobs = array();
+
+ /** $var boolean */
+ protected $enabled = true;
+
+ /**
+ * @since 1.9
+ *
+ * @param Title $title
+ * @param array $params job parameters
+ * @param integer $id job id
+ */
+ public function __construct( Title $title, $params = array(), $id = 0 )
{
+ parent::__construct( 'SMW\PropertySubjectsUpdateDispatcherJob',
$title, $params, $id );
+ $this->store = StoreFactory::getStore( isset( $params['store']
) ? $params['store'] : null );
+ }
+
+ /**
+ * Sets Store object
+ *
+ * @since 1.9
+ *
+ * @param Store $store
+ */
+ public function setStore( Store $store ) {
+ $this->store = $store;
+ }
+
+ /**
+ * Disables ability to insert jobs into the
+ * JobQueue
+ *
+ * @since 1.9
+ *
+ * @return PropertySubjectsUpdateDispatcherJob
+ */
+ public function disable() {
+ $this->enabled = false;
+ return $this;
+ }
+
+ /**
+ * @see Job::run
+ *
+ * @since 1.9
+ *
+ * @return boolean
+ */
+ public function run() {
+ Profiler::In( __METHOD__, true );
+
+ if ( $this->title->getNamespace() === SMW_NS_PROPERTY ) {
+ $this->getSubjects( DIProperty::newFromUserLabel(
$this->title->getText() ) )->push();
+ }
+
+ Profiler::Out( __METHOD__, true );
+ return true;
+ }
+
+ /**
+ * Insert batch jobs
+ *
+ * @note Job::batchInsert was deprecated in MW 1.21
+ * JobQueueGroup::singleton()->push( $job );
+ *
+ * @since 1.9
+ */
+ public function push() {
+ $this->enabled ? Job::batchInsert( $this->jobs ) : null;
+ }
+
+ /**
+ * Generates list of involved subjects
+ *
+ * @since 1.9
+ *
+ * @param DIProperty $property
+ */
+ protected function getSubjects( DIProperty $property ) {
+ Profiler::In( __METHOD__, true );
+
+ // Array of all subjects that have some value for the given
property
+ $subjects = $this->store->getAllPropertySubjects( $property );
+
+ $this->addJobs( $subjects );
+
+ // Hook deprecated with 1.9
+ wfRunHooks( 'smwUpdatePropertySubjects', array( &$this->jobs )
);
+
+ // Hook since 1.9
+ wfRunHooks( 'SMW::Data::UpdatePropertySubjects', array(
&$this->jobs ) );
+
+ // Fetch all those that have an error property attached and
+ // re-run it through the job-queue
+ $subjects = $this->store->getPropertySubjects(
+ new DIProperty( DIProperty::TYPE_ERROR ),
+ DIWikiPage::newFromTitle( $this->title )
+ );
+
+ $this->addJobs( $subjects );
+
+ Profiler::Out( __METHOD__, true );
+ return $this;
+ }
+
+ /**
+ * Helper method to iterate over an array of DIWikiPage and return and
+ * array of UpdateJobs
+ *
+ * Check whether a job with the same getPrefixedDBkey string (prefixed
title,
+ * with underscores and any interwiki and namespace prefixes) is already
+ * registered and if so don't insert a new job. This is particular
important
+ * for pages that include a large amount of subobjects where the same
Title
+ * and ParserOutput object is used (subobjects are included using the
same
+ * WikiPage which means the resulting ParserOutput object is the same)
+ *
+ * @since 1.9
+ *
+ * @param DIWikiPage[] $subjects
+ */
+ protected function addJobs( array $subjects = array() ) {
+
+ foreach ( $subjects as $subject ) {
+
+ $duplicate = false;
+ $title = $subject->getTitle();
+
+ if ( $title instanceof Title ) {
+
+ // Avoid duplicates by comparing the title DBkey
+ foreach ( $this->jobs as $job ) {
+ if (
$job->getTitle()->getPrefixedDBkey() === $title->getPrefixedDBkey() ){
+ $duplicate = true;
+ break;
+ }
+ }
+
+ if ( !$duplicate ) {
+ $this->jobs[] = new UpdateJob( $title );
+ }
+ }
+ }
+ }
+}
diff --git a/tests/phpunit/includes/PropertyDisparityDetectorTest.php
b/tests/phpunit/includes/PropertyDisparityDetectorTest.php
new file mode 100644
index 0000000..5bf0b4e
--- /dev/null
+++ b/tests/phpunit/includes/PropertyDisparityDetectorTest.php
@@ -0,0 +1,162 @@
+<?php
+
+namespace SMW\Test;
+
+use SMW\PropertyDisparityDetector;
+use SMW\DIProperty;
+
+use Title;
+
+/**
+ * Tests for the PropertyDisparityDetector class
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @file
+ *
+ * @license GNU GPL v2+
+ * @since 1.9
+ *
+ * @author mwjames
+ */
+
+/**
+ * @covers \SMW\PropertyDisparityDetector
+ *
+ * @ingroup Test
+ *
+ * @group SMW
+ * @group SMWExtension
+ */
+class PropertyDisparityDetectorTest extends SemanticMediaWikiTestCase {
+
+ /** @var DIWikiPage[] */
+ protected $storeValues;
+
+ /**
+ * Returns the name of the class to be tested
+ *
+ * @return string|false
+ */
+ public function getClass() {
+ return '\SMW\PropertyDisparityDetector';
+ }
+
+ /**
+ * Helper method that returns a PropertyDisparityDetector object
+ *
+ * @since 1.9
+ *
+ * @param $store
+ * @param $data
+ * @param $setting
+ *
+ * @return PropertyDisparityDetector
+ */
+ private function getInstance( $store = array(), $data = array(),
$setting = null ) {
+
+ $mockStore = $this->newMockObject( $store )->getMockStore();
+ $mockData = $this->newMockObject( $data
)->getMockSemanticData();
+ $settings = $this->getSettings( array(
+ 'smwgDeclarationProperties' => $setting === null ?
array( '_PVAL' ): $setting
+ ) );
+
+ return new PropertyDisparityDetector( $mockStore, $mockData,
$settings );
+ }
+
+ /**
+ * @test PropertyDisparityDetector::__construct
+ *
+ * @since 1.9
+ */
+ public function testConstructor() {
+ $this->assertInstanceOf( $this->getClass(),
$this->getInstance() );
+ }
+
+
+ /**
+ * @test PropertyDisparityDetector::detectDisparity
+ * @dataProvider dataItemDataProvider
+ *
+ * @since 1.9
+ */
+ public function testFindDisparity( $storeValues, $dataValues,
$settings, $expected ) {
+
+ $title = $this->getTitle( SMW_NS_PROPERTY );
+ $this->storeValues = $storeValues;
+
+ $store = array(
+ 'getPropertyValues' => array( $this,
'mockStorePropertyValuesCallback' ),
+ );
+
+ $data = array(
+ 'getSubject' => $this->newSubject( $title ),
+ 'getPropertyValues' => $dataValues
+ );
+
+ $instance = $this->getInstance( $store, $data, $settings );
+
+ $this->assertInstanceOf( $this->getClass(),
$instance->detectDisparity() );
+ $this->assertEquals( $expected['disp'],
$instance->hasDisparity() );
+ $this->assertInternalType( $expected['type'],
$instance->getDispatcherJob() );
+ $this->assertEquals( $expected['count'], count(
$instance->getDispatcherJob() ) );
+
+ }
+
+ /**
+ * Provides array of dataItems
+ *
+ * @return array
+ */
+ public function dataItemDataProvider() {
+
+ $subject = array(
+ $this->newSubject()
+ );
+
+ $subjects = array(
+ $this->newSubject(),
+ $this->newSubject(),
+ $this->newSubject()
+ );
+
+ return array(
+ // $storeValues, $dataValues, $settings,
$expected
+ array( $subjects, array(), array( '_PVAL', '_LIST' ),
array( 'disp' => true, 'type' => 'array', 'count' => 1 ) ),
+ array( array(), $subjects, array( '_PVAL', '_LIST' ),
array( 'disp' => true, 'type' => 'array', 'count' => 1 ) ),
+ array( $subject, $subjects, array( '_PVAL', '_LIST' ),
array( 'disp' => true, 'type' => 'array', 'count' => 1 ) ),
+ array( $subject, array(), array( '_PVAL', '_LIST' ),
array( 'disp' => true, 'type' => 'array', 'count' => 1 ) ),
+ array( $subject, array(), array( '_PVAL' ),
array( 'disp' => true, 'type' => 'array', 'count' => 1 ) ),
+ array( $subjects, $subjects, array( '_PVAL' ),
array( 'disp' => false, 'type' => 'null', 'count' => 0 ) ),
+ array( $subject, $subject, array( '_PVAL' ),
array( 'disp' => false, 'type' => 'null', 'count' => 0 ) ),
+ array( $subjects, $subjects, array( '_PVAL', '_LIST' ),
array( 'disp' => true, 'type' => 'array', 'count' => 1 ) ),
+ array( $subject, $subject, array( '_PVAL', '_LIST' ),
array( 'disp' => true, 'type' => 'array', 'count' => 1 ) )
+ );
+ }
+
+ /**
+ * Returns an array of SMWDataItem and simulates an alternating
+ * existencance of return values ('_LIST')
+ *
+ * @see Store::getPropertyValues
+ *
+ * @return SMWDataItem[]
+ */
+ public function mockStorePropertyValuesCallback( $subject, DIProperty
$property, $requestoptions = null ) {
+ return $property->getKey() === '_LIST' ? array() :
$this->storeValues;
+ }
+
+}
diff --git
a/tests/phpunit/includes/jobs/PropertySubjectsUpdateDispatcherJobTest.php
b/tests/phpunit/includes/jobs/PropertySubjectsUpdateDispatcherJobTest.php
new file mode 100644
index 0000000..b2bd20a
--- /dev/null
+++ b/tests/phpunit/includes/jobs/PropertySubjectsUpdateDispatcherJobTest.php
@@ -0,0 +1,158 @@
+<?php
+
+namespace SMW\Test;
+
+use SMW\PropertySubjectsUpdateDispatcherJob;
+use SMW\DIProperty;
+
+use Title;
+
+/**
+ * Tests for the PropertySubjectsUpdateDispatcherJob class
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @file
+ *
+ * @license GNU GPL v2+
+ * @since 1.9
+ *
+ * @author mwjames
+ */
+
+/**
+ * @covers \SMW\PropertySubjectsUpdateDispatcherJob
+ *
+ * @ingroup Test
+ *
+ * @group SMW
+ * @group SMWExtension
+ */
+class PropertySubjectsUpdateDispatcherJobTest extends
SemanticMediaWikiTestCase {
+
+ /** @var DIProperty */
+ protected $property;
+
+ /** @var DIWikiPage[] */
+ protected $subjects;
+
+ /**
+ * Returns the name of the class to be tested
+ *
+ * @return string|false
+ */
+ public function getClass() {
+ return '\SMW\PropertySubjectsUpdateDispatcherJob';
+ }
+
+ /**
+ * Helper method that returns a PropertySubjectsUpdateDispatcherJob
object
+ *
+ * @since 1.9
+ *
+ * @param Title|null $title
+ *
+ * @return PropertySubjectsUpdateDispatcherJob
+ */
+ private function getInstance( Title $title = null ) {
+ return new PropertySubjectsUpdateDispatcherJob( $title === null
? $this->getTitle() : $title );
+ }
+
+ /**
+ * @test PropertySubjectsUpdateDispatcherJob::__construct
+ *
+ * @since 1.9
+ */
+ public function testConstructor() {
+ $this->assertInstanceOf( $this->getClass(),
$this->getInstance() );
+ }
+
+ /**
+ * @test PropertySubjectsUpdateDispatcherJob::push
+ *
+ * Just verify that the push method is accessible
+ * without inserting any real job
+ *
+ * @since 1.9
+ */
+ public function testPush() {
+ $this->assertNull( $this->getInstance()->push() );
+ }
+
+ /**
+ * @test PropertySubjectsUpdateDispatcherJob::run
+ *
+ * @since 1.9
+ */
+ public function testRun() {
+
+ $title = $this->getTitle( SMW_NS_PROPERTY );
+
+ // Set-up expected property, accessible in the mock callback
+ $this->property = DIProperty::newFromUserLabel(
$title->getText() );
+
+ // Set-up expected "raw" subjects to be returned (plus
duplicate)
+ $duplicate = $this->newSubject();
+ $this->subjects = array(
+ $duplicate,
+ $this->newSubject(),
+ $this->newSubject(),
+ $duplicate,
+ $this->newSubject()
+ );
+ $count = count( $this->subjects ) - 1; // eliminate duplicate
count
+
+ $mockStore = $this->newMockObject( array(
+ 'getAllPropertySubjects' => array( $this,
'mockStoreAllPropertySubjectsCallback' ),
+ 'getPropertySubjects' => array()
+ ) )->getMockStore();
+
+ $instance = $this->getInstance( $title );
+ $instance->setStore( $mockStore );
+
+ // Disable dispatch jobs to avoid test
+ // jobs being inserted
+ $instance->disable()->run();
+
+ // Get access to protected jobs property
+ $reflector = $this->newReflector();
+ $jobs = $reflector->getProperty( 'jobs' );
+ $jobs->setAccessible( true );
+
+ $result = $jobs->getValue( $instance );
+
+ $this->assertInternalType( 'array', $result );
+ $this->assertCount( $count, $result );
+
+ foreach ( $result as $job ) {
+ $this->assertInstanceOf( 'SMW\UpdateJob', $job );
+ }
+
+ }
+
+ /**
+ * Returns an array of DIWikiPage objects if the expected property
+ * and the argument property are identical
+ *
+ * @see Store::getAllPropertySubjects
+ *
+ * @return DIWikiPage[]
+ */
+ public function mockStoreAllPropertySubjectsCallback( DIProperty
$property, $requestoptions = null ) {
+ return $this->property == $property ? $this->subjects : array();
+ }
+
+}
--
To view, visit https://gerrit.wikimedia.org/r/73931
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings
Gerrit-MessageType: merged
Gerrit-Change-Id: I81a4b206e1548e48475c6318ec4728b3cd8cb924
Gerrit-PatchSet: 7
Gerrit-Project: mediawiki/extensions/SemanticMediaWiki
Gerrit-Branch: master
Gerrit-Owner: Mwjames <[email protected]>
Gerrit-Reviewer: Mwjames <[email protected]>
Gerrit-Reviewer: jenkins-bot
_______________________________________________
MediaWiki-commits mailing list
[email protected]
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits