564 lines
16 KiB
PHP
564 lines
16 KiB
PHP
<?php
|
|
|
|
namespace SMW\Tests;
|
|
|
|
use SMW\DataValueFactory;
|
|
use SMW\Elastic\ElasticStore;
|
|
use SMW\Listener\EventListener\EventHandler;
|
|
use SMW\Services\ServicesFactory as ApplicationFactory;
|
|
use SMW\SPARQLStore\TurtleTriplesBuilder;
|
|
use SMW\Tests\Utils\JSONScript\ApiTestCaseProcessor;
|
|
use SMW\Tests\Utils\JSONScript\JsonTestCaseFileHandler;
|
|
use SMW\Tests\Utils\JSONScript\ParserHtmlTestCaseProcessor;
|
|
use SMW\Tests\Utils\JSONScript\ParserTestCaseProcessor;
|
|
use SMW\Tests\Utils\JSONScript\QueryTestCaseInterpreter;
|
|
use SMW\Tests\Utils\JSONScript\QueryTestCaseProcessor;
|
|
use SMW\Tests\Utils\JSONScript\RdfTestCaseProcessor;
|
|
use SMW\Tests\Utils\JSONScript\SpecialPageTestCaseProcessor;
|
|
|
|
/**
|
|
* It is provided for external extensions that seek a simple way of creating tests
|
|
* with (or without) Semantic MediaWiki integration in mind.
|
|
*
|
|
* @group semantic-mediawiki
|
|
* @group Database
|
|
* @group medium
|
|
*
|
|
* @license GPL-2.0-or-later
|
|
* @since 3.1
|
|
*
|
|
* @author mwjames
|
|
*/
|
|
abstract class JSONScriptServicesTestCaseRunner extends JSONScriptTestCaseRunner {
|
|
|
|
/**
|
|
* @var ValidatorFactory
|
|
*/
|
|
protected $validatorFactory;
|
|
|
|
/**
|
|
* @var RunnerFactory
|
|
*/
|
|
protected $runnerFactory;
|
|
|
|
/**
|
|
* @var ApiFactory
|
|
*/
|
|
private $apiFactory;
|
|
|
|
/**
|
|
* @see JSONScriptTestCaseRunner::$deletePagesOnTearDown
|
|
*/
|
|
protected $deletePagesOnTearDown = true;
|
|
|
|
/**
|
|
* Defines set of assertation services to be available by the runner
|
|
*/
|
|
protected $defaultAssertionTypes = [
|
|
'parser',
|
|
'parser-html',
|
|
'special'
|
|
];
|
|
|
|
protected function setUp(): void {
|
|
parent::setUp();
|
|
|
|
$utilityFactory = $this->testEnvironment->getUtilityFactory();
|
|
|
|
$this->runnerFactory = $utilityFactory->newRunnerFactory();
|
|
$this->validatorFactory = $utilityFactory->newValidatorFactory();
|
|
$this->apiFactory = $utilityFactory->newMwApiFactory();
|
|
|
|
$this->testEnvironment->resetPoolCacheById( TurtleTriplesBuilder::POOLCACHE_ID );
|
|
|
|
// Make sure LocalSettings don't interfere with the default settings
|
|
$this->testEnvironment->withConfiguration(
|
|
[
|
|
'smwgQueryResultCacheType' => false,
|
|
'smwgQFilterDuplicates' => false,
|
|
'smwgExportResourcesAsIri' => false,
|
|
'smwgCompactLinkSupport' => false,
|
|
'smwgEnabledFulltextSearch' => false,
|
|
'smwgSparqlReplicationPropertyExemptionList' => [],
|
|
'smwgPageSpecialProperties' => [ '_MDAT' ],
|
|
'smwgFieldTypeFeatures' => SMW_FIELDT_NONE,
|
|
'smwgDVFeatures' => $GLOBALS['smwgDVFeatures'] & ~SMW_DV_NUMV_USPACE,
|
|
'smwgCacheUsage' => [
|
|
'api.browse' => false
|
|
] + $GLOBALS['smwgCacheUsage'],
|
|
// MW 1.37 defaults to html5 fragment mode. Force legacy mode for consistency.
|
|
'wgFragmentMode' => [ 'legacy', 'html5' ],
|
|
]
|
|
);
|
|
}
|
|
|
|
/**
|
|
* @param string $type
|
|
*
|
|
* @return bool
|
|
*/
|
|
protected function runTestAssertionForType( string $type ): bool {
|
|
return in_array( $type, $this->defaultAssertionTypes );
|
|
}
|
|
|
|
/**
|
|
* @see JSONScriptTestCaseRunner::runTestCaseFile
|
|
*
|
|
* @param JsonTestCaseFileHandler $jsonTestCaseFileHandler
|
|
*/
|
|
protected function runTestCaseFile( JsonTestCaseFileHandler $jsonTestCaseFileHandler ) {
|
|
$this->checkEnvironmentToSkipCurrentTest( $jsonTestCaseFileHandler );
|
|
|
|
// Setup
|
|
$this->prepareTest( $jsonTestCaseFileHandler );
|
|
|
|
// Before test execution
|
|
$this->doRunBeforeTest( $jsonTestCaseFileHandler );
|
|
|
|
// For some reason there are some random failures where
|
|
// the instance hasn't reset the cache in time to fetch the
|
|
// property definition
|
|
$eventDispatcher = EventHandler::getInstance()->getEventDispatcher();
|
|
|
|
$eventDispatcher->dispatch( 'exporter.reset' );
|
|
$eventDispatcher->dispatch( 'query.comparator.reset' );
|
|
|
|
// Run test cases
|
|
if ( $this->runTestAssertionForType( 'parser' ) ) {
|
|
$this->doRunParserTests( $jsonTestCaseFileHandler );
|
|
}
|
|
|
|
if ( $this->runTestAssertionForType( 'parser-html' ) ) {
|
|
$this->doRunParserHtmlTests( $jsonTestCaseFileHandler );
|
|
}
|
|
|
|
if ( $this->runTestAssertionForType( 'special' ) ) {
|
|
$this->doRunSpecialTests( $jsonTestCaseFileHandler );
|
|
}
|
|
|
|
if ( $this->runTestAssertionForType( 'rdf' ) ) {
|
|
$this->doRunRdfTests( $jsonTestCaseFileHandler );
|
|
}
|
|
|
|
if ( $this->runTestAssertionForType( 'query' ) ) {
|
|
$this->doRunQueryStackTests( $jsonTestCaseFileHandler );
|
|
}
|
|
|
|
if ( $this->runTestAssertionForType( 'api' ) ) {
|
|
$this->doRunApiTests( $jsonTestCaseFileHandler );
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @see JSONScriptTestCaseRunner::getPermittedSettings
|
|
*/
|
|
protected function getPermittedSettings() {
|
|
parent::getPermittedSettings();
|
|
|
|
$elasticsearchConfig = function ( $val ) {
|
|
if ( $this->getStore() instanceof ElasticStore ) {
|
|
$config = $this->getStore()->getConnection( 'elastic' )->getConfig();
|
|
|
|
foreach ( $val as $key => $value ) {
|
|
$config->set( $key, array_merge( $config->get( $key ), $value ) );
|
|
}
|
|
|
|
return $config->toArray();
|
|
}
|
|
};
|
|
|
|
$this->registerConfigValueCallback( 'smwgElasticsearchConfig', $elasticsearchConfig );
|
|
|
|
// Config isolation causes NamespaceInfo to not access the `MainConfig`
|
|
// therefore reset the services so that it copies the changed setting.
|
|
// https://github.com/wikimedia/mediawiki/commit/7ada64684e6477be44405dedbfdb0d96242f2e73
|
|
$capitalLinks = function ( $val ) {
|
|
$this->testEnvironment->resetMediaWikiService( 'NamespaceInfo' );
|
|
return $val;
|
|
};
|
|
|
|
$this->registerConfigValueCallback( 'wgCapitalLinks', $capitalLinks );
|
|
|
|
return [
|
|
'smwgNamespacesWithSemanticLinks',
|
|
'smwgPageSpecialProperties',
|
|
'smwgNamespace',
|
|
'smwgExportBCNonCanonicalFormUse',
|
|
'smwgExportBCAuxiliaryUse',
|
|
'smwgExportResourcesAsIri',
|
|
'smwgQMaxSize',
|
|
'smwgQMaxDepth',
|
|
'smwStrictComparators',
|
|
'smwgQSubpropertyDepth',
|
|
'smwgQSubcategoryDepth',
|
|
'smwgQConceptCaching',
|
|
'smwgMaxNonExpNumber',
|
|
'smwgDVFeatures',
|
|
'smwgEnabledQueryDependencyLinksStore',
|
|
'smwgEnabledFulltextSearch',
|
|
'smwgFulltextDeferredUpdate',
|
|
'smwgFulltextSearchIndexableDataTypes',
|
|
'smwgFixedProperties',
|
|
'smwgPropertyZeroCountDisplay',
|
|
'smwgQueryResultCacheType',
|
|
'smwgLinksInValues',
|
|
'smwgQFilterDuplicates',
|
|
'smwgQueryProfiler',
|
|
'smwgEntityCollation',
|
|
'smwgSparqlQFeatures',
|
|
'smwgQExpensiveThreshold',
|
|
'smwgQExpensiveExecutionLimit',
|
|
'smwgFieldTypeFeatures',
|
|
'smwgCreateProtectionRight',
|
|
'smwgParserFeatures',
|
|
'smwgCategoryFeatures',
|
|
'smwgDefaultOutputFormatters',
|
|
'smwgCompactLinkSupport',
|
|
'smwgCacheUsage',
|
|
'smwgQSortFeatures',
|
|
'smwgQMaxInlineLimit',
|
|
'smwgElasticsearchConfig',
|
|
'smwgDefaultNumRecurringEvents',
|
|
'smwgMandatorySubpropertyParentTypeInheritance',
|
|
'smwgPlainList',
|
|
'smwgMaxPropertyValues',
|
|
|
|
// MW related
|
|
'wgLanguageCode',
|
|
'wgContLang',
|
|
'wgLang',
|
|
'wgCapitalLinks',
|
|
'wgAllowDisplayTitle',
|
|
'wgRestrictDisplayTitle',
|
|
'wgSearchType',
|
|
'wgEnableUploads',
|
|
'wgFileExtensions',
|
|
'wgDefaultUserOptions',
|
|
'wgLocalTZoffset'
|
|
];
|
|
}
|
|
|
|
private function prepareTest( JsonTestCaseFileHandler $jsonTestCaseFileHandler ) {
|
|
foreach ( $this->getPermittedSettings() as $key ) {
|
|
$this->changeGlobalSettingTo(
|
|
$key,
|
|
$jsonTestCaseFileHandler->getSettingsFor( $key, $this->getConfigValueCallback( $key ) )
|
|
);
|
|
}
|
|
|
|
// This ensures that if content is created in the NS_MEDIAWIKI namespace
|
|
// and an object relies on the MediaWikiNsContentReader then it uses the DB
|
|
ApplicationFactory::clear();
|
|
ApplicationFactory::getInstance()->getMediaWikiNsContentReader()->skipMessageCache();
|
|
DataValueFactory::getInstance()->clear();
|
|
|
|
if ( $jsonTestCaseFileHandler->hasSetting( 'smwgFieldTypeFeatures' ) ) {
|
|
$this->doRunTableSetupBeforeContentCreation();
|
|
}
|
|
|
|
// #2135
|
|
// On some occasions (e.g. fixed properties) and to setup the correct
|
|
// table schema, run the creation once before the content is created
|
|
$pageList = $jsonTestCaseFileHandler->getPageCreationSetupList();
|
|
|
|
if ( $jsonTestCaseFileHandler->hasSetting( 'smwgFixedProperties' ) ) {
|
|
foreach ( $pageList as $page ) {
|
|
if ( isset( $page['namespace'] ) && $page['namespace'] === 'SMW_NS_PROPERTY' ) {
|
|
$this->createPagesFrom( [ $page ] );
|
|
}
|
|
}
|
|
|
|
$this->doRunTableSetupBeforeContentCreation();
|
|
}
|
|
|
|
$this->createPagesFrom(
|
|
$pageList,
|
|
NS_MAIN
|
|
);
|
|
}
|
|
|
|
private function doRunTableSetupBeforeContentCreation( $pageList = null ) {
|
|
if ( $pageList !== null ) {
|
|
$this->createPagesFrom( $pageList );
|
|
}
|
|
|
|
$maintenanceRunner = $this->runnerFactory->newMaintenanceRunner( 'setupStore' );
|
|
$maintenanceRunner->setQuiet();
|
|
$maintenanceRunner->run();
|
|
}
|
|
|
|
private function doRunBeforeTest( JsonTestCaseFileHandler $jsonTestCaseFileHandler ) {
|
|
foreach ( $jsonTestCaseFileHandler->findTasksBeforeTestExecutionByType( 'maintenance-run' ) as $runner => $options ) {
|
|
|
|
$maintenanceRunner = $this->runnerFactory->newMaintenanceRunner( $runner );
|
|
$maintenanceRunner->setQuiet();
|
|
|
|
$maintenanceRunner->setOptions(
|
|
(array)$options
|
|
);
|
|
|
|
$maintenanceRunner->run();
|
|
|
|
if ( isset( $options['quiet'] ) && $options['quiet'] === false ) {
|
|
print_r( $maintenanceRunner->getOutput() );
|
|
}
|
|
}
|
|
|
|
foreach ( $jsonTestCaseFileHandler->findTasksBeforeTestExecutionByType( 'job-run' ) as $jobType ) {
|
|
$jobQueueRunner = $this->runnerFactory->newJobQueueRunner( $jobType );
|
|
$jobQueueRunner->run();
|
|
}
|
|
|
|
$this->testEnvironment->executePendingDeferredUpdates();
|
|
}
|
|
|
|
private function doRunParserTests( JsonTestCaseFileHandler $jsonTestCaseFileHandler ) {
|
|
$testCases = $jsonTestCaseFileHandler->findTestCasesByType( 'parser' );
|
|
|
|
if ( $testCases === [] ) {
|
|
return;
|
|
}
|
|
|
|
$parserTestCaseProcessor = new ParserTestCaseProcessor(
|
|
$this->getStore(),
|
|
$this->validatorFactory->newSemanticDataValidator(),
|
|
$this->validatorFactory->newIncomingSemanticDataValidator( $this->getStore() ),
|
|
$this->validatorFactory->newStringValidator()
|
|
);
|
|
|
|
$parserTestCaseProcessor->setDebugMode(
|
|
$jsonTestCaseFileHandler->getDebugMode()
|
|
);
|
|
|
|
foreach ( $testCases as $case ) {
|
|
|
|
if ( $jsonTestCaseFileHandler->requiredToSkipFor( $case, $this->connectorId ) ) {
|
|
continue;
|
|
}
|
|
|
|
$parserTestCaseProcessor->process( $case );
|
|
}
|
|
}
|
|
|
|
private function doRunParserHtmlTests( JsonTestCaseFileHandler $jsonTestCaseFileHandler ) {
|
|
$testCases = $jsonTestCaseFileHandler->findTestCasesByType( 'parser-html' );
|
|
|
|
if ( $testCases === [] ) {
|
|
return;
|
|
}
|
|
|
|
$parserHtmlTestCaseProcessor = new ParserHtmlTestCaseProcessor(
|
|
$this->validatorFactory->newHtmlValidator()
|
|
);
|
|
|
|
if ( !$parserHtmlTestCaseProcessor->canUse() ) {
|
|
$this->markTestIncomplete(
|
|
'The required resource for the ParserHtmlTestCaseProcessor/HtmlValidator is not available.'
|
|
);
|
|
}
|
|
|
|
foreach ( $testCases as $case ) {
|
|
|
|
if ( $jsonTestCaseFileHandler->requiredToSkipFor( $case, $this->connectorId ) ) {
|
|
continue;
|
|
}
|
|
|
|
$parserHtmlTestCaseProcessor->process( $case );
|
|
}
|
|
}
|
|
|
|
private function doRunSpecialTests( JsonTestCaseFileHandler $jsonTestCaseFileHandler ) {
|
|
$testCases = $jsonTestCaseFileHandler->findTestCasesByType( 'special' );
|
|
|
|
if ( $testCases === [] ) {
|
|
return;
|
|
}
|
|
|
|
$specialPageTestCaseProcessor = new SpecialPageTestCaseProcessor(
|
|
$this->getStore(),
|
|
$this->validatorFactory->newStringValidator()
|
|
);
|
|
|
|
$specialPageTestCaseProcessor->setDebugMode(
|
|
$jsonTestCaseFileHandler->getDebugMode()
|
|
);
|
|
|
|
$specialPageTestCaseProcessor->setTestCaseLocation(
|
|
$this->getTestCaseLocation()
|
|
);
|
|
|
|
foreach ( $testCases as $case ) {
|
|
|
|
if ( $jsonTestCaseFileHandler->requiredToSkipFor( $case, $this->connectorId ) ) {
|
|
continue;
|
|
}
|
|
|
|
$specialPageTestCaseProcessor->process( $case );
|
|
}
|
|
}
|
|
|
|
private function doRunRdfTests( JsonTestCaseFileHandler $jsonTestCaseFileHandler ) {
|
|
$testCases = $jsonTestCaseFileHandler->findTestCasesByType( 'rdf' );
|
|
|
|
if ( $testCases === [] ) {
|
|
return;
|
|
}
|
|
|
|
$rdfTestCaseProcessor = new RdfTestCaseProcessor(
|
|
$this->getStore(),
|
|
$this->validatorFactory->newStringValidator(),
|
|
$this->runnerFactory
|
|
);
|
|
|
|
$rdfTestCaseProcessor->setDebugMode(
|
|
$jsonTestCaseFileHandler->getDebugMode()
|
|
);
|
|
|
|
foreach ( $testCases as $case ) {
|
|
$rdfTestCaseProcessor->process( $case );
|
|
}
|
|
}
|
|
|
|
private function doRunQueryStackTests( JsonTestCaseFileHandler $jsonTestCaseFileHandler ) {
|
|
// Set query parser late to ensure that expected settings are adjusted
|
|
// (language etc.) because the __construct relies on the context language
|
|
$queryParser = ApplicationFactory::getInstance()->getQueryFactory()->newQueryParser();
|
|
|
|
$i = 0;
|
|
$count = 0;
|
|
|
|
$this->doRunQueryTests( $jsonTestCaseFileHandler, $queryParser, $i, $count );
|
|
$this->doRunConceptTests( $jsonTestCaseFileHandler, $queryParser, $i, $count );
|
|
$this->doRunFormatTests( $jsonTestCaseFileHandler, $queryParser, $i, $count );
|
|
|
|
// Avoid tests being marked as risky when all cases were skipped
|
|
if ( $i == 0 && $count > 0 ) {
|
|
$this->markTestSkipped( 'Skipped all assertions for: ' . $this->getName() );
|
|
}
|
|
}
|
|
|
|
private function doRunQueryTests( $jsonTestCaseFileHandler, $queryParser, &$i, &$count ) {
|
|
$testCases = $jsonTestCaseFileHandler->findTestCasesByType( 'query' );
|
|
$count += count( $testCases );
|
|
|
|
if ( $testCases === [] ) {
|
|
return;
|
|
}
|
|
|
|
$queryTestCaseProcessor = new QueryTestCaseProcessor(
|
|
$this->getStore(),
|
|
$this->validatorFactory->newQueryResultValidator(),
|
|
$this->validatorFactory->newStringValidator(),
|
|
$this->validatorFactory->newNumberValidator()
|
|
);
|
|
|
|
$queryTestCaseProcessor->setQueryParser(
|
|
$queryParser
|
|
);
|
|
|
|
$queryTestCaseProcessor->setDebugMode(
|
|
$jsonTestCaseFileHandler->getDebugMode()
|
|
);
|
|
|
|
foreach ( $testCases as $case ) {
|
|
|
|
if ( $jsonTestCaseFileHandler->requiredToSkipFor( $case, $this->connectorId ) ) {
|
|
continue;
|
|
}
|
|
|
|
$queryTestCaseProcessor->processQueryCase( new QueryTestCaseInterpreter( $case ) );
|
|
$i++;
|
|
}
|
|
}
|
|
|
|
private function doRunConceptTests( $jsonTestCaseFileHandler, $queryParser, &$i, &$count ) {
|
|
$testCases = $jsonTestCaseFileHandler->findTestCasesByType( 'concept' );
|
|
$count += count( $testCases );
|
|
|
|
if ( $testCases === [] ) {
|
|
return;
|
|
}
|
|
|
|
$queryTestCaseProcessor = new QueryTestCaseProcessor(
|
|
$this->getStore(),
|
|
$this->validatorFactory->newQueryResultValidator(),
|
|
$this->validatorFactory->newStringValidator(),
|
|
$this->validatorFactory->newNumberValidator()
|
|
);
|
|
|
|
$queryTestCaseProcessor->setQueryParser(
|
|
$queryParser
|
|
);
|
|
|
|
$queryTestCaseProcessor->setDebugMode(
|
|
$jsonTestCaseFileHandler->getDebugMode()
|
|
);
|
|
|
|
foreach ( $testCases as $case ) {
|
|
$queryTestCaseProcessor->processConceptCase( new QueryTestCaseInterpreter( $case ) );
|
|
$i++;
|
|
}
|
|
}
|
|
|
|
private function doRunFormatTests( $jsonTestCaseFileHandler, $queryParser, &$i, &$count ) {
|
|
$testCases = $jsonTestCaseFileHandler->findTestCasesByType( 'format' );
|
|
$count += count( $testCases );
|
|
|
|
if ( $testCases === [] ) {
|
|
return;
|
|
}
|
|
|
|
$queryTestCaseProcessor = new QueryTestCaseProcessor(
|
|
$this->getStore(),
|
|
$this->validatorFactory->newQueryResultValidator(),
|
|
$this->validatorFactory->newStringValidator(),
|
|
$this->validatorFactory->newNumberValidator()
|
|
);
|
|
|
|
$queryTestCaseProcessor->setQueryParser(
|
|
$queryParser
|
|
);
|
|
|
|
$queryTestCaseProcessor->setDebugMode(
|
|
$jsonTestCaseFileHandler->getDebugMode()
|
|
);
|
|
|
|
foreach ( $testCases as $case ) {
|
|
|
|
if ( $jsonTestCaseFileHandler->requiredToSkipFor( $case, $this->connectorId ) ) {
|
|
continue;
|
|
}
|
|
|
|
$queryTestCaseProcessor->processFormatCase( new QueryTestCaseInterpreter( $case ) );
|
|
$i++;
|
|
}
|
|
}
|
|
|
|
private function doRunApiTests( JsonTestCaseFileHandler $jsonTestCaseFileHandler ) {
|
|
$testCases = $jsonTestCaseFileHandler->findTestCasesByType( 'api' );
|
|
|
|
if ( $testCases === [] ) {
|
|
return;
|
|
}
|
|
|
|
$apiTestCaseProcessor = new ApiTestCaseProcessor(
|
|
$this->apiFactory,
|
|
$this->validatorFactory->newStringValidator()
|
|
);
|
|
|
|
$apiTestCaseProcessor->setTestCaseLocation(
|
|
$this->getTestCaseLocation()
|
|
);
|
|
|
|
foreach ( $testCases as $case ) {
|
|
|
|
if ( $jsonTestCaseFileHandler->requiredToSkipFor( $case, $this->connectorId ) ) {
|
|
continue;
|
|
}
|
|
|
|
$apiTestCaseProcessor->process( $case );
|
|
}
|
|
}
|
|
|
|
}
|