Page Menu
Home
WickedGov Phorge
Search
Configure Global Search
Log In
Files
F1426022
PhpUnitXmlManager.php
No One
Temporary
Actions
Download File
Edit File
Delete File
View Transforms
Subscribe
Flag For Later
Award Token
Size
6 KB
Referenced Files
None
Subscribers
None
PhpUnitXmlManager.php
View Options
<?php
declare
(
strict_types
=
1
);
namespace
MediaWiki\Composer\PhpUnitSplitter
;
/**
* @license GPL-2.0-or-later
*/
class
PhpUnitXmlManager
{
private
string
$rootDir
;
private
string
$testsListFile
;
/**
* The `SkippedTestCase` is generated dynamically by PHPUnit for tests
* that are marked as skipped. We don't need to find a matching filesystem
* file for these.
*
* The `ParserIntegrationTest` is a special case - it's a single test class
* that generates very many tests. To balance out the test suites, we exclude
* the class from the scan, and add it back in PhpUnitXml::addSpecialCaseTests
*/
private
const
EXPECTED_MISSING_CLASSES
=
[
"PHPUnit
\\
Framework
\\
SkippedTestCase"
,
"MediaWiki
\\
Extension
\\
Scribunto
\\
Tests
\\
Engines
\\
LuaCommon
\\
LuaEngineTestSkip"
,
"
\\
ParserIntegrationTest"
,
];
public
function
__construct
(
string
$rootDir
,
string
$testsListFile
)
{
$this
->
rootDir
=
$rootDir
;
$this
->
testsListFile
=
$testsListFile
;
}
private
function
getPhpUnitXmlTarget
():
string
{
return
$this
->
rootDir
.
DIRECTORY_SEPARATOR
.
PhpUnitXml
::
PHP_UNIT_XML_FILE
;
}
private
function
getPhpUnitXmlDist
():
string
{
return
$this
->
rootDir
.
DIRECTORY_SEPARATOR
.
"phpunit.xml.dist"
;
}
private
function
getTestsList
():
string
{
return
$this
->
rootDir
.
DIRECTORY_SEPARATOR
.
$this
->
testsListFile
;
}
private
function
loadPhpUnitXmlDist
():
PhpUnitXml
{
return
$this
->
loadPhpUnitXml
(
$this
->
getPhpUnitXmlDist
()
);
}
private
function
loadPhpUnitXml
(
string
$targetFile
):
PhpUnitXml
{
return
new
PhpUnitXml
(
$targetFile
);
}
private
function
loadTestClasses
():
array
{
if
(
!
file_exists
(
$this
->
getTestsList
()
)
)
{
throw
new
TestListMissingException
(
$this
->
getTestsList
()
);
}
return
(
new
PhpUnitTestListProcessor
(
$this
->
getTestsList
()
)
)->
getTestClasses
();
}
private
function
scanForTestFiles
():
array
{
return
(
new
PhpUnitTestFileScanner
(
$this
->
rootDir
)
)->
scanForFiles
();
}
private
static
function
extractNamespaceFromFile
(
$filename
):
array
{
$contents
=
file_get_contents
(
$filename
);
$matches
=
[];
if
(
preg_match
(
'/^namespace
\s
+([^
\s
;]+)/m'
,
$contents
,
$matches
)
)
{
return
explode
(
'
\\
'
,
$matches
[
1
]
);
}
return
[];
}
/**
* @param TestDescriptor $testDescriptor
* @param array $phpFiles
* @return ?string
* @throws MissingNamespaceMatchForTestException
* @throws UnlocatedTestException
*/
private
function
resolveFileForTest
(
TestDescriptor
$testDescriptor
,
array
$phpFiles
):
?
string
{
$filename
=
$testDescriptor
->
getClassName
()
.
".php"
;
if
(
!
array_key_exists
(
$filename
,
$phpFiles
)
)
{
if
(
!
in_array
(
$testDescriptor
->
getFullClassname
(),
self
::
EXPECTED_MISSING_CLASSES
)
)
{
throw
new
UnlocatedTestException
(
$testDescriptor
);
}
else
{
return
null
;
}
}
if
(
count
(
$phpFiles
[
$filename
]
)
===
1
)
{
return
$phpFiles
[
$filename
][
0
];
}
$possibleNamespaces
=
[];
foreach
(
$phpFiles
[
$filename
]
as
$file
)
{
$namespace
=
self
::
extractNamespaceFromFile
(
$file
);
if
(
$namespace
===
$testDescriptor
->
getNamespace
()
)
{
return
$file
;
}
$possibleNamespaces
[]
=
$namespace
;
}
throw
new
MissingNamespaceMatchForTestException
(
$testDescriptor
);
}
private
function
buildSuites
(
array
$testClasses
,
int
$groups
):
array
{
return
(
new
TestSuiteBuilder
()
)->
buildSuites
(
$testClasses
,
$groups
);
}
public
function
isPhpUnitXmlPrepared
():
bool
{
return
PhpUnitXml
::
isPhpUnitXmlPrepared
(
$this
->
rootDir
.
DIRECTORY_SEPARATOR
.
"phpunit.xml"
);
}
/**
* @return void
* @throws MissingNamespaceMatchForTestException
* @throws TestListMissingException
* @throws UnlocatedTestException
* @throws SuiteGenerationException
*/
public
function
createPhpUnitXml
(
int
$groups
)
{
$unitFile
=
$this
->
loadPhpUnitXmlDist
();
$testFiles
=
$this
->
scanForTestFiles
();
$testClasses
=
$this
->
loadTestClasses
();
$seenFiles
=
[];
foreach
(
$testClasses
as
$testDescriptor
)
{
$file
=
$this
->
resolveFileForTest
(
$testDescriptor
,
$testFiles
);
if
(
is_string
(
$file
)
&&
!
array_key_exists
(
$file
,
$seenFiles
)
)
{
$testDescriptor
->
setFilename
(
$file
);
$seenFiles
[
$file
]
=
1
;
}
}
$suites
=
$this
->
buildSuites
(
$testClasses
,
$groups
-
1
);
$unitFile
->
addSplitGroups
(
$suites
);
$unitFile
->
addSpecialCaseTests
(
$groups
);
$unitFile
->
saveToDisk
(
$this
->
getPhpUnitXmlTarget
()
);
}
public
static
function
listTestsNotice
()
{
print
(
PHP_EOL
);
print
(
'Running `phpunit --list-tests-xml` to get a list of expected tests ... '
.
PHP_EOL
);
print
(
PHP_EOL
);
}
/**
* @throws TestListMissingException
* @throws UnlocatedTestException
* @throws MissingNamespaceMatchForTestException
* @throws SuiteGenerationException
*/
public
static
function
splitTestsList
(
string
$testListFile
)
{
/**
* We split into 8 groups here, because experimentally that generates 100% CPU load
* on developer machines and results in groups that are similar in size to the
* Parser tests (which we have to run in a group on their own - see T345481)
*/
(
new
PhpUnitXmlManager
(
getcwd
(),
$testListFile
)
)->
createPhpUnitXml
(
8
);
print
(
PHP_EOL
.
'Created modified `phpunit.xml` with test suite groups'
.
PHP_EOL
);
}
/**
* @throws TestListMissingException
* @throws UnlocatedTestException
* @throws MissingNamespaceMatchForTestException
* @throws SuiteGenerationException
*/
public
static
function
splitTestsListExtensions
()
{
self
::
splitTestsList
(
'tests-list-extensions.xml'
);
}
/**
* @throws TestListMissingException
* @throws UnlocatedTestException
* @throws MissingNamespaceMatchForTestException
* @throws SuiteGenerationException
*/
public
static
function
splitTestsListDefault
()
{
self
::
splitTestsList
(
'tests-list-default.xml'
);
}
/**
* @throws TestListMissingException
* @throws UnlocatedTestException
* @throws MissingNamespaceMatchForTestException
* @throws SuiteGenerationException
*/
public
static
function
splitTestsCustom
()
{
if
(
$_SERVER
[
"argc"
]
<
3
)
{
print
(
'Specify a filename to split'
.
PHP_EOL
);
exit
(
1
);
}
$filename
=
$_SERVER
[
"argv"
][
2
];
self
::
splitTestsList
(
$filename
);
}
}
File Metadata
Details
Attached
Mime Type
text/x-php
Expires
Sat, May 16, 12:26 (1 d, 5 h)
Storage Engine
local-disk
Storage Format
Raw Data
Storage Handle
3a/68/644685c086aabca857b873f23735
Default Alt Text
PhpUnitXmlManager.php (6 KB)
Attached To
Mode
rMWPROD MediaWiki Production
Attached
Detach File
Event Timeline
Log In to Comment