mediawiki-extensions-Scribunto/tests/phpunit/Engines/LuaCommon/LuaEngineTestHelper.php
Kunal Mehta 1000d322e5 Add mw.loadJsonData()
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
2022-10-13 04:46:25 +00:00

216 lines
6 KiB
PHP

<?php
namespace MediaWiki\Extension\Scribunto\Tests\Engines\LuaCommon;
use MediaWiki\Extension\Scribunto\Engines\LuaCommon\LuaInterpreterNotFoundError;
use MediaWiki\Extension\Scribunto\Engines\LuaSandbox\LuaSandboxEngine;
use MediaWiki\Extension\Scribunto\Engines\LuaStandalone\LuaStandaloneEngine;
use MediaWiki\Extension\Scribunto\ScribuntoEngineBase;
use MediaWiki\MediaWikiServices;
use Parser;
use ParserOptions;
use PHPUnit\Framework\DataProviderTestSuite;
use PHPUnit\Framework\TestSuite;
use PHPUnit\Framework\WarningTestCase;
use PHPUnit\Util\Test;
use ReflectionClass;
use Title;
/**
* Trait that helps LuaEngineTestBase and LuaEngineUnitTestBase
*/
trait 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 ( LuaInterpreterNotFoundError $e ) {
$suite->addTest(
new 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;
}
}