576 lines
14 KiB
PHP
576 lines
14 KiB
PHP
<?php
|
|
|
|
namespace SMW\Tests\ParserFunctions;
|
|
|
|
use MediaWiki\MediaWikiServices;
|
|
use MediaWiki\Parser\ParserOutput;
|
|
use ReflectionClass;
|
|
use SMW\Localizer\Localizer;
|
|
use SMW\ParserFunctions\AskParserFunction;
|
|
use SMW\Services\ServicesFactory as ApplicationFactory;
|
|
use SMW\Tests\PHPUnitCompat;
|
|
use SMW\Tests\TestEnvironment;
|
|
|
|
/**
|
|
* @covers \SMW\ParserFunctions\AskParserFunction
|
|
* @group semantic-mediawiki
|
|
*
|
|
* @license GPL-2.0-or-later
|
|
* @since 1.9
|
|
*
|
|
* @author mwjames
|
|
*/
|
|
class AskParserFunctionTest extends \PHPUnit\Framework\TestCase {
|
|
|
|
use PHPUnitCompat;
|
|
|
|
private $testEnvironment;
|
|
private $semanticDataValidator;
|
|
private $messageFormatter;
|
|
private $circularReferenceGuard;
|
|
private $expensiveFuncExecutionWatcher;
|
|
|
|
protected function setUp(): void {
|
|
parent::setUp();
|
|
|
|
$this->testEnvironment = new TestEnvironment();
|
|
$this->semanticDataValidator = $this->testEnvironment->getUtilityFactory()->newValidatorFactory()->newSemanticDataValidator();
|
|
|
|
$this->testEnvironment->addConfiguration( 'smwgQueryProfiler', true );
|
|
$this->testEnvironment->addConfiguration( 'smwgQMaxLimit', 1000 );
|
|
|
|
$this->messageFormatter = $this->getMockBuilder( '\SMW\MessageFormatter' )
|
|
->disableOriginalConstructor()
|
|
->getMock();
|
|
|
|
$this->circularReferenceGuard = $this->getMockBuilder( '\SMW\Utils\CircularReferenceGuard' )
|
|
->disableOriginalConstructor()
|
|
->getMock();
|
|
|
|
$this->expensiveFuncExecutionWatcher = $this->getMockBuilder( '\SMW\ParserFunctions\ExpensiveFuncExecutionWatcher' )
|
|
->disableOriginalConstructor()
|
|
->getMock();
|
|
|
|
$this->expensiveFuncExecutionWatcher->expects( $this->any() )
|
|
->method( 'hasReachedExpensiveLimit' )
|
|
->willReturn( false );
|
|
|
|
$queryResult = $this->getMockBuilder( '\SMW\Query\QueryResult' )
|
|
->disableOriginalConstructor()
|
|
->getMock();
|
|
|
|
$queryResult->expects( $this->any() )
|
|
->method( 'getErrors' )
|
|
->willReturn( [] );
|
|
|
|
$store = $this->getMockBuilder( '\SMW\Store' )
|
|
->disableOriginalConstructor()
|
|
->getMockForAbstractClass();
|
|
|
|
$store->expects( $this->any() )
|
|
->method( 'getQueryResult' )
|
|
->willReturn( $queryResult );
|
|
|
|
$this->testEnvironment->registerObject( 'Store', $store );
|
|
}
|
|
|
|
protected function tearDown(): void {
|
|
$this->testEnvironment->tearDown();
|
|
parent::tearDown();
|
|
}
|
|
|
|
public function testCanConstruct() {
|
|
$parserData = $this->getMockBuilder( '\SMW\ParserData' )
|
|
->disableOriginalConstructor()
|
|
->getMock();
|
|
|
|
$this->assertInstanceOf(
|
|
'\SMW\ParserFunctions\AskParserFunction',
|
|
new AskParserFunction( $parserData, $this->messageFormatter, $this->circularReferenceGuard, $this->expensiveFuncExecutionWatcher )
|
|
);
|
|
}
|
|
|
|
/**
|
|
* @dataProvider queryDataProvider
|
|
*/
|
|
public function testParse( array $params ) {
|
|
$parserData = ApplicationFactory::getInstance()->newParserData(
|
|
MediaWikiServices::getInstance()->getTitleFactory()->newFromText( __METHOD__ ),
|
|
new ParserOutput()
|
|
);
|
|
|
|
$instance = new AskParserFunction(
|
|
$parserData,
|
|
$this->messageFormatter,
|
|
$this->circularReferenceGuard,
|
|
$this->expensiveFuncExecutionWatcher
|
|
);
|
|
|
|
$this->assertIsString(
|
|
|
|
$instance->parse( $params )
|
|
);
|
|
}
|
|
|
|
public function testIsQueryDisabled() {
|
|
$parserData = $this->getMockBuilder( '\SMW\ParserData' )
|
|
->disableOriginalConstructor()
|
|
->getMock();
|
|
|
|
$this->messageFormatter->expects( $this->any() )
|
|
->method( 'addFromKey' )
|
|
->willReturnSelf();
|
|
|
|
$this->messageFormatter->expects( $this->once() )
|
|
->method( 'getHtml' );
|
|
|
|
$instance = new AskParserFunction(
|
|
$parserData,
|
|
$this->messageFormatter,
|
|
$this->circularReferenceGuard,
|
|
$this->expensiveFuncExecutionWatcher
|
|
);
|
|
|
|
$instance->isQueryDisabled();
|
|
}
|
|
|
|
public function testHasReachedExpensiveLimit() {
|
|
$params = [
|
|
'[[Modification date::+]]',
|
|
'?Modification date',
|
|
'format=list'
|
|
];
|
|
|
|
$parserData = ApplicationFactory::getInstance()->newParserData(
|
|
MediaWikiServices::getInstance()->getTitleFactory()->newFromText( __METHOD__ ),
|
|
new ParserOutput()
|
|
);
|
|
|
|
$expensiveFuncExecutionWatcher = $this->getMockBuilder( '\SMW\ParserFunctions\ExpensiveFuncExecutionWatcher' )
|
|
->disableOriginalConstructor()
|
|
->getMock();
|
|
|
|
$expensiveFuncExecutionWatcher->expects( $this->any() )
|
|
->method( 'hasReachedExpensiveLimit' )
|
|
->willReturn( true );
|
|
|
|
$this->messageFormatter->expects( $this->any() )
|
|
->method( 'addFromKey' )
|
|
->willReturnSelf();
|
|
|
|
$this->messageFormatter->expects( $this->once() )
|
|
->method( 'getHtml' );
|
|
|
|
$instance = new AskParserFunction(
|
|
$parserData,
|
|
$this->messageFormatter,
|
|
$this->circularReferenceGuard,
|
|
$expensiveFuncExecutionWatcher
|
|
);
|
|
|
|
$instance->parse( $params );
|
|
}
|
|
|
|
public function testSetShowMode() {
|
|
$parserData = $this->getMockBuilder( '\SMW\ParserData' )
|
|
->disableOriginalConstructor()
|
|
->getMock();
|
|
|
|
$instance = new AskParserFunction(
|
|
$parserData,
|
|
$this->messageFormatter,
|
|
$this->circularReferenceGuard,
|
|
$this->expensiveFuncExecutionWatcher
|
|
);
|
|
|
|
$reflector = new ReflectionClass( '\SMW\ParserFunctions\AskParserFunction' );
|
|
$showMode = $reflector->getProperty( 'showMode' );
|
|
$showMode->setAccessible( true );
|
|
|
|
$this->assertFalse( $showMode->getValue( $instance ) );
|
|
$instance->setShowMode( true );
|
|
|
|
$this->assertTrue( $showMode->getValue( $instance ) );
|
|
}
|
|
|
|
public function testCircularGuard() {
|
|
$parserData = ApplicationFactory::getInstance()->newParserData(
|
|
MediaWikiServices::getInstance()->getTitleFactory()->newFromText( __METHOD__ ),
|
|
new ParserOutput()
|
|
);
|
|
|
|
$this->circularReferenceGuard->expects( $this->once() )
|
|
->method( 'mark' );
|
|
|
|
$this->circularReferenceGuard->expects( $this->never() )
|
|
->method( 'unmark' );
|
|
|
|
$this->circularReferenceGuard->expects( $this->once() )
|
|
->method( 'isCircular' )
|
|
->willReturn( true );
|
|
|
|
$instance = new AskParserFunction(
|
|
$parserData,
|
|
$this->messageFormatter,
|
|
$this->circularReferenceGuard,
|
|
$this->expensiveFuncExecutionWatcher
|
|
);
|
|
|
|
$params = [];
|
|
|
|
$this->assertEmpty(
|
|
$instance->parse( $params )
|
|
);
|
|
}
|
|
|
|
public function testQueryIdStabilityForFixedSetOfParametersWithFingerprintMethod() {
|
|
$parserData = ApplicationFactory::getInstance()->newParserData(
|
|
MediaWikiServices::getInstance()->getTitleFactory()->newFromText( __METHOD__ ),
|
|
new ParserOutput()
|
|
);
|
|
|
|
$instance = new AskParserFunction(
|
|
$parserData,
|
|
$this->messageFormatter,
|
|
$this->circularReferenceGuard,
|
|
$this->expensiveFuncExecutionWatcher
|
|
);
|
|
|
|
$params = [
|
|
'[[Modification date::+]]',
|
|
'?Modification date',
|
|
'format=list'
|
|
];
|
|
|
|
$instance->parse( $params );
|
|
|
|
$this->assertTrue(
|
|
$parserData->getSemanticData()->hasSubSemanticData( '_QUERYaa38249db4bc6d3e8133588fb08d0f0d' )
|
|
);
|
|
|
|
// Limit is a factor that influences the query id, count uses the
|
|
// max limit available in $GLOBALS['smwgQMaxLimit'] therefore we set
|
|
// the limit to make the test independent from possible other settings
|
|
|
|
$params = [
|
|
'[[Modification date::+]]',
|
|
'?Modification date',
|
|
'format=count'
|
|
];
|
|
|
|
$instance->parse( $params );
|
|
|
|
$this->assertTrue(
|
|
$parserData->getSemanticData()->hasSubSemanticData( '_QUERYb6a190747f7d3c2775730f6bc6c5e469' )
|
|
);
|
|
}
|
|
|
|
/**
|
|
* @dataProvider queryDataProvider
|
|
*/
|
|
public function testInstantiatedQueryData( array $params, array $expected, array $settings ) {
|
|
foreach ( $settings as $key => $value ) {
|
|
$this->testEnvironment->addConfiguration( $key, $value );
|
|
}
|
|
|
|
$parserData = ApplicationFactory::getInstance()->newParserData(
|
|
MediaWikiServices::getInstance()->getTitleFactory()->newFromText( __METHOD__ ),
|
|
new ParserOutput()
|
|
);
|
|
|
|
$instance = new AskParserFunction(
|
|
$parserData,
|
|
$this->messageFormatter,
|
|
$this->circularReferenceGuard,
|
|
$this->expensiveFuncExecutionWatcher
|
|
);
|
|
|
|
$instance->parse( $params );
|
|
|
|
foreach ( $parserData->getSemanticData()->getSubSemanticData() as $containerSemanticData ) {
|
|
$this->assertInstanceOf( '\SMW\DataModel\ContainerSemanticData', $containerSemanticData );
|
|
|
|
$this->semanticDataValidator->assertThatPropertiesAreSet(
|
|
$expected,
|
|
$containerSemanticData
|
|
);
|
|
}
|
|
}
|
|
|
|
public function testEmbeddedQueryWithError() {
|
|
$params = [
|
|
'[[--ABC·|DEF::123]]',
|
|
'format=table'
|
|
];
|
|
|
|
$expected = [
|
|
'propertyCount' => 2,
|
|
'propertyKeys' => [ '_ASK', '_ERRC' ],
|
|
];
|
|
|
|
$parserData = ApplicationFactory::getInstance()->newParserData(
|
|
MediaWikiServices::getInstance()->getTitleFactory()->newFromText( __METHOD__ ),
|
|
new ParserOutput()
|
|
);
|
|
|
|
$instance = new AskParserFunction(
|
|
$parserData,
|
|
$this->messageFormatter,
|
|
$this->circularReferenceGuard,
|
|
$this->expensiveFuncExecutionWatcher
|
|
);
|
|
|
|
$instance->parse( $params );
|
|
|
|
$this->semanticDataValidator->assertThatPropertiesAreSet(
|
|
$expected,
|
|
$parserData->getSemanticData()
|
|
);
|
|
}
|
|
|
|
public function testWithDisabledQueryProfiler() {
|
|
$params = [
|
|
'[[Modification date::+]]',
|
|
'format=table'
|
|
];
|
|
|
|
$expected = [
|
|
'propertyCount' => 0
|
|
];
|
|
|
|
$this->testEnvironment->addConfiguration( 'smwgQueryProfiler', false );
|
|
|
|
$parserData = ApplicationFactory::getInstance()->newParserData(
|
|
MediaWikiServices::getInstance()->getTitleFactory()->newFromText( __METHOD__ ),
|
|
new ParserOutput()
|
|
);
|
|
|
|
$instance = new AskParserFunction(
|
|
$parserData,
|
|
$this->messageFormatter,
|
|
$this->circularReferenceGuard,
|
|
$this->expensiveFuncExecutionWatcher
|
|
);
|
|
|
|
$instance->parse( $params );
|
|
|
|
$this->semanticDataValidator->assertThatPropertiesAreSet(
|
|
$expected,
|
|
$parserData->getSemanticData()
|
|
);
|
|
}
|
|
|
|
public function testNoQueryProfileOnSpecialPages() {
|
|
$params = [
|
|
'[[Modification date::+]]',
|
|
'format=table'
|
|
];
|
|
|
|
$expected = [
|
|
'propertyCount' => 0
|
|
];
|
|
|
|
$this->testEnvironment->addConfiguration( 'smwgQueryProfiler', true );
|
|
|
|
$parserData = ApplicationFactory::getInstance()->newParserData(
|
|
MediaWikiServices::getInstance()->getTitleFactory()->newFromText( __METHOD__, NS_SPECIAL ),
|
|
new ParserOutput()
|
|
);
|
|
|
|
$instance = new AskParserFunction(
|
|
$parserData,
|
|
$this->messageFormatter,
|
|
$this->circularReferenceGuard,
|
|
$this->expensiveFuncExecutionWatcher
|
|
);
|
|
|
|
$instance->parse( $params );
|
|
|
|
$this->semanticDataValidator->assertThatPropertiesAreSet(
|
|
$expected,
|
|
$parserData->getSemanticData()
|
|
);
|
|
}
|
|
|
|
public function testQueryWithAnnotationMarker() {
|
|
$params = [
|
|
'[[Modification date::+]]',
|
|
'format=table',
|
|
'@annotation'
|
|
];
|
|
|
|
$postProcHandler = $this->getMockBuilder( '\SMW\PostProcHandler' )
|
|
->disableOriginalConstructor()
|
|
->getMock();
|
|
|
|
$postProcHandler->expects( $this->once() )
|
|
->method( 'addUpdate' );
|
|
|
|
$parserData = ApplicationFactory::getInstance()->newParserData(
|
|
MediaWikiServices::getInstance()->getTitleFactory()->newFromText( __METHOD__ ),
|
|
new ParserOutput()
|
|
);
|
|
|
|
$instance = new AskParserFunction(
|
|
$parserData,
|
|
$this->messageFormatter,
|
|
$this->circularReferenceGuard,
|
|
$this->expensiveFuncExecutionWatcher
|
|
);
|
|
|
|
$instance->setPostProcHandler( $postProcHandler );
|
|
$instance->parse( $params );
|
|
}
|
|
|
|
public function queryDataProvider() {
|
|
$categoryNS = Localizer::getInstance()->getNsText( NS_CATEGORY );
|
|
$fileNS = Localizer::getInstance()->getNsText( NS_FILE );
|
|
|
|
$provider = [];
|
|
|
|
// #0
|
|
// {{#ask: [[Modification date::+]]
|
|
// |?Modification date
|
|
// |format=list
|
|
// }}
|
|
$provider[] = [
|
|
[
|
|
'[[Modification date::+]]',
|
|
'?Modification date',
|
|
'format=list'
|
|
],
|
|
[
|
|
'propertyCount' => 4,
|
|
'propertyKeys' => [ '_ASKST', '_ASKSI', '_ASKDE', '_ASKFO' ],
|
|
'propertyValues' => [ 'list', 1, 1, '[[Modification date::+]]' ]
|
|
],
|
|
[
|
|
'smwgQueryProfiler' => true
|
|
]
|
|
];
|
|
|
|
// #1 Query string with spaces
|
|
// {{#ask: [[Modification date::+]] [[Category:Foo bar]] [[Has title::!Foo bar]]
|
|
// |?Modification date
|
|
// |?Has title
|
|
// |format=list
|
|
// }}
|
|
$provider[] = [
|
|
[
|
|
'[[Modification date::+]] [[Category:Foo bar]] [[Has title::!Foo bar]]',
|
|
'?Modification date',
|
|
'?Has title',
|
|
'format=list'
|
|
],
|
|
[
|
|
'propertyCount' => 4,
|
|
'propertyKeys' => [ '_ASKST', '_ASKSI', '_ASKDE', '_ASKFO' ],
|
|
'propertyValues' => [ 'list', 4, 1, "[[Modification date::+]] [[$categoryNS:Foo bar]] [[Has title::!Foo bar]]" ]
|
|
],
|
|
[
|
|
'smwgCreateProtectionRight' => false,
|
|
'smwgQueryProfiler' => true
|
|
]
|
|
];
|
|
|
|
// #2
|
|
// {{#ask: [[Modification date::+]][[Category:Foo]]
|
|
// |?Modification date
|
|
// |?Has title
|
|
// |format=list
|
|
// }}
|
|
$provider[] = [
|
|
[
|
|
'[[Modification date::+]][[Category:Foo]]',
|
|
'?Modification date',
|
|
'?Has title',
|
|
'format=list'
|
|
],
|
|
[
|
|
'propertyCount' => 4,
|
|
'propertyKeys' => [ '_ASKST', '_ASKSI', '_ASKDE', '_ASKFO' ],
|
|
'propertyValues' => [ 'list', 2, 1, "[[Modification date::+]] [[$categoryNS:Foo]]" ]
|
|
],
|
|
[
|
|
'smwgQueryProfiler' => true
|
|
]
|
|
];
|
|
|
|
// #3 Known format
|
|
// {{#ask: [[File:Fooo]]
|
|
// |?Modification date
|
|
// |default=no results
|
|
// |format=feed
|
|
// }}
|
|
$provider[] = [
|
|
[
|
|
'[[File:Fooo]]',
|
|
'?Modification date',
|
|
'default=no results',
|
|
'format=feed'
|
|
],
|
|
[
|
|
'propertyCount' => 4,
|
|
'propertyKeys' => [ '_ASKST', '_ASKSI', '_ASKDE', '_ASKFO' ],
|
|
'propertyValues' => [ 'feed', 1, 0, "[[:$fileNS:Fooo]]" ]
|
|
],
|
|
[
|
|
'smwgQueryProfiler' => true
|
|
]
|
|
];
|
|
|
|
// #4 Unknown format, default table
|
|
// {{#ask: [[Modification date::+]][[Category:Foo]]
|
|
// |?Modification date
|
|
// |?Has title
|
|
// |format=bar
|
|
// }}
|
|
$provider[] = [
|
|
[
|
|
'[[Modification date::+]][[Category:Foo]]',
|
|
'?Modification date',
|
|
'?Has title',
|
|
'format=lula'
|
|
],
|
|
[
|
|
'propertyCount' => 4,
|
|
'propertyKeys' => [ '_ASKST', '_ASKSI', '_ASKDE', '_ASKFO' ],
|
|
'propertyValues' => [ 'table', 2, 1, "[[Modification date::+]] [[$categoryNS:Foo]]" ]
|
|
],
|
|
[
|
|
'smwgQueryProfiler' => true
|
|
]
|
|
];
|
|
|
|
// #6 Invalid parameters
|
|
// {{#ask: [[Modification date::+]]
|
|
// |?Modification date
|
|
// |format=list
|
|
// |someParameterWithoutValue
|
|
// |{{{template}}}
|
|
// |@internal
|
|
// }}
|
|
$provider[] = [
|
|
[
|
|
'[[Modification date::+]]',
|
|
'someParameterWithoutValue',
|
|
'{{{template}}}',
|
|
'format=list',
|
|
'@internal',
|
|
'?Modification date'
|
|
],
|
|
[
|
|
'propertyCount' => 5,
|
|
'propertyKeys' => [ '_ASKST', '_ASKSI', '_ASKDE', '_ASKFO', '_ASKPA' ],
|
|
'propertyValues' => [ 'list', 1, 1, '[[Modification date::+]]', '{"limit":50,"offset":0,"sort":[""],"order":["asc"],"mode":1}' ]
|
|
],
|
|
[
|
|
'smwgQueryProfiler' => SMW_QPRFL_PARAMS
|
|
]
|
|
];
|
|
|
|
return $provider;
|
|
}
|
|
|
|
}
|