Localisation updates from https://translatewiki.net.
[mediawiki.git] / tests / phpunit / suites / ParserTestTopLevelSuite.php
blob83bd67b59c81a071a212c327d081581d34340da7
1 <?php
3 use PHPUnit\Framework\TestSuite;
4 use Wikimedia\ScopedCallback;
6 /**
7 * The UnitTest must be either a class that inherits from MediaWikiIntegrationTestCase
8 * or a class that provides a public static suite() method which returns
9 * an PHPUnit\Framework\Test object
11 * @group Parser
12 * @group ParserTests
13 * @group Database
15 class ParserTestTopLevelSuite extends TestSuite {
16 use SuiteEventsTrait;
18 /** @var PhpunitTestRecorder */
19 private $ptRecorder;
21 /** @var ParserTestRunner */
22 private $ptRunner;
24 /** @var ScopedCallback */
25 private $ptTeardownScope;
27 /**
28 * @defgroup filtering_constants Filtering constants
30 * Limit inclusion of parser tests files coming from MediaWiki core
31 * @{
34 /**
35 * Include files shipped with MediaWiki core
37 public const CORE_ONLY = 1;
38 /** Include non core files returned by
39 * ParserTestRunner::getParserTestFiles() (that is, parser tests belonging
40 * to extensions).
42 public const NO_CORE = 2;
43 /** Include anything returned by ParserTestRunner::getParserTestFiles(),
44 * both core and extensions.
46 public const WITH_ALL = self::CORE_ONLY | self::NO_CORE;
48 /** @} */
50 /**
51 * Get a PHPUnit test suite of parser tests. Optionally filtered with
52 * $flags.
54 * @par Examples:
55 * Get a suite of parser tests shipped by MediaWiki core:
56 * @code
57 * ParserTestTopLevelSuite::suite( ParserTestTopLevelSuite::CORE_ONLY );
58 * @endcode
59 * Get a suite of various parser tests, like extensions:
60 * @code
61 * ParserTestTopLevelSuite::suite( ParserTestTopLevelSuite::NO_CORE );
62 * @endcode
63 * Get any test returned by ParserTestRunner::getParserTestFiles():
64 * @code
65 * ParserTestTopLevelSuite::suite( ParserTestTopLevelSuite::WITH_ALL );
66 * @endcode
68 * @param int $flags Bitwise flag to filter out the test files that
69 * will be included. Default: ParserTestTopLevelSuite::CORE_ONLY
71 * @return TestSuite
73 public static function suite( $flags = self::CORE_ONLY ) {
74 return new self( $flags );
77 public function __construct( $flags, ?array $parserTestFlags = null ) {
78 parent::__construct();
80 $this->ptRecorder = new PhpunitTestRecorder;
81 $runnerOpts = $parserTestFlags ?? json_decode( getenv( "PARSERTEST_FLAGS" ) ?: "[]", true );
82 // PHPUnit test runners requires all tests to be pregenerated.
83 // But, generating Parsoid selser edit trees requires the DOM.
84 // So, we cannot pregenerate Parsoid selser auto-edit tests.
85 // They have to be generated dynamically. So, set this to 0.
86 // We will handle auto-edit selser tests as a composite test.
87 $runnerOpts['numchanges'] = 0;
88 $this->ptRunner = new ParserTestRunner(
89 $this->ptRecorder, $runnerOpts
92 if ( is_string( $flags ) ) {
93 $flags = self::CORE_ONLY;
95 global $IP;
97 $mwTestDir = $IP . '/tests/';
99 # Human friendly helpers
100 $wantsCore = ( $flags & self::CORE_ONLY );
101 $wantsRest = ( $flags & self::NO_CORE );
103 # Will hold the .txt parser test files we will include
104 $filesToTest = [];
106 # Filter out .txt files
107 $files = ParserTestRunner::getParserTestFiles();
108 foreach ( $files as $extName => $parserTestFile ) {
109 $isCore = str_starts_with( $parserTestFile, $mwTestDir );
111 if ( $isCore && $wantsCore ) {
112 self::debug( "included core parser tests: $parserTestFile" );
113 $filesToTest[$extName] = $parserTestFile;
114 } elseif ( !$isCore && $wantsRest ) {
115 self::debug( "included non core parser tests: $parserTestFile" );
116 $filesToTest[$extName] = $parserTestFile;
117 } else {
118 self::debug( "skipped parser tests: $parserTestFile" );
121 self::debug( 'parser tests files: '
122 . implode( ' ', $filesToTest ) );
124 $testList = [];
125 $counter = 0;
126 foreach ( $filesToTest as $extensionName => $fileName ) {
127 $isCore = str_starts_with( $fileName, $mwTestDir );
128 if ( is_int( $extensionName ) ) {
129 // If there's no extension name because this is coming
130 // from the legacy global, then assume the next level directory
131 // is the extension name (e.g. extensions/FooBar/parserTests.txt).
132 $extensionName = basename( dirname( $fileName ) );
134 $testsName = $extensionName . '__' . basename( $fileName, '.txt' );
135 $parserTestClassName = ucfirst( $testsName );
137 // Official spec for class names: https://www.php.net/manual/en/language.oop5.basic.php
138 // Prepend 'ParserTest_' to be paranoid about it not starting with a number
139 $parserTestClassName = 'ParserTest_' .
140 preg_replace( '/[^a-zA-Z0-9_\x7f-\xff]/', '_', $parserTestClassName );
142 $originalClassName = $parserTestClassName;
143 while ( isset( $testList[$parserTestClassName] ) ) {
144 // If there is a conflict, append a number.
145 $counter++;
146 $parserTestClassName = $originalClassName . '_' . $counter;
148 $testList[$parserTestClassName] = true;
150 // Previously we actually created a class here, with eval(). We now
151 // just override the name.
153 self::debug( "Adding test class $parserTestClassName" );
154 // Legacy parser
155 $this->addTest( new ParserTestFileSuite(
156 $this->ptRunner, "Legacy$parserTestClassName", $fileName ) );
157 // Parsoid (only run this on extensions for now, since Parsoid
158 // has its own copy of core's parser tests which it runs in its
159 // own test suite)
160 if ( !$isCore ) {
161 $this->addTest( new ParsoidTestFileSuite(
162 $this->ptRunner, "Parsoid$parserTestClassName", $fileName
163 ) );
168 protected function setUp(): void {
169 // MediaWikiIntegrationTestCase leaves its test DB hanging around.
170 // we want to make sure we have a clean instance, so tear down any
171 // existing test DB. This has no effect if no test DB exists.
172 MediaWikiIntegrationTestCase::teardownTestDB();
173 // Similarly, make sure we don't reuse Test users from other tests
174 TestUserRegistry::clear();
176 $teardown = $this->ptRunner->setupDatabase( null );
177 $teardown = $this->ptRunner->staticSetup( $teardown );
178 $teardown = $this->ptRunner->setupUploads( $teardown );
179 $this->ptTeardownScope = $teardown;
182 protected function tearDown(): void {
183 if ( $this->ptTeardownScope ) {
184 ScopedCallback::consume( $this->ptTeardownScope );
186 TestUserRegistry::clear();
190 * Write $msg under log group 'tests-parser'
191 * @param string $msg Message to log
193 protected static function debug( $msg ) {
194 wfDebugLog( 'tests-parser', wfGetCaller() . ' ' . $msg );