mediawiki-extensions-Scribunto/tests/phpunit/engines/LuaCommon/LuaEngineTestHelper.php
Kunal Mehta a839ba855d Add mw.loadJsonData()
Backporting this so the LTS release has forwards compatibility with
Wikipedia templates.

mw.loadData() allows for optimizing the loading Lua tables by requiring
only one parse and lookup. However it's often easier for people to
write/maintain bulk data in JSON rather than Lua tables.

mw.loadJsonData() has roughly the same characteristics as mw.loadData()
and it can be used on JSON content model pages in any namespace.

As noted on the linked bug report, it's possible to already implement
this by writing a wrapper Lua module that loads and parses the JSON
content. But that requires a dummy module for each JSON page, which is
just annoying and inconvenient.

Test cases are copied from the mw.loadData() ones, with a few omissions
for syntax not supported in JSON (e.g. NaN, infinity, etc.).

Bug: T217500
Change-Id: I1b35ad27a37b94064707bb8c9b7108c7078ed4d1
(cherry picked from commit 1000d322e5)
2022-11-07 07:34:42 +00:00

209 lines
5.8 KiB
PHP

<?php
use MediaWiki\Extension\Scribunto\Engines\LuaSandbox\LuaSandboxEngine;
use MediaWiki\Extension\Scribunto\Engines\LuaStandalone\LuaStandaloneEngine;
use MediaWiki\Extension\Scribunto\ScribuntoEngineBase;
use MediaWiki\MediaWikiServices;
use PHPUnit\Framework\DataProviderTestSuite;
use PHPUnit\Framework\TestSuite;
use PHPUnit\Framework\WarningTestCase;
use PHPUnit\Util\Test;
/**
* Trait that helps LuaEngineTestBase and LuaEngineUnitTestBase
*/
trait Scribunto_LuaEngineTestHelper {
/** @var array[] */
private static $engineConfigurations = [
'LuaSandbox' => [
'class' => LuaSandboxEngine::class,
'memoryLimit' => 50000000,
'cpuLimit' => 30,
'allowEnvFuncs' => true,
'maxLangCacheSize' => 30,
],
'LuaStandalone' => [
'class' => LuaStandaloneEngine::class,
'errorFile' => null,
'luaPath' => null,
'memoryLimit' => 50000000,
'cpuLimit' => 30,
'allowEnvFuncs' => true,
'maxLangCacheSize' => 30,
],
];
/** @var int[] */
protected $templateLoadCounts = [];
/**
* Create a PHPUnit test suite to run the test against all engines
* @param string $className Test class name
* @param string|null $group Engine to run with, or null to run all engines
* @return TestSuite
*/
protected static function makeSuite( $className, $group = null ) {
$suite = new TestSuite;
$suite->setName( $className );
$class = new ReflectionClass( $className );
foreach ( self::$engineConfigurations as $engineName => $opts ) {
if ( $group !== null && $group !== $engineName ) {
continue;
}
try {
$parser = MediaWikiServices::getInstance()->getParserFactory()->create();
$parser->startExternalParse(
Title::newMainPage(),
ParserOptions::newFromAnon(),
Parser::OT_HTML,
true
);
$engineClass = $opts['class'];
$engine = new $engineClass(
self::$engineConfigurations[$engineName] + [ 'parser' => $parser ]
);
$parser->scribunto_engine = $engine;
$engine->setTitle( $parser->getTitle() );
$engine->getInterpreter();
} catch ( Scribunto_LuaInterpreterNotFoundError $e ) {
$suite->addTest(
new Scribunto_LuaEngineTestSkip(
$className, "interpreter for $engineName is not available"
), [ 'Lua', $engineName ]
);
continue;
}
// Work around PHPUnit breakage: the only straightforward way to
// get the data provider is to call Test::getProvidedData, but that
// instantiates the class without passing any parameters to the
// constructor. But we *need* that engine name.
self::$staticEngineName = $engineName;
$engineSuite = new DataProviderTestSuite;
$engineSuite->setName( "$engineName: $className" );
foreach ( $class->getMethods() as $method ) {
if ( Test::isTestMethod( $method ) && $method->isPublic() ) {
$name = $method->getName();
$groups = Test::getGroups( $className, $name );
$groups[] = 'Lua';
$groups[] = $engineName;
// Only run tests locally if the engine isn't the MW sandbox T125050
if ( $engineName !== 'LuaSandbox' ) {
$groups[] = 'Standalone';
}
$groups = array_unique( $groups );
$data = Test::getProvidedData( $className, $name );
if ( is_iterable( $data ) ) {
// with @dataProvider
$dataSuite = new DataProviderTestSuite(
$className . '::' . $name
);
foreach ( $data as $k => $v ) {
$dataSuite->addTest(
new $className( $name, $v, $k, $engineName ),
$groups
);
}
$engineSuite->addTest( $dataSuite );
} elseif ( $data === false ) {
// invalid @dataProvider
$engineSuite->addTest( new WarningTestCase(
"The data provider specified for {$className}::$name is invalid."
) );
} else {
// no @dataProvider
$engineSuite->addTest(
new $className( $name, [], '', $engineName ),
$groups
);
}
}
}
$suite->addTest( $engineSuite );
}
return $suite;
}
/**
* @return ScribuntoEngineBase
*/
protected function getEngine() {
if ( !$this->engine ) {
$parser = MediaWikiServices::getInstance()->getParserFactory()->create();
$options = ParserOptions::newFromAnon();
$options->setTemplateCallback( [ $this, 'templateCallback' ] );
$parser->startExternalParse( $this->getTestTitle(), $options, Parser::OT_HTML, true );
// HACK
if ( $this->engineName === 'LuaSandbox' ) {
$class = LuaSandboxEngine::class;
} elseif ( $this->engineName === 'LuaStandalone' ) {
$class = LuaStandaloneEngine::class;
}
$this->engine = new $class(
self::$engineConfigurations[$this->engineName] + [ 'parser' => $parser ]
);
$parser->scribunto_engine = $this->engine;
$this->engine->setTitle( $parser->getTitle() );
}
return $this->engine;
}
/**
* @see Parser::statelessFetchTemplate
* @param Title $title
* @param Parser|false $parser
* @return array
*/
public function templateCallback( $title, $parser ) {
$this->templateLoadCounts[$title->getFullText()] =
( $this->templateLoadCounts[$title->getFullText()] ?? 0 ) + 1;
if ( isset( $this->extraModules[$title->getFullText()] ) ) {
return [
'text' => $this->extraModules[$title->getFullText()],
'finalTitle' => $title,
'deps' => []
];
}
$modules = $this->getTestModules();
foreach ( $modules as $name => $fileName ) {
$modTitle = Title::makeTitle( NS_MODULE, $name );
if ( $modTitle->equals( $title ) ) {
return [
'text' => file_get_contents( $fileName ),
'finalTitle' => $title,
'deps' => []
];
}
}
return Parser::statelessFetchTemplate( $title, $parser );
}
/**
* Get the title used for unit tests
*
* @return Title
*/
protected function getTestTitle() {
return Title::newMainPage();
}
/**
* Reset the cached engine. The next call to getEngine() will return a new
* object.
*/
protected function resetEngine() {
$this->engine = null;
}
}