gplx/mediawiki/extensions/SemanticMediaWiki/tests/phpunit/ParserFunctions/SubobjectParserFunctionTest...

724 lines
16 KiB
PHP

<?php
namespace SMW\Tests\ParserFunctions;
use MediaWiki\MediaWikiServices;
use MediaWiki\Parser\ParserOutput;
use SMW\DIProperty;
use SMW\Localizer\Localizer;
use SMW\MessageFormatter;
use SMW\ParserData;
use SMW\ParserFunctions\SubobjectParserFunction;
use SMW\ParserParameterProcessor;
use SMW\Subobject;
use SMW\Tests\PHPUnitCompat;
use SMW\Tests\Utils\UtilityFactory;
/**
* @covers \SMW\ParserFunctions\SubobjectParserFunction
* @group semantic-mediawiki
*
* @license GPL-2.0-or-later
* @since 1.9
*
* @author mwjames
*/
class SubobjectParserFunctionTest extends \PHPUnit\Framework\TestCase {
use PHPUnitCompat;
private $semanticDataValidator;
protected function setUp(): void {
parent::setUp();
$this->semanticDataValidator = UtilityFactory::getInstance()->newValidatorFactory()->newSemanticDataValidator();
}
public function testCanConstruct() {
$subobject = new Subobject( MediaWikiServices::getInstance()->getTitleFactory()->newFromText( __METHOD__ ) );
$parserData = $this->getMockBuilder( '\SMW\ParserData' )
->disableOriginalConstructor()
->getMock();
$messageFormatter = $this->getMockBuilder( '\SMW\MessageFormatter' )
->disableOriginalConstructor()
->getMock();
$this->assertInstanceOf(
'\SMW\ParserFunctions\SubobjectParserFunction',
new SubobjectParserFunction(
$parserData,
$subobject,
$messageFormatter
)
);
}
/**
* @dataProvider parameterDataProvider
*/
public function testParse( array $parameters, array $expected ) {
$subobject = new Subobject( MediaWikiServices::getInstance()->getTitleFactory()->newFromText( __METHOD__ ) );
$instance = $this->acquireInstance( $subobject );
$result = $instance->parse( new ParserParameterProcessor( $parameters ) );
$this->assertIsString(
$result
);
}
/**
* @dataProvider parameterDataProvider
*/
public function testInstantiatedSubobject( array $parameters, array $expected ) {
$subobject = new Subobject( MediaWikiServices::getInstance()->getTitleFactory()->newFromText( __METHOD__ ) );
$instance = $this->acquireInstance( $subobject );
$instance->parse( new ParserParameterProcessor( $parameters ) );
$this->assertContains(
$expected['identifier'],
$subobject->getSubobjectId()
);
}
/**
* @dataProvider firstElementDataProvider
*/
public function testFirstElementAsPropertyLabel( $isEnabled, array $parameters, array $expected ) {
$parserOutput = new ParserOutput();
$title = MediaWikiServices::getInstance()->getTitleFactory()->newFromText( __METHOD__ );
$subobject = new Subobject( $title );
$instance = $this->acquireInstance( $subobject, $parserOutput );
$instance->useFirstElementAsPropertyLabel( $isEnabled );
$instance->parse( new ParserParameterProcessor( $parameters ) );
// If it is enabled only check for the first character {0} that should
// contain '_' as the rest is going to be an unknown hash value
$id = $subobject->getSubobjectId();
$this->assertEquals( $expected['identifier'], $isEnabled ? $id[0] : $id );
$parserData = new ParserData( $title, $parserOutput );
// Add generated title text as property value due to the auto reference
// setting
$expected['propertyValues'][] = $title->getText();
foreach ( $parserData->getSemanticData()->getSubSemanticData() as $containerSemanticData ) {
$this->semanticDataValidator->assertThatPropertiesAreSet(
$expected,
$containerSemanticData
);
}
}
/**
* @dataProvider parameterDataProvider
*/
public function testInstantiatedPropertyValues( array $parameters, array $expected ) {
$this->setupInstanceAndAssertSemanticData(
$parameters,
$expected
);
}
/**
* @dataProvider tokuFixedParameterProvider
*/
public function testSortKeyAnnotation( array $parameters, array $expected ) {
$this->setupInstanceAndAssertSemanticData(
$parameters,
$expected
);
}
public function testSubobjectIdStabilityForFixedSetOfParameters() {
$parameters = [
'Foo=Bar'
];
$subobject = new Subobject( MediaWikiServices::getInstance()->getTitleFactory()->newFromText( __METHOD__ ) );
$instance = $this->acquireInstance( $subobject );
$instance->parse( new ParserParameterProcessor( $parameters ) );
// Expected to be stable for PHP as well
$this->assertEquals(
'_be96d37a4d7c35be8673cb4229b8fdec',
$subobject->getSubobjectId()
);
}
public function testParametersOnBeingSorted() {
$parameters = [
'Foo=Foobar, Bar',
'+sep=,'
];
$subobject = new Subobject( MediaWikiServices::getInstance()->getTitleFactory()->newFromText( __METHOD__ ) );
$instance = $this->acquireInstance( $subobject );
$instance->isComparableContent(
true
);
$instance->parse(
new ParserParameterProcessor( $parameters )
);
$this->assertEquals(
'_c0bea380739b21578cdcf28ed5d4cfd3',
$subobject->getSubobjectId()
);
}
public function testParametersOnBeingSortedWithRevertedValueOrderProducesSameHash() {
$parameters = [
'Foo=Bar, Foobar',
'+sep=,'
];
$subobject = new Subobject( MediaWikiServices::getInstance()->getTitleFactory()->newFromText( __METHOD__ ) );
$instance = $this->acquireInstance( $subobject );
$instance->isComparableContent(
true
);
$instance->parse(
new ParserParameterProcessor( $parameters )
);
$this->assertEquals(
'_c0bea380739b21578cdcf28ed5d4cfd3',
$subobject->getSubobjectId()
);
}
public function testParametersIsNotSorted() {
$parameters = [
'Foo=Foobar, Bar',
'+sep=,'
];
$subobject = new Subobject( MediaWikiServices::getInstance()->getTitleFactory()->newFromText( __METHOD__ ) );
$instance = $this->acquireInstance( $subobject );
$instance->isComparableContent(
false
);
$instance->parse(
new ParserParameterProcessor( $parameters )
);
// Expected to be stable for PHP as well
$this->assertEquals(
'_ec7323184d89fe1409b5cfaf09950a95',
$subobject->getSubobjectId()
);
}
public function testCreateSameIdForNormalizedParametersWithEnabledCapitalLinks() {
$title = MediaWikiServices::getInstance()->getTitleFactory()->newFromText( __METHOD__ );
$parametersOne = [
'Foo=Bar',
'Has foo=bar,Foo',
'+sep=,'
];
$parametersTwo = [
'foo=Bar',
'has foo=Foo,bar',
'+sep=,'
];
$subobject = new Subobject( $title );
$instance = $this->acquireInstance( $subobject );
$instance->isComparableContent( true );
$instance->isCapitalLinks( true );
$instance->parse(
new ParserParameterProcessor( $parametersOne )
);
$id = $subobject->getSubobjectId();
$instance->parse(
new ParserParameterProcessor( $parametersTwo )
);
$this->assertEquals(
$id,
$subobject->getSubobjectId()
);
}
public function testRestrictionOnTooShortFirstPartWhenDotIsUsedForUserNamedSubobject() {
// #1299, #1302
// Has dot restriction
// {{#subobject:foo.bar
// |Date=Foo
// }}
$parameters = [
'foo.bar',
'Date=Foo'
];
$subobject = new Subobject( MediaWikiServices::getInstance()->getTitleFactory()->newFromText( __METHOD__ ) );
$instance = $this->acquireInstance( $subobject );
$instance->parse( new ParserParameterProcessor( $parameters ) );
$this->expectException( '\SMW\Exception\SubSemanticDataException' );
$subobject->getSubobjectId();
}
protected function setupInstanceAndAssertSemanticData( array $parameters, array $expected ) {
$title = isset( $expected['embeddedTitle'] ) ? $expected['embeddedTitle'] : __METHOD__;
$parserOutput = new ParserOutput();
$title = MediaWikiServices::getInstance()->getTitleFactory()->newFromText( $title );
$subobject = new Subobject( $title );
$instance = $this->acquireInstance(
$subobject,
$parserOutput
);
$instance->parse( new ParserParameterProcessor( $parameters ) );
$parserData = new ParserData(
$title,
$parserOutput
);
$subSemanticData = $parserData->getSemanticData()->getSubSemanticData();
if ( $expected['propertyCount'] == 0 ) {
$this->assertEmpty(
$subSemanticData
);
}
foreach ( $subSemanticData as $key => $semanticData ) {
if ( strpos( $semanticData->getSubject()->getSubobjectName(), '_ERR' ) !== false ) {
continue;
}
$this->semanticDataValidator->assertThatPropertiesAreSet(
$expected,
$semanticData
);
}
}
public function parameterDataProvider() {
$helpNS = Localizer::getInstance()->getNsText( NS_HELP );
$provider = [];
# 0 Anonymous identifier
// {{#subobject:
// |Foo=bar
// }}
$provider[] = [
[ '', 'Foo=bar' ],
[
'hasErrors' => false,
'identifier' => '_',
'propertyCount' => 1,
'propertyLabels' => 'Foo',
'propertyValues' => 'Bar'
]
];
# 1 Anonymous identifier
// {{#subobject:-
// |Foo=1001 9009
// }}
$provider[] = [
[ '-', 'Foo=1001 9009' ],
[
'hasErrors' => false,
'identifier' => '_',
'propertyCount' => 1,
'propertyLabels' => 'Foo',
'propertyValues' => '1001 9009'
]
];
# 2 Named identifier
// {{#subobject:FooBar
// |FooBar=Bar foo
// }}
$provider[] = [
[ 'FooBar', 'FooBar=Bar foo' ],
[
'hasErrors' => false,
'identifier' => 'FooBar',
'propertyCount' => 1,
'propertyLabels' => 'FooBar',
'propertyValues' => 'Bar foo'
]
];
# 3 Named identifier
// {{#subobject:Foo bar
// |Foo=Help:Bar
// }}
$provider[] = [
[ 'Foo bar', 'Foo=Help:Bar' ],
[
'hasErrors' => false,
'identifier' => 'Foo_bar',
'propertyCount' => 1,
'propertyLabels' => 'Foo',
'propertyValues' => "$helpNS:Bar"
]
];
# 4 Named identifier
// {{#subobject: Foo bar foo
// |Bar=foo Bar
// }}
$provider[] = [
[ ' Foo bar foo ', 'Bar=foo Bar' ],
[
'hasErrors' => false,
'identifier' => 'Foo_bar_foo',
'propertyCount' => 1,
'propertyLabels' => 'Bar',
'propertyValues' => 'Foo Bar'
]
];
# 5 Named identifier
// {{#subobject: Foo bar foo
// |状況=超やばい
// |Bar=http://www.semantic-mediawiki.org/w/index.php?title=Subobject
// }}
$provider[] = [
[
' Foo bar foo ',
'状況=超やばい',
'Bar=http://www.semantic-mediawiki.org/w/index.php?title=Subobject' ],
[
'hasErrors' => false,
'identifier' => 'Foo_bar_foo',
'propertyCount' => 2,
'propertyLabels' => [ '状況', 'Bar' ],
'propertyValues' => [ '超やばい', 'Http://www.semantic-mediawiki.org/w/index.php?title=Subobject' ]
]
];
# 6 {{#subobject: Foo bar foo
// |Bar=foo Bar
// |Modification date=foo Bar
// }}
$provider[] = [
[ ' Foo bar foo ', 'Bar=foo Bar', 'Modification date=foo Bar' ],
[
'hasErrors' => true,
'identifier' => 'Foo_bar_foo',
'strictPropertyValueMatch' => false,
'propertyCount' => 2,
'propertyKeys' => [ 'Bar', '_ERRC' ],
'propertyValues' => [ 'Foo Bar' ]
]
];
# 7 {{#subobject: Foo bar foo
// |Bar=foo Bar
// |-Foo=foo Bar
// }}
$provider[] = [
[ ' Foo bar foo ', 'Bar=foo Bar', '-Foo=foo Bar' ],
[
'hasErrors' => true,
'identifier' => 'Foo_bar_foo',
'strictPropertyValueMatch' => false,
'propertyCount' => 2,
'propertyKeys' => [ 'Bar', '_ERRC' ],
'propertyValues' => [ 'Foo Bar' ]
]
];
// An empty subobject is not being classified as valid (to create an object)
#8 {{#subobject: Foo bar foo
// |Bar=foo Bar
// |Modification date=1 Jan 1970
// }}
$provider[] = [
[ ' Foo bar foo ', 'Modification date=1 Jan 1970' ],
[
'hasErrors' => true,
'identifier' => 'Foo_bar_foo',
'strictPropertyValueMatch' => false,
'propertyCount' => 1,
'propertyKeys' => [ '_ERRC' ]
]
];
// Get the right language for an error object
$diPropertyError = new DIProperty( DIProperty::TYPE_ERROR );
# 9 {{#subobject: Foo bar foo
// |Bar=foo Bar
// |Date=Foo
// }}
$provider[] = [
[ ' Foo bar foo ', 'Date=Foo' ],
[
'hasErrors' => true,
'identifier' => 'Foo_bar_foo',
'strictPropertyValueMatch' => false,
'propertyCount' => 1,
'propertyKeys' => [ '_ERRC' ]
]
];
// Not dot restriction
#10 {{#subobject:foobar.bar
// |Bar=foo Bar
// }}
$provider[] = [
[ 'foobar.bar', 'Bar=foo Bar' ],
[
'hasErrors' => false,
'identifier' => 'foobar.bar',
'strictPropertyValueMatch' => false,
'propertyCount' => 1,
'propertyKeys' => [ 'Bar' ],
'propertyValues' => [ 'Foo Bar' ]
]
];
return $provider;
}
public function firstElementDataProvider() {
$provider = [];
// #0 / asserting that a named identifier was turned into an anonymous id
// {{#subobject: Foo bar foo
// |Bar=foo Bar
// }}
$provider[] = [
true,
[ ' Foo bar foo ', 'Bar=foo Bar' ],
[
'hasErrors' => false,
'identifier' => '_',
'propertyCount' => 2,
'propertyLabels' => [ 'Bar', 'Foo bar foo' ],
'propertyValues' => [ 'Foo Bar' ] // additional value is added during runtime
]
];
// #1 / asserting the validity of the named identifier
// {{#subobject: Foo bar foo
// |Bar=foo Bar
// }}
$provider[] = [
false,
[ ' Foo bar foo ', 'Bar=foo Bar' ],
[
'hasErrors' => false,
'identifier' => 'Foo_bar_foo',
'propertyCount' => 1,
'propertyLabels' => [ 'Bar' ],
'propertyValues' => [ 'Foo Bar' ]
]
];
return $provider;
}
public function tokuFixedParameterProvider() {
$provider = [];
// #0 @sortkey
// {{#subobject:
// |Bar=foo Bar
// |@sortkey=9999
// }}
$provider[] = [
[
'Bar=foo Bar',
'@sortkey=9999'
],
[
'propertyCount' => 2,
'properties' => [
new DIProperty( 'Bar' ),
new DIProperty( '_SKEY' )
],
'propertyValues' => [
'Foo Bar',
'9999'
]
]
];
// #1 @sortkey being empty
// {{#subobject:
// |Bar=foo Bar
// |@sortkey=
// }}
$provider[] = [
[
'Bar=foo Bar',
'@sortkey='
],
[
'propertyCount' => 1,
'properties' => [
new DIProperty( 'Bar' )
],
'propertyValues' => [
'Foo Bar'
]
]
];
// #2 @category
// {{#subobject:
// |Bar=foo Bar
// |@category=1001
// }}
$provider[] = [
[
'Bar=foo Bar',
'@category=1001'
],
[
'propertyCount' => 2,
'properties' => [
new DIProperty( 'Bar' ),
new DIProperty( '_INST' )
],
'propertyValues' => [
'Foo Bar',
'Category:1001'
]
]
];
// #3 @category empty
// {{#subobject:
// |Bar=foo Bar
// |@category=
// }}
$provider[] = [
[
'Bar=foo Bar',
'@category='
],
[
'propertyCount' => 1,
'properties' => [
new DIProperty( 'Bar' )
],
'propertyValues' => [
'Foo Bar'
]
]
];
// #4 display title to set sortkey
// {{#subobject:
// |Display title of=Foo
// }}
$provider[] = [
[
'Display title of=Foo'
],
[
'propertyCount' => 2,
'properties' => [
new DIProperty( '_DTITLE' ),
new DIProperty( '_SKEY' )
],
'propertyValues' => [
'Foo'
]
]
];
// #4 display title to set sortkey
// {{#subobject:
// |Display title of=Foo
// |@sortkey=Bar
// }}
$provider[] = [
[
'Display title of=Foo',
'@sortkey=Bar'
],
[
'propertyCount' => 2,
'properties' => [
new DIProperty( '_DTITLE' ),
new DIProperty( '_SKEY' )
],
'propertyValues' => [
'Foo',
'Bar'
]
]
];
// #5 `Bar` auto-linked with `LinkedToSubjectXY`
// {{#subobject:
// |@linkWith=Bar
// }}
$provider[] = [
[
'@linkWith=Bar'
],
[
'embeddedTitle' => 'LinkedToSubjectXY',
'propertyCount' => 1,
'properties' => [
new DIProperty( 'Bar' )
],
'propertyValues' => [
'LinkedToSubjectXY'
]
]
];
return $provider;
}
/**
* @return SubobjectParserFunction
*/
private function acquireInstance( Subobject $subobject, ?ParserOutput $parserOutput = null ) {
if ( $parserOutput === null ) {
$parserOutput = new ParserOutput();
}
return new SubobjectParserFunction(
new ParserData( $subobject->getTitle(), $parserOutput ),
$subobject,
new MessageFormatter( $subobject->getTitle()->getPageLanguage() )
);
}
}