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

Reply via email to