Page Menu
Home
WickedGov Phorge
Search
Configure Global Search
Log In
Files
F4136864
parserTests.php
No One
Temporary
Actions
Download File
Edit File
Delete File
View Transforms
Subscribe
Flag For Later
Award Token
Size
29 KB
Referenced Files
None
Subscribers
None
parserTests.php
View Options
<?php
// phpcs:disable Generic.Files.LineLength.TooLong
require_once
__DIR__
.
'/../tools/Maintenance.php'
;
use
MediaWiki\Settings\SettingsBuilder
;
use
SebastianBergmann\Diff\Differ
;
use
Wikimedia\Parsoid\ParserTests\Stats
;
use
Wikimedia\Parsoid\ParserTests\Test
;
use
Wikimedia\Parsoid\ParserTests\TestRunner
;
use
Wikimedia\Parsoid\ParserTests\TestUtils
;
use
Wikimedia\Parsoid\Utils\ScriptUtils
;
// phpcs:ignore MediaWiki.Files.ClassMatchesFilename.WrongCase
class
ParserTests
extends
\Wikimedia\Parsoid\Tools\Maintenance
{
use
\Wikimedia\Parsoid\Tools\ExtendedOptsProcessor
;
/** @var array */
public
$processedOptions
;
// PORT-FIXME: Used to be colors::mode in all the use sites
public
static
$colors_mode
;
/** @var Differ */
private
static
$differ
;
public
function
__construct
()
{
parent
::
__construct
();
self
::
setupOpts
(
$this
);
$this
->
setAllowUnregisteredOptions
(
false
);
}
/**
* @inheritDoc
*/
public
function
finalSetup
(
?
SettingsBuilder
$settingsBuilder
=
null
)
{
parent
::
finalSetup
(
$settingsBuilder
);
self
::
requireTestsAutoloader
();
}
public
function
execute
():
bool
{
$this
->
processedOptions
=
self
::
processOptions
(
$this
);
$testFile
=
$this
->
getArg
(
0
);
if
(
$testFile
)
{
$testFilePaths
=
[
realpath
(
$testFile
)
];
}
else
{
$testFilePaths
=
[];
$repos
=
json_decode
(
file_get_contents
(
__DIR__
.
'/../tests/parserTests.json'
),
true
);
foreach
(
$repos
as
$repo
=>
$repoInfo
)
{
foreach
(
$repoInfo
[
"targets"
]
as
$f
=>
$info
)
{
$testFilePaths
[]
=
realpath
(
__DIR__
.
'/../tests/parser/'
.
$f
);
}
}
}
$globalStats
=
new
Stats
();
$knownFailuresChanged
=
false
;
$exitCode
=
0
;
if
(
$this
->
processedOptions
[
'integrated'
]
??
false
)
{
// Some methods which are discouraged for normal code throw
// exceptions unless we declare this is just a test.
define
(
'MW_PARSER_TEST'
,
true
);
// See Ifaf53862b96e9127d8f375ad8dd0cc362cba9f5b in gerrit;
// this should use the \ParserTestRunner::runParsoidTest()
// method from core.
throw
new
\RuntimeException
(
"Not yet implemented"
);
}
else
{
foreach
(
$testFilePaths
as
$testFile
)
{
$testRunner
=
new
TestRunner
(
$testFile
,
'standalone'
,
$this
->
processedOptions
[
'modes'
]
);
$result
=
$testRunner
->
run
(
$this
->
processedOptions
);
$globalStats
->
accum
(
$result
[
'stats'
]
);
// Sum all stats
$knownFailuresChanged
=
$knownFailuresChanged
||
$result
[
'knownFailuresChanged'
];
$exitCode
=
$exitCode
?:
$result
[
'exitCode'
];
if
(
$exitCode
!==
0
&&
$this
->
processedOptions
[
'exit-unexpected'
]
)
{
break
;
}
}
}
$this
->
processedOptions
[
'reportSummary'
](
[],
$globalStats
,
null
,
null
,
$knownFailuresChanged
,
$this
->
processedOptions
);
return
$exitCode
===
0
;
}
/**
* Process CLI opts and return
*
* @param \Wikimedia\Parsoid\Tools\Maintenance $script
*/
public
static
function
setupOpts
(
\Wikimedia\Parsoid\Tools\Maintenance
$script
):
void
{
$opts
=
ScriptUtils
::
addStandardOptions
(
[
'wt2html'
=>
[
'description'
=>
'Wikitext -> HTML(DOM)'
,
'default'
=>
false
,
'boolean'
=>
true
],
'html2wt'
=>
[
'description'
=>
'HTML(DOM) -> Wikitext'
,
'default'
=>
false
,
'boolean'
=>
true
],
'wt2wt'
=>
[
'description'
=>
'Roundtrip testing: Wikitext -> DOM(HTML) -> Wikitext'
,
'default'
=>
false
,
'boolean'
=>
true
],
'html2html'
=>
[
'description'
=>
'Roundtrip testing: HTML(DOM) -> Wikitext -> HTML(DOM)'
,
'default'
=>
false
,
'boolean'
=>
true
],
'selser'
=>
[
'description'
=>
'Roundtrip testing: Wikitext -> DOM(HTML) -> Wikitext (with selective serialization). '
.
'Set to "noauto" to just run the tests with manual selser changes.'
,
'boolean'
=>
false
],
'changetree'
=>
[
'description'
=>
'Changes to apply to parsed HTML to generate new HTML to be serialized (useful with selser)'
,
'default'
=>
null
,
'boolean'
=>
false
],
'numchanges'
=>
[
'description'
=>
'Make multiple different changes to the DOM, run a selser test for each one.'
,
'default'
=>
20
,
'boolean'
=>
false
],
'cache'
=>
[
'description'
=>
'Get tests cases from cache file'
,
'boolean'
=>
true
,
'default'
=>
false
],
'filter'
=>
[
'description'
=>
'Only run tests whose descriptions match given string'
],
'regex'
=>
[
'description'
=>
'Only run tests whose descriptions match given regex'
,
'alias'
=>
[
'regexp'
,
're'
]
],
'run-disabled'
=>
[
'description'
=>
'Run disabled tests'
,
'default'
=>
false
,
'boolean'
=>
true
],
'run-php'
=>
[
'description'
=>
'Run php-only tests'
,
'default'
=>
false
,
'boolean'
=>
true
],
'maxtests'
=>
[
'description'
=>
'Maximum number of tests to run'
,
'boolean'
=>
false
],
'quick'
=>
[
'description'
=>
'Suppress diff output of failed tests'
,
'boolean'
=>
true
,
'default'
=>
false
],
'quiet'
=>
[
'description'
=>
'Suppress notification of passed tests (shows only failed tests)'
,
'boolean'
=>
true
,
'default'
=>
false
],
'quieter'
=>
[
'description'
=>
'Suppress per-file summary and failed test diffs. '
.
'Implies --quick and --quiet.'
,
'boolean'
=>
true
,
'default'
=>
false
],
'offsetType'
=>
[
'description'
=>
'Test DSR offset conversion code while running tests.'
,
'boolean'
=>
false
,
'default'
=>
'byte'
,
],
'knownFailures'
=>
[
'description'
=>
'Compare against known failures'
,
'default'
=>
true
,
'boolean'
=>
false
],
'updateKnownFailures'
=>
[
'description'
=>
'Update parserTests-knownFailures.json with failing tests.'
,
'default'
=>
false
,
'boolean'
=>
true
],
'exit-zero'
=>
[
'description'
=>
"Don't exit with nonzero status if failures are found."
,
'default'
=>
false
,
'boolean'
=>
true
],
'xml'
=>
[
'description'
=>
'Print output in JUnit XML format.'
,
'default'
=>
false
,
'boolean'
=>
true
],
'exit-unexpected'
=>
[
'description'
=>
'Exit after the first unexpected result.'
,
'default'
=>
false
,
'boolean'
=>
true
],
'update-tests'
=>
[
'description'
=>
'Update parserTests.txt with results from wt2html fails.'
,
'default'
=>
false
,
'boolean'
=>
true
],
'update-unexpected'
=>
[
'description'
=>
'Update parserTests.txt with results from wt2html unexpected fails.'
,
'default'
=>
false
,
'boolean'
=>
true
],
'update-format'
=>
[
'description'
=>
'format with which to update tests; only useful in conjunction '
.
'with update-tests or update-unexpected. Values: raw, noDsr, actualNormalized.'
,
'default'
=>
'noDsr'
,
]
],
[
// override defaults for standard options
'fetchTemplates'
=>
false
,
'usePHPPreProcessor'
=>
false
,
'fetchConfig'
=>
false
]
);
foreach
(
$opts
as
$opt
=>
$optInfo
)
{
$script
->
addOption
(
$opt
,
$optInfo
[
'description'
],
false
,
empty
(
$optInfo
[
'boolean'
]
),
false
);
if
(
isset
(
$optInfo
[
'default'
]
)
)
{
$script
->
setOptionDefault
(
$opt
,
$optInfo
[
'default'
]
);
}
}
}
public
static
function
processOptions
(
\Wikimedia\Parsoid\Tools\Maintenance
$script
):
array
{
$options
=
$script
->
optionsToArray
();
if
(
$options
[
'help'
]
)
{
$script
->
maybeHelp
();
print
"Additional dump options specific to parserTests script:
\n
"
.
"* dom:post-changes : Dumps DOM after applying selser changetree
\n
"
.
"Examples
\n
"
.
"
\$
php parserTests.php --selser --filter '...' --dump dom:post-changes
\n
"
.
"
\$
php parserTests.php --selser --filter '...' --changetree '...' --dump dom:post-changes
\n
"
;
die
(
0
);
}
ScriptUtils
::
setColorFlags
(
$options
);
if
(
!(
$options
[
'wt2wt'
]
||
$options
[
'wt2html'
]
||
$options
[
'html2wt'
]
||
$options
[
'html2html'
]
||
isset
(
$options
[
'selser'
]
)
)
)
{
$options
[
'wt2wt'
]
=
true
;
$options
[
'wt2html'
]
=
true
;
$options
[
'html2html'
]
=
true
;
$options
[
'html2wt'
]
=
true
;
if
(
ScriptUtils
::
booleanOption
(
$options
[
'updateKnownFailures'
]
??
null
)
)
{
// turn on all modes by default for --updateKnownFailures
$options
[
'selser'
]
=
true
;
// double checking options are valid (T53448 asks to be able to use --filter here)
if
(
isset
(
$options
[
'filter'
]
)
||
isset
(
$options
[
'regex'
]
)
||
isset
(
$options
[
'maxtests'
]
)
||
$options
[
'exit-unexpected'
]
)
{
print
"
\n
ERROR: can't combine --updateKnownFailures with --filter, --maxtests or --exit-unexpected"
;
die
(
1
);
}
}
}
if
(
$options
[
'xml'
]
)
{
$options
[
'reportResult'
]
=
[
self
::
class
,
'reportResultXML'
];
$options
[
'reportStart'
]
=
[
self
::
class
,
'reportStartXML'
];
$options
[
'reportSummary'
]
=
[
self
::
class
,
'reportSummaryXML'
];
$options
[
'reportFailure'
]
=
[
self
::
class
,
'reportFailureXML'
];
self
::
$colors_mode
=
'none'
;
}
if
(
!
is_callable
(
$options
[
'reportFailure'
]
??
null
)
)
{
// default failure reporting is standard out,
// see printFailure for documentation of the default.
$options
[
'reportFailure'
]
=
[
self
::
class
,
'printFailure'
];
}
if
(
!
is_callable
(
$options
[
'reportSuccess'
]
??
null
)
)
{
// default success reporting is standard out,
// see printSuccess for documentation of the default.
$options
[
'reportSuccess'
]
=
[
self
::
class
,
'printSuccess'
];
}
if
(
!
is_callable
(
$options
[
'reportStart'
]
??
null
)
)
{
// default summary reporting is standard out,
// see reportStart for documentation of the default.
$options
[
'reportStart'
]
=
[
self
::
class
,
'reportStartOfTests'
];
}
if
(
!
is_callable
(
$options
[
'reportSummary'
]
??
null
)
)
{
// default summary reporting is standard out,
// see reportSummary for documentation of the default.
$options
[
'reportSummary'
]
=
[
self
::
class
,
'reportSummary'
];
}
if
(
!
is_callable
(
$options
[
'reportResult'
]
??
null
)
)
{
// default result reporting is standard out,
// see printResult for documentation of the default.
$options
[
'reportResult'
]
=
function
(
...
$args
)
use
(
&
$options
)
{
return
self
::
printResult
(
$options
[
'reportFailure'
],
$options
[
'reportSuccess'
],
...
$args
);
};
}
if
(
!
is_callable
(
$options
[
'getDiff'
]
??
null
)
)
{
// this is the default for diff-getting, but it can be overridden
// see doDiff for documentation of the default.
$options
[
'getDiff'
]
=
[
self
::
class
,
'doDiff'
];
}
if
(
!
is_callable
(
$options
[
'getActualExpected'
]
??
null
)
)
{
// this is the default for getting the actual and expected
// outputs, but it can be overridden
// see getActualExpected for documentation of the default.
$options
[
'getActualExpected'
]
=
[
self
::
class
,
'getActualExpected'
];
}
$options
[
'modes'
]
=
[];
foreach
(
Test
::
ALL_TEST_MODES
as
$m
)
{
if
(
(
$m
!==
'selser'
&&
$options
[
$m
]
)
||
(
$m
===
'selser'
&&
isset
(
$options
[
$m
]
)
)
)
{
$options
[
'modes'
][]
=
$m
;
}
}
return
$options
;
}
/**
* Colorize given number if <> 0.
*
* @param int $count
* @param string $color
* @return string Colorized count
*/
private
static
function
colorizeCount
(
int
$count
,
string
$color
):
string
{
$s
=
(
string
)
$count
;
return
TestUtils
::
colorString
(
$s
,
$color
);
}
/**
* @param array $modesRan
* @param Stats $stats
* - failedTests int Number of failed tests due to differences in output.
* - passedTests int Number of tests passed without any special consideration.
* - modes array All of the stats (failedTests, passedTests) per-mode.
* @param ?string $file
* @param ?array $testFilter
* @param bool $knownFailuresChanged
* @param array $options
*/
public
static
function
reportSummary
(
array
$modesRan
,
Stats
$stats
,
?
string
$file
,
?
array
$testFilter
,
bool
$knownFailuresChanged
,
array
$options
):
void
{
$curStr
=
null
;
$mode
=
null
;
$thisMode
=
null
;
$failTotalTests
=
$stats
->
failedTests
;
$happiness
=
$stats
->
passedTestsUnexpected
===
0
&&
$stats
->
failedTestsUnexpected
===
0
;
$filename
=
$file
===
null
?
'ALL TESTS'
:
$file
;
$quieter
=
ScriptUtils
::
booleanOption
(
$options
[
'quieter'
]
??
''
);
if
(
$quieter
&&
$file
!==
null
)
{
return
;
}
if
(
!
$quieter
)
{
print
"==========================================================
\n
"
;
print
'SUMMARY:'
.
TestUtils
::
colorString
(
$filename
,
$happiness
?
'green'
:
'red'
)
.
"
\n
"
;
}
if
(
$file
!==
null
)
{
print
'Execution time: '
.
round
(
1000
*
(
microtime
(
true
)
-
$stats
->
startTime
),
3
)
.
"ms
\n
"
;
}
if
(
$failTotalTests
!==
0
)
{
foreach
(
$modesRan
as
$mode
)
{
$curStr
=
$mode
.
': '
;
$thisMode
=
$stats
->
modes
[
$mode
];
$curStr
.=
self
::
colorizeCount
(
$thisMode
->
passedTests
,
'green'
)
.
' passed ('
;
$curStr
.=
self
::
colorizeCount
(
$thisMode
->
passedTestsUnexpected
,
'red'
)
.
' unexpected) / '
;
$curStr
.=
self
::
colorizeCount
(
$thisMode
->
failedTests
,
'red'
)
.
' failed ('
;
$curStr
.=
self
::
colorizeCount
(
$thisMode
->
failedTestsUnexpected
,
'red'
)
.
' unexpected)'
;
print
$curStr
.
"
\n
"
;
}
$curStr
=
'TOTAL'
.
': '
;
$curStr
.=
self
::
colorizeCount
(
$stats
->
passedTests
,
'green'
)
.
' passed ('
;
$curStr
.=
self
::
colorizeCount
(
$stats
->
passedTestsUnexpected
,
'red'
)
.
' unexpected) / '
;
$curStr
.=
self
::
colorizeCount
(
$stats
->
failedTests
,
'red'
)
.
' failed ('
;
$curStr
.=
self
::
colorizeCount
(
$stats
->
failedTestsUnexpected
,
'red'
)
.
' unexpected)'
;
print
$curStr
.
"
\n
"
;
if
(
$file
===
null
)
{
$buf
=
self
::
colorizeCount
(
$stats
->
passedTests
,
'green'
);
$buf
.=
' total passed tests (expected '
;
$buf
.=
(
string
)(
$stats
->
passedTests
-
$stats
->
passedTestsUnexpected
+
$stats
->
failedTestsUnexpected
);
$buf
.=
'), '
;
$buf
.=
self
::
colorizeCount
(
$failTotalTests
,
'red'
)
.
' total failures (expected '
;
$buf
.=
(
string
)(
$stats
->
failedTests
-
$stats
->
failedTestsUnexpected
+
$stats
->
passedTestsUnexpected
);
$buf
.=
")
\n
"
;
print
$buf
;
}
}
else
{
if
(
$testFilter
!==
null
)
{
$buf
=
'Passed '
.
$stats
->
passedTests
.
' of '
.
$stats
->
passedTests
.
' tests matching '
.
$testFilter
[
'raw'
];
}
else
{
// Should not happen if it does: Champagne!
$buf
=
'Passed '
.
$stats
->
passedTests
.
' of '
.
$stats
->
passedTests
.
' tests'
;
}
print
$buf
.
'... '
.
TestUtils
::
colorString
(
'ALL TESTS PASSED!'
,
'green'
)
.
"
\n
"
;
}
// If we logged error messages, complain about it.
$logMsg
=
TestUtils
::
colorString
(
'No errors logged.'
,
'green'
);
if
(
$stats
->
loggedErrorCount
>
0
)
{
$logMsg
=
TestUtils
::
colorString
(
$stats
->
loggedErrorCount
.
' errors logged.'
,
'red'
);
}
if
(
$file
===
null
)
{
if
(
$stats
->
loggedErrorCount
>
0
)
{
$logMsg
=
TestUtils
::
colorString
(
''
.
$stats
->
loggedErrorCount
,
'red'
);
}
else
{
$logMsg
=
TestUtils
::
colorString
(
''
.
$stats
->
loggedErrorCount
,
'green'
);
}
$logMsg
.=
' errors logged.'
;
}
print
$logMsg
.
"
\n
"
;
$failures
=
$stats
->
allFailures
();
// If the knownFailures changed, complain about it.
if
(
$knownFailuresChanged
)
{
print
TestUtils
::
colorString
(
'Known failures changed!'
,
'red'
)
.
"
\n
"
;
}
if
(
$file
===
null
)
{
if
(
$failures
===
0
)
{
print
'--> '
.
TestUtils
::
colorString
(
'NO UNEXPECTED RESULTS'
,
'green'
)
.
" <--
\n
"
;
if
(
$knownFailuresChanged
)
{
print
"Perhaps some tests were deleted or renamed.
\n
"
;
print
"Use `php bin/parserTests.php --updateKnownFailures` to update knownFailures list.
\n
"
;
}
}
else
{
print
TestUtils
::
colorString
(
'--> '
.
$failures
.
' UNEXPECTED RESULTS. <--'
,
'red'
)
.
"
\n
"
;
}
}
}
private
static
function
prettyPrintIOptions
(
?
array
$iopts
=
null
):
string
{
if
(
!
$iopts
)
{
return
''
;
}
$ppValue
=
null
;
// Forward declaration
$ppValue
=
static
function
(
$v
)
use
(
&
$ppValue
)
{
if
(
is_array
(
$v
)
)
{
return
implode
(
','
,
array_map
(
$ppValue
,
$v
)
);
}
if
(
is_string
(
$v
)
&&
(
preg_match
(
'/^
\[\[
[^
\]
]*
\]\]
$/D'
,
$v
)
||
preg_match
(
'/^[-
\w
]+$/D'
,
$v
)
)
)
{
return
$v
;
}
return
json_encode
(
$v
);
};
$strPieces
=
array_map
(
static
function
(
$k
)
use
(
$iopts
,
$ppValue
)
{
if
(
$iopts
[
$k
]
===
''
)
{
return
$k
;
}
return
$k
.
'='
.
$ppValue
(
$iopts
[
$k
]
);
},
array_keys
(
$iopts
)
);
return
implode
(
' '
,
$strPieces
);
}
/**
* @param Stats $stats
* @param Test $item
* @param array $options
* @param string $mode
* @param string $title
* @param array $actual
* @param array $expected
* @param ?string $expectFail If this test was expected to fail (on knownFailures list), then the expected failure output; otherwise null.
* @return bool true if the failure was expected.
*/
public
static
function
printFailure
(
Stats
$stats
,
Test
$item
,
array
$options
,
string
$mode
,
string
$title
,
array
$actual
,
array
$expected
,
?
string
$expectFail
):
bool
{
$quiet
=
ScriptUtils
::
booleanOption
(
$options
[
'quiet'
]
??
null
);
$quieter
=
ScriptUtils
::
booleanOption
(
$options
[
'quieter'
]
??
null
);
$quick
=
ScriptUtils
::
booleanOption
(
$options
[
'quick'
]
??
null
);
$failureOnly
=
$quieter
||
$quick
;
$extTitle
=
str_replace
(
"
\n
"
,
' '
,
"$title ($mode)"
);
$knownFailures
=
false
;
if
(
ScriptUtils
::
booleanOption
(
$options
[
'knownFailures'
]
??
null
)
&&
$expectFail
!==
null
)
{
// compare with remembered output
$offsetType
=
$options
[
'offsetType'
]
??
'byte'
;
if
(
$offsetType
===
'byte'
&&
$item
->
normalizeKnownFailure
(
$expectFail
)
!==
$item
->
normalizeKnownFailure
(
$actual
[
'raw'
]
)
)
{
$knownFailures
=
true
;
}
else
{
if
(
!
$quiet
&&
!
$quieter
)
{
print
TestUtils
::
colorString
(
'EXPECTED FAIL'
,
'red'
)
.
': '
.
TestUtils
::
colorString
(
$extTitle
,
'yellow'
)
.
"
\n
"
;
}
return
true
;
}
}
if
(
!
$failureOnly
)
{
print
"=====================================================
\n
"
;
}
if
(
$knownFailures
)
{
print
TestUtils
::
colorString
(
'UNEXPECTED CHANGE TO KNOWN FAILURE OUTPUT'
,
'red'
,
true
)
.
': '
.
TestUtils
::
colorString
(
$extTitle
,
'yellow'
)
.
"
\n
"
;
print
TestUtils
::
colorString
(
'Known failure, but the output changed!'
,
'red'
)
.
"
\n
"
;
}
else
{
print
TestUtils
::
colorString
(
'UNEXPECTED FAIL'
,
'red'
,
true
)
.
': '
.
TestUtils
::
colorString
(
$extTitle
,
'yellow'
)
.
"
\n
"
;
}
if
(
$mode
===
'selser'
)
{
if
(
$item
->
wt2wtPassed
)
{
print
TestUtils
::
colorString
(
'Even worse, the non-selser wt2wt test passed!'
,
'red'
)
.
"
\n
"
;
}
elseif
(
$actual
&&
$item
->
wt2wtResult
!==
$actual
[
'raw'
]
)
{
print
TestUtils
::
colorString
(
'Even worse, the non-selser wt2wt test had a different result!'
,
'red'
)
.
"
\n
"
;
}
}
if
(
!
$failureOnly
)
{
// PORT-FIXME: Removed comments .. maybe need to put it back
// print implode( "\n", $item->comments ) . "\n";
if
(
$options
)
{
print
TestUtils
::
colorString
(
'OPTIONS'
,
'cyan'
)
.
':'
.
"
\n
"
;
print
self
::
prettyPrintIOptions
(
$item
->
options
)
.
"
\n
"
;
}
print
TestUtils
::
colorString
(
'INPUT'
,
'cyan'
)
.
':'
.
"
\n
"
;
print
$actual
[
'input'
]
.
"
\n
"
;
if
(
$knownFailures
)
{
print
"
\n
"
.
TestUtils
::
colorString
(
'KNOWN FAILURE OUTPUT'
,
'cyan'
)
.
":
\n
"
;
print
$expectFail
.
"
\n
"
;
}
print
$options
[
'getActualExpected'
](
$actual
,
$expected
,
$options
[
'getDiff'
]
)
.
"
\n
"
;
}
return
false
;
}
/**
* @param Stats $stats
* @param Test $item
* @param array $options
* @param string $mode
* @param string $title
* @param string $raw
* @param bool $expectSuccess Whether this success was expected (or was it a known failure).
* @return bool true if the success was expected.
*/
public
static
function
printSuccess
(
Stats
$stats
,
Test
$item
,
array
$options
,
string
$mode
,
string
$title
,
string
$raw
,
bool
$expectSuccess
):
bool
{
$quiet
=
ScriptUtils
::
booleanOption
(
$options
[
'quiet'
]
??
null
);
$quieter
=
ScriptUtils
::
booleanOption
(
$options
[
'quieter'
]
??
null
);
$quick
=
ScriptUtils
::
booleanOption
(
$options
[
'quick'
]
??
null
);
$failureOnly
=
$quieter
||
$quick
;
$extTitle
=
str_replace
(
"
\n
"
,
' '
,
"$title ($mode)"
);
if
(
ScriptUtils
::
booleanOption
(
$options
[
'knownFailures'
]
??
null
)
&&
!
$expectSuccess
)
{
if
(
!
$failureOnly
)
{
print
"=====================================================
\n
"
;
}
print
TestUtils
::
colorString
(
'UNEXPECTED PASS'
,
'green'
,
true
)
.
': '
.
TestUtils
::
colorString
(
$extTitle
,
'yellow'
)
.
"
\n
"
;
if
(
!
$failureOnly
)
{
print
TestUtils
::
colorString
(
'RAW RENDERED'
,
'cyan'
)
.
":
\n
"
;
print
$raw
.
"
\n
"
;
}
return
false
;
}
if
(
!
$quiet
&&
!
$quieter
)
{
$outStr
=
'EXPECTED PASS'
;
$outStr
=
TestUtils
::
colorString
(
$outStr
,
'green'
)
.
': '
.
TestUtils
::
colorString
(
$extTitle
,
'yellow'
);
print
$outStr
.
"
\n
"
;
if
(
$mode
===
'selser'
&&
isset
(
$item
->
wt2wtPassed
)
&&
!
$item
->
wt2wtPassed
)
{
print
TestUtils
::
colorString
(
'Even better, the non-selser wt2wt test failed!'
,
'red'
)
.
"
\n
"
;
}
}
return
true
;
}
/**
* Print the actual and expected outputs.
*
* @param array $actual
* - string raw
* - string normal
* @param array $expected
* - string raw
* - string normal
* @param callable $getDiff Returns a string showing the diff(s) for the test.
* - array actual
* - array expected
* @return string
*/
public
static
function
getActualExpected
(
array
$actual
,
array
$expected
,
callable
$getDiff
):
string
{
if
(
self
::
$colors_mode
===
'none'
)
{
$mkVisible
=
static
function
(
$s
)
{
return
$s
;
};
}
else
{
$mkVisible
=
static
function
(
$s
)
{
return
preg_replace
(
'/
\x
A0/'
,
TestUtils
::
colorString
(
"␣"
,
"white"
),
preg_replace
(
'/
\n
/'
,
TestUtils
::
colorString
(
"↵
\n
"
,
"white"
),
$s
)
);
};
}
$returnStr
=
''
;
$returnStr
.=
TestUtils
::
colorString
(
'RAW EXPECTED'
,
'cyan'
)
.
":
\n
"
;
$returnStr
.=
$expected
[
'raw'
]
.
"
\n
"
;
$returnStr
.=
TestUtils
::
colorString
(
'RAW RENDERED'
,
'cyan'
)
.
":
\n
"
;
$returnStr
.=
$actual
[
'raw'
]
.
"
\n
"
;
$returnStr
.=
TestUtils
::
colorString
(
'NORMALIZED EXPECTED'
,
'magenta'
)
.
":
\n
"
;
$returnStr
.=
$mkVisible
(
$expected
[
'normal'
]
)
.
"
\n
"
;
$returnStr
.=
TestUtils
::
colorString
(
'NORMALIZED RENDERED'
,
'magenta'
)
.
":
\n
"
;
$returnStr
.=
$mkVisible
(
$actual
[
'normal'
]
)
.
"
\n
"
;
$returnStr
.=
TestUtils
::
colorString
(
'DIFF'
,
'cyan'
)
.
":
\n
"
;
$returnStr
.=
$getDiff
(
$actual
,
$expected
);
return
$returnStr
;
}
/**
* @param array $actual
* - string normal
* @param array $expected
* - string normal
* @return string Colorized diff
*/
public
static
function
doDiff
(
array
$actual
,
array
$expected
):
string
{
// safe to always request color diff, because we set color mode='none'
// if colors are turned off.
$e
=
preg_replace
(
'/
\x
A0/'
,
"␣"
,
$expected
[
'normal'
]
);
$a
=
preg_replace
(
'/
\x
A0/'
,
"␣"
,
$actual
[
'normal'
]
);
// PORT_FIXME:
if
(
!
self
::
$differ
)
{
self
::
$differ
=
new
Differ
();
}
$diffs
=
self
::
$differ
->
diff
(
$e
,
$a
);
$diffs
=
preg_replace_callback
(
'/^(-.*)/m'
,
static
function
(
$m
)
{
return
TestUtils
::
colorString
(
$m
[
0
],
'green'
);
},
$diffs
);
$diffs
=
preg_replace_callback
(
'/^(
\+
.*)/m'
,
static
function
(
$m
)
{
return
TestUtils
::
colorString
(
$m
[
0
],
'red'
);
},
$diffs
);
return
$diffs
;
}
/**
* @param callable $reportFailure
* @param callable $reportSuccess
* @param Stats $stats
* @param Test $item
* @param array $options
* @param string $mode
* @param array $expected
* @param array $actual
* @param ?callable $pre
* @param ?callable $post
* @return bool True if the result was as expected.
*/
public
static
function
printResult
(
callable
$reportFailure
,
callable
$reportSuccess
,
Stats
$stats
,
Test
$item
,
array
$options
,
string
$mode
,
array
$expected
,
array
$actual
,
?
callable
$pre
=
null
,
?
callable
$post
=
null
):
bool
{
$title
=
$item
->
testName
;
// Title may be modified here, so pass it on.
$changeTree
=
$item
->
changetree
;
$suffix
=
''
;
if
(
$mode
===
'selser'
)
{
$suffix
=
' '
.
(
$changeTree
===
[
'manual'
]
?
'[manual]'
:
json_encode
(
$changeTree
)
);
}
elseif
(
$mode
===
'wt2html'
&&
isset
(
$item
->
options
[
'langconv'
]
)
)
{
$title
.=
' [langconv]'
;
}
$title
.=
$suffix
;
$expectFail
=
$item
->
knownFailures
[
$mode
.
$suffix
]
??
null
;
$fail
=
$expected
[
'normal'
]
!==
$actual
[
'normal'
];
// Return whether the test was as expected, independent of pass/fail
$asExpected
=
null
;
if
(
$mode
===
'wt2wt'
)
{
$item
->
wt2wtPassed
=
!
$fail
;
$item
->
wt2wtResult
=
$actual
[
'raw'
];
}
// don't report selser fails when nothing was changed or it's a dup
if
(
$mode
===
'selser'
&&
$changeTree
!==
[
'manual'
]
&&
(
$changeTree
===
[]
||
$item
->
duplicateChange
)
)
{
return
true
;
}
if
(
is_callable
(
$pre
)
)
{
$pre
(
$stats
,
$mode
,
$title
,
$item
->
time
);
}
if
(
$fail
)
{
$stats
->
failedTests
++;
$stats
->
modes
[
$mode
]->
failedTests
++;
$asExpected
=
$reportFailure
(
$stats
,
$item
,
$options
,
$mode
,
$title
,
$actual
,
$expected
,
$expectFail
);
if
(
!
$asExpected
)
{
$stats
->
failedTestsUnexpected
++;
$stats
->
modes
[
$mode
]->
failedTestsUnexpected
++;
}
$stats
->
modes
[
$mode
]->
failList
[]
=
[
'testName'
=>
$item
->
testName
,
'suffix'
=>
$suffix
,
'raw'
=>
$actual
[
'raw'
]
??
null
,
'expected'
=>
$expected
[
'raw'
]
??
null
,
'actualNormalized'
=>
$actual
[
'normal'
]
??
null
,
'unexpected'
=>
!
$asExpected
,
];
}
else
{
$stats
->
passedTests
++;
$stats
->
modes
[
$mode
]->
passedTests
++;
$asExpected
=
$reportSuccess
(
$stats
,
$item
,
$options
,
$mode
,
$title
,
$actual
[
'raw'
],
$expectFail
===
null
);
if
(
!
$asExpected
)
{
$stats
->
passedTestsUnexpected
++;
$stats
->
modes
[
$mode
]->
passedTestsUnexpected
++;
}
}
if
(
is_callable
(
$post
)
)
{
$post
(
$stats
,
$mode
);
}
return
$asExpected
;
}
/**
* Simple function for reporting the start of the tests.
*
* This method can be reimplemented in the options of the ParserTests object.
*/
public
static
function
reportStartOfTests
()
{
}
/**
* Get the actual and expected outputs encoded for XML output.
*
* @inheritDoc getActualExpected
*
* @return string $The XML representation of the actual and expected outputs.
*/
public
static
function
getActualExpectedXML
(
array
$actual
,
array
$expected
,
callable
$getDiff
)
{
$returnStr
=
''
;
$returnStr
.=
"RAW EXPECTED:
\n
"
;
$returnStr
.=
self
::
encodeXml
(
$expected
[
'raw'
]
)
.
"
\n\n
"
;
$returnStr
.=
"RAW RENDERED:
\n
"
;
$returnStr
.=
self
::
encodeXml
(
$actual
[
'raw'
]
)
.
"
\n\n
"
;
$returnStr
.=
"NORMALIZED EXPECTED:
\n
"
;
$returnStr
.=
self
::
encodeXml
(
$expected
[
'normal'
]
)
.
"
\n\n
"
;
$returnStr
.=
"NORMALIZED RENDERED:
\n
"
;
$returnStr
.=
self
::
encodeXml
(
$actual
[
'normal'
]
)
.
"
\n\n
"
;
$returnStr
.=
"DIFF:
\n
"
;
$returnStr
.=
self
::
encodeXml
(
$getDiff
(
$actual
,
$expected
,
false
)
);
return
$returnStr
;
}
/**
* Report the start of the tests output.
*
* @inheritDoc reportStart
*/
public
static
function
reportStartXML
():
void
{
}
/**
* Report the end of the tests output.
*
* @inheritDoc reportSummary
*/
public
static
function
reportSummaryXML
(
array
$modesRan
,
Stats
$stats
,
?
string
$file
,
?
array
$testFilter
,
bool
$knownFailuresChanged
,
array
$options
):
void
{
if
(
$file
===
null
)
{
/* Summary for all tests; not included in XML format output. */
return
;
}
print
'<testsuites file="'
.
$file
.
'">'
;
foreach
(
$modesRan
as
$mode
)
{
print
'<testsuite name="parserTests-'
.
$mode
.
'">'
;
print
$stats
->
modes
[
$mode
]->
result
;
print
'</testsuite>'
;
}
print
'</testsuites>'
;
}
/**
* Print a failure message for a test in XML.
*
* @inheritDoc printFailure
*/
public
static
function
reportFailureXML
(
Stats
$stats
,
Test
$item
,
array
$options
,
string
$mode
,
string
$title
,
array
$actual
,
array
$expected
,
?
string
$expectFail
,
bool
$failureOnly
):
bool
{
$failEle
=
''
;
$knownFailures
=
false
;
if
(
ScriptUtils
::
booleanOption
(
$options
[
'knownFailures'
]
)
&&
$expectFail
!==
null
)
{
// compare with remembered output
$knownFailures
=
$expectFail
===
$actual
[
'raw'
];
}
if
(
!
$knownFailures
)
{
$failEle
.=
"<failure type=
\"
parserTestsDifferenceInOutputFailure
\"
>
\n
"
;
$failEle
.=
self
::
getActualExpectedXML
(
$actual
,
$expected
,
$options
[
'getDiff'
]
);
$failEle
.=
"
\n
</failure>"
;
$stats
->
modes
[
$mode
]->
result
.=
$failEle
;
return
false
;
}
return
true
;
}
/**
* Print a success method for a test in XML.
*
* @inheritDoc printSuccess
*/
public
static
function
reportSuccessXML
(
Stats
$stats
,
Test
$item
,
array
$options
,
string
$mode
,
string
$title
,
string
$raw
,
bool
$expectSuccess
):
bool
{
return
ScriptUtils
::
booleanOption
(
$options
[
'knownFailures'
]
??
null
)
&&
!
$expectSuccess
;
}
private
static
function
pre
(
Stats
$stats
,
string
$mode
,
string
$title
,
array
$time
):
void
{
$testcaseEle
=
'<testcase name="'
.
self
::
encodeXml
(
$title
)
.
'" '
;
$testcaseEle
.=
'assertions="1" '
;
$timeTotal
=
null
;
if
(
$time
&&
$time
[
'end'
]
&&
$time
[
'start'
]
)
{
$timeTotal
=
$time
[
'end'
]
-
$time
[
'start'
];
if
(
!
is_nan
(
$timeTotal
)
)
{
$testcaseEle
.=
'time="'
.
(
(
$time
[
'end'
]
-
$time
[
'start'
]
)
/
1000.0
)
.
'"'
;
}
}
$testcaseEle
.=
'>'
;
$stats
->
modes
[
$mode
]->
result
.=
$testcaseEle
;
}
private
static
function
post
(
Stats
$stats
,
string
$mode
):
void
{
$stats
->
modes
[
$mode
]->
result
.=
'</testcase>'
;
}
/**
* Print the result of a test in XML.
*
* @inheritDoc printResult
*/
public
static
function
reportResultXML
(
...
$args
)
{
$args
[]
=
[
self
::
class
,
'pre'
];
$args
[]
=
[
self
::
class
,
'post'
];
self
::
printResult
(
[
self
::
class
,
'reportFailureXML'
],
[
self
::
class
,
'reportSuccessXML'
],
...
$args
);
// In xml, test all cases always
return
true
;
}
}
$maintClass
=
ParserTests
::
class
;
require_once
PARSOID_RUN_MAINTENANCE_IF_MAIN
;
File Metadata
Details
Attached
Mime Type
text/x-php
Expires
Wed, Aug 19, 13:53 (3 w, 3 d ago)
Storage Engine
local-disk
Storage Format
Raw Data
Storage Handle
05/76/b7a4b39dcf9b10324b3775fd43f9
Default Alt Text
parserTests.php (29 KB)
Attached To
Mode
rMWPROD MediaWiki Production
Attached
Detach File
Event Timeline
Log In to Comment