Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 2
CRAP
53.66% covered (warning)
53.66%
44 / 82
TestFileReader
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 2
133.91
53.66% covered (warning)
53.66%
44 / 82
 read
0.00% covered (danger)
0.00%
0 / 1
12
0.00% covered (danger)
0.00%
0 / 9
 __construct
0.00% covered (danger)
0.00%
0 / 1
81.73
60.27% covered (warning)
60.27%
44 / 73
<?php
declare( strict_types = 1 );
namespace Wikimedia\Parsoid\ParserTests;
class TestFileReader {
    /** @var array File-level options and requirements for these parser tests */
    public $fileOptions = [];
    /** @var Test[] */
    public $testCases = [];
    /** @var Article[] */
    public $articles = [];
    /**
     * @var ?string Path to known failures file, or null if does not exist
     *   or is not readable.
     */
    public $knownFailuresPath;
    /**
     * Read and parse a parserTest file.
     * @param string $testFilePath The parserTest file to read
     * @param ?callable(string) $warnFunc An optional function to use to
     *   report the use of deprecated test section names
     * @param ?callable(string):string $normalizeFunc An optional function
     *   to use to normalize article titles for uniqueness testing
     * @return TestFileReader
     */
    public static function read(
        string $testFilePath,
        ?callable $warnFunc = null,
        ?callable $normalizeFunc = null
    ): TestFileReader {
        $info = pathinfo( $testFilePath );
        $knownFailuresPath = $info['dirname'] . '/' . $info['filename'] .
            '-knownFailures.json';
        $reader = new self(
            $testFilePath,
            $knownFailuresPath,
            $warnFunc,
            $normalizeFunc
        );
        return $reader;
    }
    /**
     * @param string $testFilePath The parserTest file to read
     * @param ?string $knownFailuresPath The known failures file to read
     *   (or null, if there is no readable known failures file)
     * @param ?callable(string) $warnFunc An optional function to use to
     *   report the use of deprecated test section names
     * @param ?callable(string):string $normalizeFunc An optional function
     *   to use to normalize article titles for uniqueness testing
     */
    private function __construct(
        string $testFilePath, ?string $knownFailuresPath,
        ?callable $warnFunc = null, ?callable $normalizeFunc = null
    ) {
        $this->knownFailuresPath = $knownFailuresPath && is_readable( $knownFailuresPath ) ?
            $knownFailuresPath : null;
        $parsedTests = Grammar::load( $testFilePath );
        // Start off with any comments before `!! format`
        $rawTestItems = $parsedTests[0];
        $testFormat = $parsedTests[1];
        if ( $testFormat != null ) {
            // If `!!format` was present, existing comments applied to the
            // format declaration, not the first item.
            $rawTestItems = [];
        }
        // Add any comments after `!! format`
        array_splice( $rawTestItems, count( $rawTestItems ), 0, $parsedTests[2] );
        if ( $parsedTests[3] == null ) {
            $this->fileOptions = [];
        } else {
            $this->fileOptions = $parsedTests[3]['text'];
            // If `!!options` was present, existing comments applied to the
            // file options, not the first item.
            $rawTestItems = [];
        }
        // Add the rest of the comments and items appearing after `!!options`
        array_splice( $rawTestItems, count( $rawTestItems ), 0, $parsedTests[4] );
        if ( $testFormat !== null ) {
            if ( isset( $this->fileOptions['version'] ) ) {
                ( new Item( $parsedTests[3] ) )->error( 'Duplicate version specification' );
            } else {
                $this->fileOptions['version'] = $testFormat['text'];
            }
        }
        if ( !isset( $this->fileOptions['version'] ) ) {
            $this->fileOptions['version'] = '1';
        }
        $knownFailures = $this->knownFailuresPath !== null ?
            json_decode( file_get_contents( $knownFailuresPath ), true ) :
            null;
        $testNames = [];
        $articleTitles = [];
        $lastComment = '';
        foreach ( $rawTestItems as $item ) {
            if ( $item['type'] === 'article' ) {
                $art = new Article( $item, $lastComment );
                $key = $normalizeFunc ? $normalizeFunc( $art->title ) : $art->title;
                if ( isset( $articleTitles[$key] ) ) {
                    $art->error( 'Duplicate article', $art->title );
                }
                $articleTitles[$key] = true;
                $this->articles[] = $art;
                $lastComment = '';
            } elseif ( $item['type'] === 'test' ) {
                $test = new Test(
                    $item,
                    $knownFailures[$item['testName']] ?? [],
                    $lastComment,
                    $warnFunc
                );
                if ( isset( $testNames[$test->testName] ) ) {
                    $test->error( 'Duplicate test name', $test->testName );
                }
                $testNames[$test->testName] = true;
                $this->testCases[] = $test;
                $lastComment = '';
            } elseif ( $item['type'] === 'comment' ) {
                $lastComment .= $item['text'];
            } elseif ( $item['type'] === 'hooks' ) {
                foreach ( explode( "\n", $item['text'] ) as $line ) {
                    $this->fileOptions['requirements'][] = [
                        'type' => 'hook',
                        'name' => trim( $line ),
                    ];
                }
                $lastComment = '';
            } elseif ( $item['type'] === 'functionhooks' ) {
                foreach ( explode( "\n", $item['text'] ) as $line ) {
                    $this->fileOptions['requirements'][] = [
                        'type' => 'functionHook',
                        'name' => trim( $line ),
                    ];
                }
                $lastComment = '';
            } elseif ( $item['type'] === 'line' ) {
                if ( !empty( trim( $item['text'] ) ) ) {
                    ( new Item( $item ) )->error( 'Invalid line', $item['text'] );
                }
            } else {
                ( new Item( $item ) )->error( 'Unknown item type', $item['type'] );
            }
        }
        // Convenience function to expand 'requirements'
        if ( isset( $this->fileOptions['requirements'] ) ) {
            if ( !is_array( $this->fileOptions['requirements'] ) ) {
                $this->fileOptions['requirements'] = [
                    $this->fileOptions['requirements']
                ];
            }
            foreach ( $this->fileOptions['requirements'] as &$item ) {
                if ( is_string( $item ) ) {
                    $item = [
                        'type' => 'hook',
                        'name' => "$item",
                    ];
                }
            }
            unset( $item );
        }
    }
}