+
+class RemoteTestRecorder extends TestRecorder {
+ function start() {
+ parent::start();
+ $this->results = array();
+ $this->ping( 'running' );
+ }
+
+ function record( $test, $result ) {
+ parent::record( $test, $result );
+ $this->results[$test] = (bool)$result;
+ }
+
+ function end() {
+ $this->ping( 'complete', $this->results );
+ parent::end();
+ }
+
+ /**
+ * Inform a CodeReview instance that we've started or completed a test run...
+ *
+ * @param $status string: "running" - tell it we've started
+ * "complete" - provide test results array
+ * "abort" - something went horribly awry
+ * @param $results array of test name => true/false
+ */
+ function ping( $status, $results = false ) {
+ global $wgParserTestRemote, $IP;
+
+ $remote = $wgParserTestRemote;
+ $revId = SpecialVersion::getSvnRevision( $IP );
+ $jsonResults = FormatJson::encode( $results );
+
+ if ( !$remote ) {
+ print "Can't do remote upload without configuring \$wgParserTestRemote!\n";
+ exit( 1 );
+ }
+
+ // Generate a hash MAC to validate our credentials
+ $message = array(
+ $remote['repo'],
+ $remote['suite'],
+ $revId,
+ $status,
+ );
+ if ( $status == "complete" ) {
+ $message[] = $jsonResults;
+ }
+ $hmac = hash_hmac( "sha1", implode( "|", $message ), $remote['secret'] );
+
+ $postData = array(
+ 'action' => 'codetestupload',
+ 'format' => 'json',
+ 'repo' => $remote['repo'],
+ 'suite' => $remote['suite'],
+ 'rev' => $revId,
+ 'status' => $status,
+ 'hmac' => $hmac,
+ );
+ if ( $status == "complete" ) {
+ $postData['results'] = $jsonResults;
+ }
+ $response = $this->post( $remote['api-url'], $postData );
+
+ if ( $response === false ) {
+ print "CodeReview info upload failed to reach server.\n";
+ exit( 1 );
+ }
+ $responseData = FormatJson::decode( $response, true );
+ if ( !is_array( $responseData ) ) {
+ print "CodeReview API response not recognized...\n";
+ wfDebug( "Unrecognized CodeReview API response: $response\n" );
+ exit( 1 );
+ }
+ if ( isset( $responseData['error'] ) ) {
+ $code = $responseData['error']['code'];
+ $info = $responseData['error']['info'];
+ print "CodeReview info upload failed: $code $info\n";
+ exit( 1 );
+ }
+ }
+
+ function post( $url, $data ) {
+ return Http::post( $url, array( 'postData' => $data ) );
+ }
+}
+
+class TestFileIterator implements Iterator {
+ private $file;
+ private $fh;
+ private $parser;
+ private $index = 0;
+ private $test;
+ private $lineNum;
+ private $eof;
+
+ function __construct( $file, $parser = null ) {
+ global $IP;
+
+ $this->file = $file;
+ $this->fh = fopen( $this->file, "rt" );
+ if ( !$this->fh ) {
+ wfDie( "Couldn't open file '$file'\n" );
+ }
+
+ $this->parser = $parser;
+
+ if ( $this->parser ) $this->parser->showRunFile( wfRelativePath( $this->file, $IP ) );
+ $this->lineNum = $this->index = 0;
+ }
+
+ function setParser( ParserTest $parser ) {
+ $this->parser = $parser;
+ }
+
+ function rewind() {
+ if ( fseek( $this->fh, 0 ) ) {
+ wfDie( "Couldn't fseek to the start of '$this->file'\n" );
+ }
+ $this->index = -1;
+ $this->lineNum = 0;
+ $this->eof = false;
+ $this->next();
+
+ return true;
+ }
+
+ function current() {
+ return $this->test;
+ }
+
+ function key() {
+ return $this->index;
+ }
+
+ function next() {
+ if ( $this->readNextTest() ) {
+ $this->index++;
+ return true;
+ } else {
+ $this->eof = true;
+ }
+ }
+
+ function valid() {
+ return $this->eof != true;
+ }
+
+ function readNextTest() {
+ $data = array();
+ $section = null;
+
+ while ( false !== ( $line = fgets( $this->fh ) ) ) {
+ $this->lineNum++;
+ $matches = array();
+ if ( preg_match( '/^!!\s*(\w+)/', $line, $matches ) ) {
+ $section = strtolower( $matches[1] );
+ if ( $section == 'endarticle' ) {
+ if ( !isset( $data['text'] ) ) {
+ wfDie( "'endarticle' without 'text' at line {$this->lineNum} of $this->file\n" );
+ }
+ if ( !isset( $data['article'] ) ) {
+ wfDie( "'endarticle' without 'article' at line {$this->lineNum} of $this->file\n" );
+ }
+ if ( $this->parser ) {
+ $this->parser->addArticle( $this->parser->chomp( $data['article'] ), $this->parser->chomp( $data['text'] ),
+ $this->lineNum );
+ }
+ $data = array();
+ $section = null;
+ continue;
+ }
+ if ( $section == 'endhooks' ) {
+ if ( !isset( $data['hooks'] ) ) {
+ wfDie( "'endhooks' without 'hooks' at line {$this->lineNum} of $this->file\n" );
+ }
+ foreach ( explode( "\n", $data['hooks'] ) as $line ) {
+ $line = trim( $line );
+ if ( $line ) {
+ if ( $this->parser && !$this->parser->requireHook( $line ) ) {
+ return false;
+ }
+ }
+ }
+ $data = array();
+ $section = null;
+ continue;
+ }
+ if ( $section == 'endfunctionhooks' ) {
+ if ( !isset( $data['functionhooks'] ) ) {
+ wfDie( "'endfunctionhooks' without 'functionhooks' at line {$this->lineNum} of $this->file\n" );
+ }
+ foreach ( explode( "\n", $data['functionhooks'] ) as $line ) {
+ $line = trim( $line );
+ if ( $line ) {
+ if ( $this->parser && !$this->parser->requireFunctionHook( $line ) ) {
+ return false;
+ }
+ }
+ }
+ $data = array();
+ $section = null;
+ continue;
+ }
+ if ( $section == 'end' ) {
+ if ( !isset( $data['test'] ) ) {
+ wfDie( "'end' without 'test' at line {$this->lineNum} of $this->file\n" );
+ }
+ if ( !isset( $data['input'] ) ) {
+ wfDie( "'end' without 'input' at line {$this->lineNum} of $this->file\n" );
+ }
+ if ( !isset( $data['result'] ) ) {
+ wfDie( "'end' without 'result' at line {$this->lineNum} of $this->file\n" );
+ }
+ if ( !isset( $data['options'] ) ) {
+ $data['options'] = '';
+ }
+ if ( !isset( $data['config'] ) )
+ $data['config'] = '';
+
+ if ( $this->parser
+ && ( ( preg_match( '/\\bdisabled\\b/i', $data['options'] ) && !$this->parser->runDisabled )
+ || !preg_match( "/" . $this->parser->regex . "/i", $data['test'] ) ) ) {
+ # disabled test
+ $data = array();
+ $section = null;
+ continue;
+ }
+ global $wgUseTeX;
+ if ( $this->parser &&
+ preg_match( '/\\bmath\\b/i', $data['options'] ) && !$wgUseTeX ) {
+ # don't run math tests if $wgUseTeX is set to false in LocalSettings
+ $data = array();
+ $section = null;
+ continue;
+ }
+
+ if ( $this->parser ) {
+ $this->test = array(
+ 'test' => $this->parser->chomp( $data['test'] ),
+ 'input' => $this->parser->chomp( $data['input'] ),
+ 'result' => $this->parser->chomp( $data['result'] ),
+ 'options' => $this->parser->chomp( $data['options'] ),
+ 'config' => $this->parser->chomp( $data['config'] ) );
+ } else {
+ $this->test['test'] = $data['test'];
+ }
+ return true;
+ }
+ if ( isset ( $data[$section] ) ) {
+ wfDie( "duplicate section '$section' at line {$this->lineNum} of $this->file\n" );
+ }
+ $data[$section] = '';
+ continue;
+ }
+ if ( $section ) {
+ $data[$section] .= $line;
+ }
+ }
+ return false;
+ }
+}
+