724 lines
16 KiB
PHP
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() )
|
|
);
|
|
}
|
|
|
|
}
|