Foxtrott has submitted this change and it was merged. Change subject: Refactored SemanticGlossaryBackend ......................................................................
Refactored SemanticGlossaryBackend - Encapsulated LingoBackend using \SG\LingoBackendAdapter - Added \SG\Cache\ElementsCacheBuilder Change-Id: Id59164a9cc2c34a777313fb12524ceedf35801e5 --- A README.md M SemanticGlossary.php D SemanticGlossaryBackend.php D SemanticGlossaryCacheHandling.php A src/Cache/ElementsCacheBuilder.php A src/LingoBackendAdapter.php M tests/bootstrap.php A tests/mw-phpunit-runner.php A tests/phpunit/Cache/ElementsCacheBuilderTest.php A tests/phpunit/LingoBackendAdapterTest.php 10 files changed, 596 insertions(+), 380 deletions(-) Approvals: Foxtrott: Verified; Looks good to me, approved diff --git a/README.md b/README.md new file mode 100644 index 0000000..4982fc6 --- /dev/null +++ b/README.md @@ -0,0 +1,38 @@ +# Semantic Glossary +[![Latest Stable Version](https://poser.pugx.org/mediawiki/semantic-glossary/version.png)](https://packagist.org/packages/mediawiki/chameleon-skin) +[![Packagist download count](https://poser.pugx.org/mediawiki/semantic-glossary/d/total.png)](https://packagist.org/packages/mediawiki/chameleon-skin) +[![Dependency Status](https://www.versioneye.com/php/mediawiki:semantic-glossary/badge.png)](https://www.versioneye.com/php/mediawiki:chameleon-skin) + +The [Semantic Glossary][mw-semantic-glossary] (a.k.a SG) is a [Semantic MediaWiki][smw] extension where terms and abbreviations can be defined using semantic properties. + +## Requirements + +- PHP 5.3.2 or later +- MediaWiki 1.20 or later +- [Lingo extension][mw-lingo] 1.0 or later + +## Installation + +The recommended way to install this skin is by using [Composer][composer]. Just add the following to the MediaWiki `composer.json` file and run the `php composer.phar install/update` command. + +```json +{ + "require": { + "mediawiki/semantic-glossary": "~1.0" + } +} +``` + +## Tests + +The extension provides unit tests that covers core-functionality normally run by a continues integration platform. Tests can also be executed manually using the `mw-phpunit-runner.php` script (loads necessary MediaWiki dependencies) or running `phpunit` with the [PHPUnit][mw-testing] configuration file found in the root directory. + +```sh +php mw-phpunit-runner.php +``` + +[mw-semantic-glossary]: https://www.mediawiki.org/wiki/Extension:Semantic_Glossary +[mw-lingo]: https://www.mediawiki.org/wiki/Extension:Lingo +[smw]: https://www.mediawiki.org/wiki/Semantic_MediaWiki +[mw-testing]: https://www.mediawiki.org/wiki/Manual:PHP_unit_testing +[composer]: https://getcomposer.org/ \ No newline at end of file diff --git a/SemanticGlossary.php b/SemanticGlossary.php index 37509b8..68e2252 100644 --- a/SemanticGlossary.php +++ b/SemanticGlossary.php @@ -44,9 +44,8 @@ 'version' => SG_VERSION, ); - // set SemanticGlossaryBackend as the backend to access the glossary - $GLOBALS[ 'wgexLingoBackend' ] = 'SemanticGlossaryBackend'; + $GLOBALS[ 'wgexLingoBackend' ] = 'SG\LingoBackendAdapter'; // server-local path to this file $dir = __DIR__; @@ -57,11 +56,12 @@ // register class files with the Autoloader $autoloadClasses = array( - 'SemanticGlossaryBackend' => $dir . '/SemanticGlossaryBackend.php', 'SG\PropertyRegistry' => $dir . '/src/PropertyRegistry.php', 'SG\CacheInvalidator' => $dir . '/src/CacheInvalidator.php', 'SG\CacheHelper' => $dir . '/src/CacheHelper.php', 'SG\DataComparator' => $dir . '/src/DataComparator.php', + 'SG\LingoBackendAdapter' => $dir . '/src/LingoBackendAdapter.php', + 'SG\Cache\ElementsCacheBuilder' => $dir . '/src/Cache/ElementsCacheBuilder.php' ); $GLOBALS[ 'wgAutoloadClasses' ] = array_merge( $GLOBALS[ 'wgAutoloadClasses' ], $autoloadClasses ); diff --git a/SemanticGlossaryBackend.php b/SemanticGlossaryBackend.php deleted file mode 100644 index ad9797d..0000000 --- a/SemanticGlossaryBackend.php +++ /dev/null @@ -1,195 +0,0 @@ -<?php - -/** - * File holding the SemanticGlossaryBackend class - * - * @author Stephan Gambke - * @file - * @ingroup SemanticGlossary - */ -if ( !defined( 'SG_VERSION' ) ) { - die( 'This file is part of the SemanticGlossary extension, it is not a valid entry point.' ); -} - -/** - * The SemanticGlossaryBackend class. - * - * @ingroup SemanticGlossary - */ -class SemanticGlossaryBackend extends LingoBackend { - - //array of SMWDIWikiPage - protected $mQueryResults; - - protected $mDiTerm; - protected $mDiDefinition; - protected $mDiLink; - protected $mDiStyle; - - protected $mDvTerm; - protected $mDvDefinition; - protected $mDvLink; - protected $mDvStyle; - - protected $mStore; - - public function __construct( LingoMessageLog &$messages = null ) { - - parent::__construct( $messages ); - - // get the store - $this->mStore = smwfGetStore(); - - // build term data item and data value for later use - $this->mDiTerm = new SMWDIProperty( '___glt' ); - $this->mDvTerm = new SMWStringValue( '_str' ); - $this->mDvTerm->setProperty( $this->mDiTerm ); - - $pvTerm = new SMWPropertyValue( '__pro' ); - $pvTerm->setDataItem( $this->mDiTerm ); - $prTerm = new SMWPrintRequest( SMWPrintRequest::PRINT_PROP, null, $pvTerm ); - - // build definition data item and data value for later use - $this->mDiDefinition = new SMWDIProperty( '___gld' ); - $this->mDvDefinition = new SMWStringValue( '_txt' ); - $this->mDvDefinition->setProperty( $this->mDiDefinition ); - - $pvDefinition = new SMWPropertyValue( '__pro' ); - $pvDefinition->setDataItem( $this->mDiDefinition ); - $prDefinition = new SMWPrintRequest( SMWPrintRequest::PRINT_PROP, null, $pvDefinition ); - - // build link data item and data value for later use - $this->mDiLink = new SMWDIProperty( '___gll' ); - $this->mDvLink = new SMWStringValue( '_str' ); - $this->mDvLink->setProperty( $this->mDiLink ); - - $pvLink = new SMWPropertyValue( '__pro' ); - $pvLink->setDataItem( $this->mDiLink ); - $prLink = new SMWPrintRequest( SMWPrintRequest::PRINT_PROP, null, $pvLink ); - - // build style data item and data value for later use - $this->mDiStyle = new SMWDIProperty( '___gls' ); - $this->mDvStyle = new SMWStringValue( '_txt' ); - $this->mDvStyle->setProperty( $this->mDiStyle ); - - $pvStyle = new SMWPropertyValue( '__pro' ); - $pvStyle->setDataItem( $this->mDiStyle ); - $prStyle = new SMWPrintRequest( SMWPrintRequest::PRINT_PROP, null, $pvStyle ); - - // Create query - $desc = new SMWSomeProperty( new SMWDIProperty( '___glt' ), new SMWThingDescription() ); - $desc->addPrintRequest( $prTerm ); - $desc->addPrintRequest( $prDefinition ); - $desc->addPrintRequest( $prLink ); - $desc->addPrintRequest( $prStyle ); - - $query = new SMWQuery( $desc, false, false ); - $query->sort = true; - $query->sortkeys['___glt'] = 'ASC'; - - // get the query result - $this->mQueryResults = $this->mStore->getQueryResult( $query )->getResults(); - } - - /** - * This function returns the next element. The element is an array of four - * strings: Term, Definition, Link, Source, Style. If there is no next element - * the function returns null. - * - * @return the next element or null - */ - public function next() { - - wfProfileIn( __METHOD__ ); - static $ret = array(); - - // find next line - $page = current( $this->mQueryResults ); - - if ( $page && count( $ret ) == 0 ) { - - next( $this->mQueryResults ); - - // Try cache first - $cache = \SG\CacheHelper::getCache(); // Needs to be injected to allow for proper unit testing - $cachekey = \SG\CacheHelper::getKey( $page ); - $cachedResult = $cache->get( $cachekey ); - - // cache hit? - if ( $cachedResult !== false && $cachedResult !== null ) { - - wfDebug( "Cache hit: Got glossary entry $cachekey from cache.\n" ); - $ret = &$cachedResult; - } else { - - wfDebug( "Cache miss: Glossary entry $cachekey not found in cache.\n" ); - - $terms = $this->mStore->getPropertyValues( $page, $this->mDiTerm ); - $definitions = $this->mStore->getPropertyValues( $page, $this->mDiDefinition ); - $links = $this->mStore->getPropertyValues( $page, $this->mDiLink ); - $styles = $this->mStore->getPropertyValues( $page, $this->mDiStyle ); - - if ( empty( $definitions ) ) { - $definition = null; - } else { - $this->mDvDefinition->setDataItem( $definitions[0] ); - $definition = trim( $this->mDvDefinition->getShortWikiText() ); - } - - if ( empty( $links ) ) { - $link = null; - } else { - $this->mDvLink->setDataItem( $links[0] ); - $link = trim( $this->mDvLink->getShortWikiText() ); - } - - if ( empty( $styles ) ) { - $style = null; - } else { - $this->mDvStyle->setDataItem( $styles[0] ); - $style = trim( $this->mDvStyle->getShortWikiText() ); - } - - $tmp_terms = array(); - - if ( !empty( $terms ) ) { - foreach ( $terms as $term ) { - $this->mDvTerm->setDataItem( $term ); - $tmp_terms[] = trim( $this->mDvTerm->getShortWikiText() ); - } - } - - foreach ( $tmp_terms as $tmp_term ) { - $tmp_ret = array( - LingoElement::ELEMENT_TERM => $tmp_term, - LingoElement::ELEMENT_DEFINITION => $definition, - LingoElement::ELEMENT_LINK => $link, - LingoElement::ELEMENT_STYLE => $style, - LingoElement::ELEMENT_SOURCE => $page - ); - - wfDebug( "Cached glossary entry $cachekey.\n" ); - $ret[] = $tmp_ret; - } - - $cache->set( $cachekey, $ret ); - } - } - - wfProfileOut( __METHOD__ ); - return array_pop($ret); - } - - /** - * This backend is cache-enabled so this function returns true. - * - * Actual caching is done by the parser, the backend just calls - * LingoParser::purgeCache when necessary. - * - * @return boolean - */ - public function useCache() { - return true; - } - -} diff --git a/SemanticGlossaryCacheHandling.php b/SemanticGlossaryCacheHandling.php deleted file mode 100644 index daab196..0000000 --- a/SemanticGlossaryCacheHandling.php +++ /dev/null @@ -1,169 +0,0 @@ -<?php - -/** - * File holding the SemanticGlossaryCacheHandling class - * - * @author Stephan Gambke - * @file - * @ingroup SemanticGlossary - */ -if ( !defined( 'SG_VERSION' ) ) { - die( 'This file is part of the SemanticGlossary extension, it is not a valid entry point.' ); -} - -/** - * The SemanticGlossaryCacheHandling class. - * - * @ingroup SemanticGlossary - */ -class SemanticGlossaryCacheHandling { - - /** - * Initiates the purging of the cache when a Glossary property was changed. - * - * @param Page $wikipage - * @return Bool - */ - static function purgeCacheForData( SMWStore $store, SMWSemanticData $data ) { - - wfProfileIn( __METHOD__ ); - - // get properties - $properties = $data->getProperties(); - - $subject = $data->getSubject(); - - // first handle subobjects recursively - if ( array_key_exists( '_SOBJ', $properties ) ) { - foreach ( $data->getPropertyValues( $properties['_SOBJ'] ) as $so ) { - self::purgeCacheForData( $store, $store->getSemanticData($so), false ); - } - } - - // check if terms, definitions or links changed - if ( self::propValuesChanged( $store, $data, $subject, $properties, '___glt' ) || - self::propValuesChanged( $store, $data, $subject, $properties, '___gld' ) || - self::propValuesChanged( $store, $data, $subject, $properties, '___gll' ) || - self::propValuesChanged( $store, $data, $subject, $properties, '___gls' ) - ) { - self::purgeSubjectFromCache( $subject ); - LingoParser::purgeCache(); - } - - wfProfileOut( __METHOD__ ); - return true; - } - - /** - * Initiates the purging of the cache for a given subject page. - * - * @param Page $wikipage - * @return Bool - */ - static function purgeCacheForSubject( SMWDIWikiPage $subject, $purgeLingo = true ) { - - wfProfileIn( __METHOD__ ); - - // get the store - $store = smwfGetStore(); - - // get its properties - $properties = $store->getProperties($subject); - - // first handle subobjects recursively - if ( array_key_exists( '_SOBJ', $properties ) ) { - foreach ( $store->getPropertyValues( $subject, $properties['_SOBJ'] ) as $so ) { - self::purgeCacheForSubject( $so->getSubject(), false ); - } - } - - self::purgeSubjectFromCache( $subject ); - if ( $purgeLingo ) { - LingoParser::purgeCache(); - } - - wfProfileOut( __METHOD__ ); - return true; - } - - /** - * Initiates the purging of the cache for a given title. - * Handler for TitleMoveComplete hook. - * - * @param Page $wikipage - * @return Bool - */ - static public function purgeCacheForTitle( &$old_title, &$new_title, &$user, $pageid, $redirid ) { - self::purgeCacheForSubject( SMWDIWikiPage::newFromTitle( $old_title ) ); - return true; - } - - /** - * Check if the values of the given property changed. - * To be unchanged every old value must match against exactly one new. - * - * @param SMWStore $store - * @param SMWSemanticData $data provides the subject and by that the new data - * @param type $properties contains the old data - * @param type $propId the id of the property to be checked - * @return boolean - */ - static protected function propValuesChanged( SMWStore &$store, SMWSemanticData &$data, SMWDIWikiPage &$subject, &$properties, $propId ) { - - // check if property changed - if ( array_key_exists( $propId, $properties ) ) { - $newEntries = $data->getPropertyValues( $properties[$propId] ); - $oldEntries = $store->getPropertyValues( $subject, $properties[$propId] ); - } else { - $newEntries = array(); - $oldEntries = $store->getPropertyValues( $subject, new SMWDIProperty( $propId ) ); - } - - // Did the number of entries change? - if ( count( $newEntries ) !== count( $oldEntries ) ) { - return true; - } - - // Match each new entry against an old entry - foreach ( $newEntries as $newDi ) { - $found = false; - foreach ( $oldEntries as $oldKey => $oldDi ) { - if ( $newDi->getHash() === $oldDi->getHash() ) { - $found = true; - unset( $oldEntries[$oldKey] ); - break; - } - } - - // If no match was possible... - if ( !$found ) { - return true; - } - } - - // Are there unmatched old entries left? - if ( count( $oldEntries ) > 0 ) { - return true; - } - - // Every new entry matched to exaclty one old entry and vice versa - return false; - } - - /** - * Purges the glossary entry for the given SMWSemanticData object from the cache. - */ - static protected function purgeSubjectFromCache( SMWDIWikiPage &$subject ) { - - wfProfileIn( __METHOD__ ); - - global $wgexLingoCacheType; - $cache = ($wgexLingoCacheType !== null) ? wfGetCache( $wgexLingoCacheType ) : wfGetMainCache(); - $cachekey = wfMemcKey( 'ext', 'semanticglossary', $subject->getSerialization() ); - $cache->delete( $cachekey ); - - wfProfileOut( __METHOD__ ); - return true; - } - -} diff --git a/src/Cache/ElementsCacheBuilder.php b/src/Cache/ElementsCacheBuilder.php new file mode 100644 index 0000000..a036123 --- /dev/null +++ b/src/Cache/ElementsCacheBuilder.php @@ -0,0 +1,245 @@ +<?php + +namespace SG\Cache; + +use SG\CacheHelper; + +use SMWStore; +use SMWDIProperty; +use SMWStringValue; +use SMWPrintRequest; +use SMWPropertyValue; +use SMWThingDescription; +use SMWSomeProperty; +use SMWQuery; +use LingoElement; + +use BagOStuff; + +/** + * @ingroup SG + * @ingroup SemanticGlossary + * + * @license GNU GPL v2+ + * @since 1.1 + * + * @author Stephan Gambke + * @author mwjames + */ +class ElementsCacheBuilder { + + /* @var Store */ + protected $store; + + /* @var BagOStuff */ + protected $cache; + + protected $mDiTerm; + protected $mDiDefinition; + protected $mDiLink; + protected $mDiStyle; + + protected $mDvTerm; + protected $mDvDefinition; + protected $mDvLink; + protected $mDvStyle; + + protected $queryResults; + + /** + * @since 1.1 + * + * @param SMWStore $store + * @param BagOStuff|null $cache + */ + public function __construct( SMWStore $store, BagOStuff $cache = null ) { + $this->store = $store; + $this->cache = $cache; + + if ( $this->cache === null ) { + $this->cache = CacheHelper::getCache(); + } + } + + /** + * @since 1.1 + * + * @return array + */ + public function getElements() { + wfProfileIn( __METHOD__ ); + + $ret = array(); + + if ( $this->queryResults === null ) { + $this->queryResults = $this->store->getQueryResult( $this->buildQuery() )->getResults(); + } + + // find next line + $page = current( $this->queryResults ); + + if ( $page && count( $ret ) == 0 ) { + + next( $this->queryResults ); + + $cachekey = CacheHelper::getKey( $page ); + $cachedResult = $this->cache->get( $cachekey ); + + // cache hit? + if ( $cachedResult !== false && $cachedResult !== null ) { + + wfDebug( "Cache hit: Got glossary entry $cachekey from cache.\n" ); + $ret = &$cachedResult; + } else { + + wfDebug( "Cache miss: Glossary entry $cachekey not found in cache.\n" ); + + $ret = $this->buildElements( + $this->getTerms( $page ), + $this->getDefinitionValue( $page ), + $this->getLinkValue( $page ), + $this->getStyleValue( $page ), + $page + ); + + wfDebug( "Cached glossary entry $cachekey.\n" ); + $this->cache->set( $cachekey, $ret ); + } + } + + wfProfileOut( __METHOD__ ); + return $ret; + } + + protected function buildElements( $terms, $definition, $link, $style, $page ) { + + $ret = array(); + + foreach ( $terms as $term ) { + $tmp_ret = array( + LingoElement::ELEMENT_TERM => $term, + LingoElement::ELEMENT_DEFINITION => $definition, + LingoElement::ELEMENT_LINK => $link, + LingoElement::ELEMENT_STYLE => $style, + LingoElement::ELEMENT_SOURCE => $page + ); + + $ret[] = $tmp_ret; + } + + return $ret; + } + + protected function buildQuery() { + // build term data item and data value for later use + $this->mDiTerm = new SMWDIProperty( '___glt' ); + $this->mDvTerm = new SMWStringValue( '_str' ); + $this->mDvTerm->setProperty( $this->mDiTerm ); + + $pvTerm = new SMWPropertyValue( '__pro' ); + $pvTerm->setDataItem( $this->mDiTerm ); + $prTerm = new SMWPrintRequest( SMWPrintRequest::PRINT_PROP, null, $pvTerm ); + + // build definition data item and data value for later use + $this->mDiDefinition = new SMWDIProperty( '___gld' ); + $this->mDvDefinition = new SMWStringValue( '_txt' ); + $this->mDvDefinition->setProperty( $this->mDiDefinition ); + + $pvDefinition = new SMWPropertyValue( '__pro' ); + $pvDefinition->setDataItem( $this->mDiDefinition ); + $prDefinition = new SMWPrintRequest( SMWPrintRequest::PRINT_PROP, null, $pvDefinition ); + + // build link data item and data value for later use + $this->mDiLink = new SMWDIProperty( '___gll' ); + $this->mDvLink = new SMWStringValue( '_str' ); + $this->mDvLink->setProperty( $this->mDiLink ); + + $pvLink = new SMWPropertyValue( '__pro' ); + $pvLink->setDataItem( $this->mDiLink ); + $prLink = new SMWPrintRequest( SMWPrintRequest::PRINT_PROP, null, $pvLink ); + + // build style data item and data value for later use + $this->mDiStyle = new SMWDIProperty( '___gls' ); + $this->mDvStyle = new SMWStringValue( '_txt' ); + $this->mDvStyle->setProperty( $this->mDiStyle ); + + $pvStyle = new SMWPropertyValue( '__pro' ); + $pvStyle->setDataItem( $this->mDiStyle ); + $prStyle = new SMWPrintRequest( SMWPrintRequest::PRINT_PROP, null, $pvStyle ); + + // Create query + $desc = new SMWSomeProperty( new SMWDIProperty( '___glt' ), new SMWThingDescription() ); + $desc->addPrintRequest( $prTerm ); + $desc->addPrintRequest( $prDefinition ); + $desc->addPrintRequest( $prLink ); + $desc->addPrintRequest( $prStyle ); + + $query = new SMWQuery( $desc, false, false ); + $query->sort = true; + $query->sortkeys['___glt'] = 'ASC'; + + return $query; + } + + protected function getDefinitionValue( $page ) { + + $definition = null; + + $definitions = $this->store->getPropertyValues( + $page, + $this->mDiDefinition + ); + + if ( !empty( $definitions ) ) { + $this->mDvDefinition->setDataItem( $definitions[0] ); + $definition = trim( $this->mDvDefinition->getShortWikiText() ); + } + + return $definition; + } + + protected function getLinkValue( $page ) { + + $link = null; + + $links = $this->store->getPropertyValues( $page, $this->mDiLink );; + + if ( !empty( $links ) ) { + $this->mDvLink->setDataItem( $links[0] ); + $link = trim( $this->mDvLink->getShortWikiText() ); + } + + return $link; + } + + protected function getStyleValue( $page ) { + + $style = null; + + $styles = $this->store->getPropertyValues( $page, $this->mDiStyle );; + + if ( !empty( $styles ) ) { + $this->mDvStyle->setDataItem( $styles[0] ); + $style = trim( $this->mDvStyle->getShortWikiText() ); + } + + return $style; + } + + protected function getTerms( $page ) { + + $collectedTerms = array(); + + $terms = $this->store->getPropertyValues( $page, $this->mDiTerm ); + + if ( $terms !== array() ) { + foreach ( $terms as $term ) { + $this->mDvTerm->setDataItem( $term ); + $collectedTerms[] = trim( $this->mDvTerm->getShortWikiText() ); + } + } + + return $collectedTerms; + } + +} diff --git a/src/LingoBackendAdapter.php b/src/LingoBackendAdapter.php new file mode 100644 index 0000000..ff77715 --- /dev/null +++ b/src/LingoBackendAdapter.php @@ -0,0 +1,76 @@ +<?php + +namespace SG; + +use SG\Cache\ElementsCacheBuilder; + +use LingoBackend; +use LingoMessageLog; + +/** + * @ingroup SG + * @ingroup SemanticGlossary + * + * @license GNU GPL v2+ + * @since 1.1 + * + * @author mwjames + */ +class LingoBackendAdapter extends LingoBackend { + + /* ElementsCacheBuilder */ + protected $elementsCacheBuilder = null; + + protected $elements = array(); + + /** + * @since 1.1 + * + * @param LingoMessageLog|null &$messages + * @param ElementsCacheBuilder|null $elementsCacheBuilder + */ + public function __construct( LingoMessageLog &$messages = null, ElementsCacheBuilder $elementsCacheBuilder = null ) { + parent::__construct( $messages ); + $this->elementsCacheBuilder = $elementsCacheBuilder; + + if ( $this->elementsCacheBuilder === null ) { + $this->elementsCacheBuilder = new ElementsCacheBuilder( smwfGetStore() ); + } + } + + /** + * This function returns the next element. The element is an array of four + * strings: Term, Definition, Link, Source, Style. If there is no next element + * the function returns null. + * + * @since 1.1 + * + * @return the next element or null + */ + public function next() { + + wfProfileIn( __METHOD__ ); + + if ( $this->elements === array() ) { + $this->elements = $this->elementsCacheBuilder->getElements(); + } + + wfProfileOut( __METHOD__ ); + return array_pop( $this->elements ); + } + + /** + * This backend is cache-enabled so this function returns true. + * + * Actual caching is done by the parser, the backend just calls + * LingoParser::purgeCache when necessary. + * + * @since 1.1 + * + * @return boolean + */ + public function useCache() { + return true; + } + +} diff --git a/tests/bootstrap.php b/tests/bootstrap.php index d7ebbed..b5fde31 100644 --- a/tests/bootstrap.php +++ b/tests/bootstrap.php @@ -1,21 +1,38 @@ <?php -/** - * PHPUnit test bootstrap file - * - * @licence GNU GPL v2+ - * @author Jeroen De Dauw < jeroended...@gmail.com > - */ - if ( php_sapi_name() !== 'cli' ) { die( 'Not an entry point' ); } -$pwd = getcwd(); -chdir( __DIR__ . '/..' ); -passthru( 'composer update' ); -chdir( $pwd ); +if ( !defined( 'MEDIAWIKI' ) ) { + die( 'MediaWiki is not available for the test environment' ); +} -if ( !is_readable( __DIR__ . '/../vendor/autoload.php' ) ) { - die( 'You need to install this package with Composer before you can run the tests' ); +function registerAutoloaderPath( $identifier, $path ) { + print( "\nUsing the {$identifier} vendor autoloader ...\n" ); + return require $path; +} + +function runTestAutoLoader() { + + $mwVendorPath = __DIR__ . '/../../../vendor/autoload.php'; + $localVendorPath = __DIR__ . '/../vendor/autoload.php'; + + if ( is_readable( $localVendorPath ) ) { + $autoLoader = registerAutoloaderPath( 'local', $localVendorPath ); + } elseif ( is_readable( $mwVendorPath ) ) { + $autoLoader = registerAutoloaderPath( 'MediaWiki', $mwVendorPath ); + } + + if ( !$autoLoader instanceof \Composer\Autoload\ClassLoader ) { + return false; + } + + $autoLoader->addPsr4( 'SG\\Tests\\', __DIR__ . '/phpunit' ); + + return true; +} + +if ( !runTestAutoLoader() ) { + die( 'The required test autoloader was not accessible' ); } diff --git a/tests/mw-phpunit-runner.php b/tests/mw-phpunit-runner.php new file mode 100644 index 0000000..741211f --- /dev/null +++ b/tests/mw-phpunit-runner.php @@ -0,0 +1,42 @@ +<?php + +/** + * Lazy script to invoke the MediaWiki phpunit runner + * + * php <mw-phpunit-runner.php> [--coverage-clover|--coverage-html] + */ + +if ( php_sapi_name() !== 'cli' ) { + die( 'Not an entry point' ); +} + +print( "\nMediaWiki phpunit runnner ... \n" ); + +function isReadablePath( $path ) { + + if ( is_readable( $path ) ) { + return $path; + } + + throw new RuntimeException( "Expected an accessible {$path} path" ); +} + +function addArguments() { + + $arguments = null; + $args = $GLOBALS['argv']; + + for ( $arg = reset( $args ); $arg !== false; $arg = next( $args ) ) { + + if ( $arg === '--coverage-clover' || $arg === '--coverage-html' ) { + $arguments = $arg . ' ' . escapeshellarg( next( $args ) ); + } + } + + return $arguments; +} + +$mw = isReadablePath( __DIR__ . "/../../../tests/phpunit/phpunit.php" ); +$config = isReadablePath( __DIR__ . "/../phpunit.xml.dist" ); + +passthru( "php {$mw} -c {$config} ". addArguments() ); diff --git a/tests/phpunit/Cache/ElementsCacheBuilderTest.php b/tests/phpunit/Cache/ElementsCacheBuilderTest.php new file mode 100644 index 0000000..1eef667 --- /dev/null +++ b/tests/phpunit/Cache/ElementsCacheBuilderTest.php @@ -0,0 +1,107 @@ +<?php + +namespace SG\Tests\Cache; + +use SG\Cache\ElementsCacheBuilder; +use SG\CacheHelper; + +use LingoElement; + +use SMWDIWikiPage as DIWikiPage; +use SMWDIBlob as DIBlob; +use Title; +use HashBagOStuff; + +/** + * @covers \SG\Cache\ElementsCacheBuilder + * + * @ingroup Test + * + * @group SG + * @group SGExtension + * @group extension-semantic-glossary + * + * @license GNU GPL v2+ + * @since 1.1 + * + * @author mwjames + */ +class ElementsCacheBuilderTest extends \PHPUnit_Framework_TestCase { + + public function testCanConstruct() { + + $store = $this->getMockBuilder( 'SMWStore' ) + ->disableOriginalConstructor() + ->getMockForAbstractClass(); + + $this->assertInstanceOf( + '\SG\Cache\ElementsCacheBuilder', + new ElementsCacheBuilder( $store ) + ); + } + + public function testGetTermsForSingleTermWithDefinitionOnNonCachedResult() { + + $page = DIWikiPage::newFromTitle( Title::newFromText( __METHOD__ ) ); + + $queryResult = $this->getMockBuilder( '\stdClass' ) + ->disableOriginalConstructor() + ->setMethods( array( 'getResults' ) ) + ->getMock(); + + $queryResult->expects( $this->once() ) + ->method( 'getResults' ) + ->will( $this->returnValue( array( $page ) ) ); + + $store = $this->getMockBuilder( 'SMWStore' ) + ->disableOriginalConstructor() + ->getMockForAbstractClass(); + + $store->expects( $this->once() ) + ->method( 'getQueryResult' ) + ->will( $this->returnValue( $queryResult ) ); + + // at() position depends on the sequence as to when a method is called + + $store->expects( $this->at( 1 ) ) + ->method( 'getPropertyValues' ) + ->will( $this->returnValue( array( new DIBlob( ' Foo term ' ) ) ) ); + + $store->expects( $this->at( 2 ) ) + ->method( 'getPropertyValues' ) + ->will( $this->returnValue( array( new DIBlob( ' some Definition ' ) ) ) ); + + $cache = new HashBagOStuff(); + + $instance = new ElementsCacheBuilder( $store, $cache ); + + $results = $instance->getElements(); + + $this->assertEquals( + $results, + $cache->get( CacheHelper::getKey( $page ) ) + ); + + $this->assertLingoElement( + 'Foo term', + 'some Definition', + null, + null, + $results[0] + ); + } + + protected function assertLingoElement( $term, $definition, $link, $style, $result ) { + + $this->assertEquals( $term, $result[ LingoElement::ELEMENT_TERM ] ); + $this->assertEquals( $definition, $result[ LingoElement::ELEMENT_DEFINITION ] ); + $this->assertEquals( $link, $result[ LingoElement::ELEMENT_LINK ] ); + $this->assertEquals( $style, $result[ LingoElement::ELEMENT_STYLE ] ); + + $this->assertInstanceOf( + 'SMWDIWikiPage', + $result[ LingoElement::ELEMENT_SOURCE ] + ); + } + +} diff --git a/tests/phpunit/LingoBackendAdapterTest.php b/tests/phpunit/LingoBackendAdapterTest.php new file mode 100644 index 0000000..800938c --- /dev/null +++ b/tests/phpunit/LingoBackendAdapterTest.php @@ -0,0 +1,55 @@ +<?php + +namespace SG\Tests; + +use SG\LingoBackendAdapter; + +/** + * @covers \SG\LingoBackendAdapter + * + * @ingroup Test + * + * @group SG + * @group SGExtension + * @group extension-semantic-glossary + * + * @license GNU GPL v2+ + * @since 1.1 + * + * @author mwjames + */ +class LingoBackendAdapterTest extends \PHPUnit_Framework_TestCase { + + public function testCanConstruct() { + + $this->assertInstanceOf( + '\SG\LingoBackendAdapter', + new LingoBackendAdapter() + ); + } + + public function testNextOnEmptyElementsResult() { + + $lingoMessageLog = $this->getMockBuilder( '\LingoMessageLog' ) + ->disableOriginalConstructor() + ->getMock(); + + $elementsCacheBuilder = $this->getMockBuilder( '\SG\Cache\ElementsCacheBuilder' ) + ->disableOriginalConstructor() + ->getMock(); + + $elementsCacheBuilder->expects( $this->once() ) + ->method( 'getElements' ) + ->will( $this->returnValue( array() ) ); + + $instance = new LingoBackendAdapter( + $lingoMessageLog, + $elementsCacheBuilder + ); + + $instance->next(); + + $this->assertTrue( $instance->useCache() ); + } + +} -- To view, visit https://gerrit.wikimedia.org/r/133044 To unsubscribe, visit https://gerrit.wikimedia.org/r/settings Gerrit-MessageType: merged Gerrit-Change-Id: Id59164a9cc2c34a777313fb12524ceedf35801e5 Gerrit-PatchSet: 4 Gerrit-Project: mediawiki/extensions/SemanticGlossary Gerrit-Branch: master Gerrit-Owner: Mwjames <jamesin.hongkon...@gmail.com> Gerrit-Reviewer: Foxtrott <s7ep...@gmail.com> Gerrit-Reviewer: Mwjames <jamesin.hongkon...@gmail.com> Gerrit-Reviewer: jenkins-bot <> _______________________________________________ MediaWiki-commits mailing list MediaWiki-commits@lists.wikimedia.org https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits