gplx/mediawiki/extensions/SemanticMediaWiki/tests/phpunit/JSONScriptServicesTestCaseR...

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 );
}
}
}