gplx/mediawiki/extensions/SemanticMediaWiki/tests/phpunit/ParserFunctions/AskParserFunctionTest.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;
}
}