Mwjames has uploaded a new change for review.

  https://gerrit.wikimedia.org/r/133044

Change subject: Refactored SemanticGlossaryBackend
......................................................................

Refactored SemanticGlossaryBackend

- Encapsulate LingoBackend using \SG\LingoBackendAdapter
- Added \SG\Cache\TermsBuilder

Change-Id: Id59164a9cc2c34a777313fb12524ceedf35801e5
---
A README.md
M SemanticGlossary.php
D SemanticGlossaryBackend.php
D SemanticGlossaryCacheHandling.php
A src/Cache/TermsBuilder.php
A src/LingoBackendAdapter.php
M tests/bootstrap.php
A tests/phpunit/Cache/TermsBuilderTest.php
A tests/phpunit/LingoBackendAdapterTest.php
9 files changed, 506 insertions(+), 380 deletions(-)


  git pull 
ssh://gerrit.wikimedia.org:29418/mediawiki/extensions/SemanticGlossary 
refs/changes/44/133044/1

diff --git a/README.md b/README.md
new file mode 100644
index 0000000..3981a91
--- /dev/null
+++ b/README.md
@@ -0,0 +1,39 @@
+# 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 [PHPUnit][mw-testing] configuration file found in the root directory:
+
+Due to an existing Mediawiki dependency, the deployed `phpunit.php` should be 
used to ensure proper autoloading of classes during testing.
+```sh
+php <mw-path>/tests/phpunit/phpunit.php -c 
<mw-path>/extensions/SemanticGlossary/phpunit.xml.dist
+```
+
+[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..b6848fc 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\TermsBuilder'    => $dir . 
'/src/Cache/TermsBuilder.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/TermsBuilder.php b/src/Cache/TermsBuilder.php
new file mode 100644
index 0000000..df5a258
--- /dev/null
+++ b/src/Cache/TermsBuilder.php
@@ -0,0 +1,213 @@
+<?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 TermsBuilder {
+
+       /* @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 $cache
+        */
+       public function __construct( SMWStore $store, BagOStuff $cache ) {
+               $this->store = $store;
+               $this->cache = $cache;
+       }
+
+       /**
+        * @since 1.1
+        *
+        * @return array
+        */
+       public function getTerms() {
+               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" );
+
+                               $definitions = $this->store->getPropertyValues( 
$page, $this->mDiDefinition );
+                               $links = $this->store->getPropertyValues( 
$page, $this->mDiLink );
+                               $styles = $this->store->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() );
+                               }
+
+                               $ret = $this->buildTermsCache(
+                                       $this->store->getPropertyValues( $page, 
$this->mDiTerm ),
+                                       $definition,
+                                       $link,
+                                       $style,
+                                       $page
+                               );
+
+                               wfDebug( "Cached glossary entry $cachekey.\n" );
+                               $this->cache->set( $cachekey, $ret );
+                       }
+               }
+
+               wfProfileOut( __METHOD__ );
+               return $ret;
+       }
+
+       protected function buildTermsCache( $terms, $definition, $link, $style, 
$page ) {
+
+               $tmp_terms = array();
+               $ret = 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
+                       );
+
+                       $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;
+       }
+
+}
diff --git a/src/LingoBackendAdapter.php b/src/LingoBackendAdapter.php
new file mode 100644
index 0000000..5daabac
--- /dev/null
+++ b/src/LingoBackendAdapter.php
@@ -0,0 +1,80 @@
+<?php
+
+namespace SG;
+
+use SG\Cache\TermsBuilder;
+use SG\CacheHelper;
+use LingoBackend;
+use LingoMessageLog;
+
+/**
+ * @ingroup SG
+ * @ingroup SemanticGlossary
+ *
+ * @license GNU GPL v2+
+ * @since 1.1
+ *
+ * @author mwjames
+ */
+class LingoBackendAdapter extends LingoBackend {
+
+       /* TermsCacheBuilder */
+       protected $termsBuilder = null;
+
+       protected $terms = array();
+
+       /**
+        * @since 1.1
+        *
+        * @param LingoMessageLog|null &$messages
+        * @param TermsBuilder|null $termsBuilder
+        */
+       public function __construct( LingoMessageLog &$messages = null, 
TermsBuilder $termsBuilder = null ) {
+               parent::__construct( $messages );
+               $this->termsBuilder = $termsBuilder;
+
+               if ( $this->termsBuilder === null ) {
+                       $this->termsBuilder = $this->newTermsBuilder();
+               }
+       }
+
+       /**
+        * 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->terms === array() ) {
+                       $this->terms = $this->termsBuilder->getTerms();
+               }
+
+               wfProfileOut( __METHOD__ );
+               return array_pop( $this->terms );
+       }
+
+       /**
+        * 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;
+       }
+
+       private function newTermsBuilder() {
+               return new TermsBuilder( smwfGetStore(), 
CacheHelper::getCache() );
+       }
+
+}
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/phpunit/Cache/TermsBuilderTest.php 
b/tests/phpunit/Cache/TermsBuilderTest.php
new file mode 100644
index 0000000..11cf2cd
--- /dev/null
+++ b/tests/phpunit/Cache/TermsBuilderTest.php
@@ -0,0 +1,92 @@
+<?php
+
+namespace SG\Tests\Cache;
+
+use SG\Cache\TermsBuilder;
+use SG\CacheHelper;
+
+use SMWDIWikiPage as DIWikiPage;
+use SMWDIBlob as DIBlob;
+use Title;
+use HashBagOStuff;
+
+/**
+ * @covers \SG\Cache\TermsBuilder
+ *
+ * @ingroup Test
+ *
+ * @group SG
+ * @group SGExtension
+ * @group extension-semantic-glossary
+ *
+ * @license GNU GPL v2+
+ * @since 1.1
+ *
+ * @author mwjames
+ */
+class TermsBuilderTest extends \PHPUnit_Framework_TestCase {
+
+       public function testCanConstruct() {
+
+               $store = $this->getMockBuilder( 'SMWStore' )
+                       ->disableOriginalConstructor()
+                       ->getMockForAbstractClass();
+
+               $cache = new HashBagOStuff();
+
+               $this->assertInstanceOf(
+                       '\SG\Cache\TermsBuilder',
+                       new TermsBuilder( $store, $cache )
+               );
+       }
+
+       public function testGetTermsForSingleTermWithDefinitionAsNonCached() {
+
+               $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 ) );
+
+               // Position depends on the sequence as the when method is called
+
+               $store->expects( $this->at( 1 ) )
+                       ->method( 'getPropertyValues' )
+                       ->will( $this->returnValue( array( new DIBlob( ' some 
Definition ' ) ) ) );
+
+               $store->expects( $this->at( 4 ) )
+                       ->method( 'getPropertyValues' )
+                       ->will( $this->returnValue( array( new DIBlob( ' Foo 
term ' ) ) ) );
+
+               $cache = new HashBagOStuff();
+
+               $instance = new TermsBuilder( $store, $cache );
+
+               $results = $instance->getTerms();
+
+               $this->assertEquals(
+                       $results,
+                       $cache->get( CacheHelper::getKey( $page ) ),
+                       'Asserts that the cached item is equal to the result 
output item'
+               );
+
+               foreach ( $results as $result ) {
+                       $this->assertEquals( 'Foo term', $result[0] );
+                       $this->assertEquals( 'some Definition', $result[1] );
+               }
+       }
+
+}
diff --git a/tests/phpunit/LingoBackendAdapterTest.php 
b/tests/phpunit/LingoBackendAdapterTest.php
new file mode 100644
index 0000000..8974590
--- /dev/null
+++ b/tests/phpunit/LingoBackendAdapterTest.php
@@ -0,0 +1,49 @@
+<?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 testNextOnEmptyTermsResult() {
+
+               $lingoMessageLog = $this->getMockBuilder( '\LingoMessageLog' )
+                       ->disableOriginalConstructor()
+                       ->getMock();
+
+               $termsBuilder = $this->getMockBuilder( '\SG\Cache\TermsBuilder' 
)
+                       ->disableOriginalConstructor()
+                       ->getMock();
+
+               $termsBuilder->expects( $this->once() )
+                       ->method( 'getTerms' )
+                       ->will( $this->returnValue( array() ) );
+
+               $instance = new LingoBackendAdapter( $lingoMessageLog, 
$termsBuilder );
+               $instance->next();
+       }
+
+}

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

Gerrit-MessageType: newchange
Gerrit-Change-Id: Id59164a9cc2c34a777313fb12524ceedf35801e5
Gerrit-PatchSet: 1
Gerrit-Project: mediawiki/extensions/SemanticGlossary
Gerrit-Branch: master
Gerrit-Owner: Mwjames <jamesin.hongkon...@gmail.com>

_______________________________________________
MediaWiki-commits mailing list
MediaWiki-commits@lists.wikimedia.org
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits

Reply via email to