Merge "Type hint against LinkTarget in WatchedItemStore"
[lhc/web/wiklou.git] / tests / phpunit / suites / ParserTestTopLevelSuite.php
1 <?php
2
3 use MediaWiki\MediaWikiServices;
4 use Wikimedia\ScopedCallback;
5
6 /**
7 * The UnitTest must be either a class that inherits from MediaWikiTestCase
8 * or a class that provides a public static suite() method which returns
9 * an PHPUnit_Framework_Test object
10 *
11 * @group Parser
12 * @group ParserTests
13 * @group Database
14 */
15 class ParserTestTopLevelSuite extends PHPUnit_Framework_TestSuite {
16 /** @var ParserTestRunner */
17 private $ptRunner;
18
19 /** @var ScopedCallback */
20 private $ptTeardownScope;
21
22 private $oldTablePrefix = '';
23
24 /**
25 * @defgroup filtering_constants Filtering constants
26 *
27 * Limit inclusion of parser tests files coming from MediaWiki core
28 * @{
29 */
30
31 /** Include files shipped with MediaWiki core */
32 const CORE_ONLY = 1;
33 /** Include non core files as set in $wgParserTestFiles */
34 const NO_CORE = 2;
35 /** Include anything set via $wgParserTestFiles */
36 const WITH_ALL = self::CORE_ONLY | self::NO_CORE;
37
38 /** @} */
39
40 /**
41 * Get a PHPUnit test suite of parser tests. Optionally filtered with
42 * $flags.
43 *
44 * @par Examples:
45 * Get a suite of parser tests shipped by MediaWiki core:
46 * @code
47 * ParserTestTopLevelSuite::suite( ParserTestTopLevelSuite::CORE_ONLY );
48 * @endcode
49 * Get a suite of various parser tests, like extensions:
50 * @code
51 * ParserTestTopLevelSuite::suite( ParserTestTopLevelSuite::NO_CORE );
52 * @endcode
53 * Get any test defined via $wgParserTestFiles:
54 * @code
55 * ParserTestTopLevelSuite::suite( ParserTestTopLevelSuite::WITH_ALL );
56 * @endcode
57 *
58 * @param int $flags Bitwise flag to filter out the $wgParserTestFiles that
59 * will be included. Default: ParserTestTopLevelSuite::CORE_ONLY
60 *
61 * @return PHPUnit_Framework_TestSuite
62 */
63 public static function suite( $flags = self::CORE_ONLY ) {
64 return new self( $flags );
65 }
66
67 function __construct( $flags ) {
68 parent::__construct();
69
70 $this->ptRecorder = new PhpunitTestRecorder;
71 $this->ptRunner = new ParserTestRunner( $this->ptRecorder );
72
73 if ( is_string( $flags ) ) {
74 $flags = self::CORE_ONLY;
75 }
76 global $IP;
77
78 $mwTestDir = $IP . '/tests/';
79
80 # Human friendly helpers
81 $wantsCore = ( $flags & self::CORE_ONLY );
82 $wantsRest = ( $flags & self::NO_CORE );
83
84 # Will hold the .txt parser test files we will include
85 $filesToTest = [];
86
87 # Filter out .txt files
88 $files = ParserTestRunner::getParserTestFiles();
89 foreach ( $files as $extName => $parserTestFile ) {
90 $isCore = ( strpos( $parserTestFile, $mwTestDir ) === 0 );
91
92 if ( $isCore && $wantsCore ) {
93 self::debug( "included core parser tests: $parserTestFile" );
94 $filesToTest[$extName] = $parserTestFile;
95 } elseif ( !$isCore && $wantsRest ) {
96 self::debug( "included non core parser tests: $parserTestFile" );
97 $filesToTest[$extName] = $parserTestFile;
98 } else {
99 self::debug( "skipped parser tests: $parserTestFile" );
100 }
101 }
102 self::debug( 'parser tests files: '
103 . implode( ' ', $filesToTest ) );
104
105 $testList = [];
106 $counter = 0;
107 foreach ( $filesToTest as $extensionName => $fileName ) {
108 if ( is_int( $extensionName ) ) {
109 // If there's no extension name because this is coming
110 // from the legacy global, then assume the next level directory
111 // is the extension name (e.g. extensions/FooBar/parserTests.txt).
112 $extensionName = basename( dirname( $fileName ) );
113 }
114 $testsName = $extensionName . '__' . basename( $fileName, '.txt' );
115 $parserTestClassName = ucfirst( $testsName );
116
117 // Official spec for class names: https://www.php.net/manual/en/language.oop5.basic.php
118 // Prepend 'ParserTest_' to be paranoid about it not starting with a number
119 $parserTestClassName = 'ParserTest_' .
120 preg_replace( '/[^a-zA-Z0-9_\x7f-\xff]/', '_', $parserTestClassName );
121
122 if ( isset( $testList[$parserTestClassName] ) ) {
123 // If there is a conflict, append a number.
124 $counter++;
125 $parserTestClassName .= $counter;
126 }
127 $testList[$parserTestClassName] = true;
128
129 // Previously we actually created a class here, with eval(). We now
130 // just override the name.
131
132 self::debug( "Adding test class $parserTestClassName" );
133 $this->addTest( new ParserTestFileSuite(
134 $this->ptRunner, $parserTestClassName, $fileName ) );
135 }
136 }
137
138 public function setUp() {
139 wfDebug( __METHOD__ );
140
141 $lb = MediaWikiServices::getInstance()->getDBLoadBalancer();
142 $db = $lb->getConnection( DB_MASTER );
143 $type = $db->getType();
144 $prefix = $type === 'oracle' ?
145 MediaWikiTestCase::ORA_DB_PREFIX : MediaWikiTestCase::DB_PREFIX;
146 $this->oldTablePrefix = $db->tablePrefix();
147 MediaWikiTestCase::setupTestDB( $db, $prefix );
148 CloneDatabase::changePrefix( $prefix );
149
150 $this->ptRunner->setDatabase( $db );
151
152 MediaWikiTestCase::resetNonServiceCaches();
153
154 MediaWikiTestCase::installMockMwServices();
155 $teardown = new ScopedCallback( function () {
156 MediaWikiTestCase::restoreMwServices();
157 } );
158
159 $teardown = $this->ptRunner->setupUploads( $teardown );
160 $this->ptTeardownScope = $teardown;
161 }
162
163 public function tearDown() {
164 wfDebug( __METHOD__ );
165 if ( $this->ptTeardownScope ) {
166 ScopedCallback::consume( $this->ptTeardownScope );
167 }
168 CloneDatabase::changePrefix( $this->oldTablePrefix );
169 }
170
171 /**
172 * Write $msg under log group 'tests-parser'
173 * @param string $msg Message to log
174 */
175 protected static function debug( $msg ) {
176 wfDebugLog( 'tests-parser', wfGetCaller() . ' ' . $msg );
177 }
178 }