Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
50.00% |
44 / 88 |
|
0.00% |
0 / 2 |
CRAP | |
0.00% |
0 / 1 |
TestFileReader | |
50.00% |
44 / 88 |
|
0.00% |
0 / 2 |
118.12 | |
0.00% |
0 / 1 |
read | |
0.00% |
0 / 11 |
|
0.00% |
0 / 1 |
6 | |||
__construct | |
57.14% |
44 / 77 |
|
0.00% |
0 / 1 |
74.20 |
1 | <?php |
2 | declare( strict_types = 1 ); |
3 | |
4 | namespace Wikimedia\Parsoid\ParserTests; |
5 | |
6 | class TestFileReader { |
7 | /** @var array File-level options and requirements for these parser tests */ |
8 | public $fileOptions = []; |
9 | |
10 | /** @var Test[] */ |
11 | public $testCases = []; |
12 | |
13 | /** @var Article[] */ |
14 | public $articles = []; |
15 | |
16 | /** |
17 | * @var ?string Path to known failures file, or null if does not exist |
18 | * or is not readable. |
19 | */ |
20 | public $knownFailuresPath; |
21 | |
22 | /** |
23 | * Read and parse a parserTest file. |
24 | * @param string $testFilePath The parserTest file to read |
25 | * @param ?callable(string) $warnFunc An optional function to use to |
26 | * report the use of deprecated test section names |
27 | * @param ?callable(string):string $normalizeFunc An optional function |
28 | * to use to normalize article titles for uniqueness testing |
29 | * @param ?string $knownFailuresInfix qualifier for the known failures file |
30 | * (usually "standalone" to distinguish from the failures from the default |
31 | * integrated test run) |
32 | * @return TestFileReader |
33 | */ |
34 | public static function read( |
35 | string $testFilePath, |
36 | ?callable $warnFunc = null, |
37 | ?callable $normalizeFunc = null, |
38 | ?string $knownFailuresInfix = null |
39 | ): TestFileReader { |
40 | $info = pathinfo( $testFilePath ); |
41 | $knownFailuresPath = $info['dirname'] . '/' . $info['filename'] . |
42 | ( $knownFailuresInfix ? "-$knownFailuresInfix" : '' ) . |
43 | '-knownFailures.json'; |
44 | $reader = new self( |
45 | $testFilePath, |
46 | $knownFailuresPath, |
47 | $warnFunc, |
48 | $normalizeFunc |
49 | ); |
50 | return $reader; |
51 | } |
52 | |
53 | /** |
54 | * @param string $testFilePath The parserTest file to read |
55 | * @param ?string $knownFailuresPath The known failures file to read |
56 | * (or null, if there is no readable known failures file) |
57 | * @param ?callable(string) $warnFunc An optional function to use to |
58 | * report the use of deprecated test section names |
59 | * @param ?callable(string):string $normalizeFunc An optional function |
60 | * to use to normalize article titles for uniqueness testing |
61 | */ |
62 | private function __construct( |
63 | string $testFilePath, ?string $knownFailuresPath, |
64 | ?callable $warnFunc = null, ?callable $normalizeFunc = null |
65 | ) { |
66 | $this->knownFailuresPath = $knownFailuresPath && is_readable( $knownFailuresPath ) ? |
67 | $knownFailuresPath : null; |
68 | $parsedTests = Grammar::load( $testFilePath ); |
69 | // Start off with any comments before `!! format` |
70 | $rawTestItems = $parsedTests[0]; |
71 | $testFormat = $parsedTests[1]; |
72 | if ( $testFormat != null ) { |
73 | // If `!!format` was present, existing comments applied to the |
74 | // format declaration, not the first item. |
75 | $rawTestItems = []; |
76 | } |
77 | |
78 | // Add any comments after `!! format` |
79 | array_splice( $rawTestItems, count( $rawTestItems ), 0, $parsedTests[2] ); |
80 | if ( $parsedTests[3] == null ) { |
81 | $this->fileOptions = []; |
82 | } else { |
83 | $this->fileOptions = $parsedTests[3]['text']; |
84 | // If `!!options` was present, existing comments applied to the |
85 | // file options, not the first item. |
86 | $rawTestItems = []; |
87 | } |
88 | |
89 | // Add the rest of the comments and items appearing after `!!options` |
90 | array_splice( $rawTestItems, count( $rawTestItems ), 0, $parsedTests[4] ); |
91 | |
92 | if ( $testFormat !== null ) { |
93 | if ( isset( $this->fileOptions['version'] ) ) { |
94 | ( new Item( $parsedTests[3] ) )->error( 'Duplicate version specification' ); |
95 | } else { |
96 | $this->fileOptions['version'] = $testFormat['text']; |
97 | } |
98 | } |
99 | $this->fileOptions['version'] ??= '1'; |
100 | |
101 | $knownFailures = $this->knownFailuresPath !== null ? |
102 | json_decode( file_get_contents( $knownFailuresPath ), true ) : |
103 | null; |
104 | |
105 | $testNames = []; |
106 | $articleTitles = []; |
107 | |
108 | $lastComment = ''; |
109 | foreach ( $rawTestItems as $item ) { |
110 | if ( $item['type'] === 'article' ) { |
111 | $art = new Article( $item, $lastComment ); |
112 | $key = $normalizeFunc ? $normalizeFunc( $art->title ) : $art->title; |
113 | if ( isset( $articleTitles[$key] ) ) { |
114 | $art->error( 'Duplicate article', $art->title ); |
115 | } |
116 | $articleTitles[$key] = true; |
117 | $this->articles[] = $art; |
118 | $lastComment = ''; |
119 | } elseif ( $item['type'] === 'test' ) { |
120 | $test = new Test( |
121 | $item, |
122 | $knownFailures[$item['testName']] ?? [], |
123 | $lastComment, |
124 | $warnFunc |
125 | ); |
126 | if ( isset( $testNames[$test->testName] ) ) { |
127 | $test->error( 'Duplicate test name', $test->testName ); |
128 | } |
129 | $testNames[$test->testName] = true; |
130 | $this->testCases[] = $test; |
131 | $lastComment = ''; |
132 | } elseif ( $item['type'] === 'comment' ) { |
133 | $lastComment .= $item['text']; |
134 | } elseif ( $item['type'] === 'hooks' ) { |
135 | foreach ( explode( "\n", $item['text'] ) as $line ) { |
136 | $this->fileOptions['requirements'][] = [ |
137 | 'type' => 'hook', |
138 | 'name' => trim( $line ), |
139 | ]; |
140 | } |
141 | $lastComment = ''; |
142 | } elseif ( $item['type'] === 'functionhooks' ) { |
143 | foreach ( explode( "\n", $item['text'] ) as $line ) { |
144 | $this->fileOptions['requirements'][] = [ |
145 | 'type' => 'functionHook', |
146 | 'name' => trim( $line ), |
147 | ]; |
148 | } |
149 | $lastComment = ''; |
150 | } elseif ( $item['type'] === 'line' ) { |
151 | if ( !empty( trim( $item['text'] ) ) ) { |
152 | ( new Item( $item ) )->error( 'Invalid line', $item['text'] ); |
153 | } |
154 | } else { |
155 | ( new Item( $item ) )->error( 'Unknown item type', $item['type'] ); |
156 | } |
157 | } |
158 | // Convenience function to expand 'requirements' |
159 | if ( isset( $this->fileOptions['requirements'] ) ) { |
160 | if ( !is_array( $this->fileOptions['requirements'] ) ) { |
161 | $this->fileOptions['requirements'] = [ |
162 | $this->fileOptions['requirements'] |
163 | ]; |
164 | } |
165 | foreach ( $this->fileOptions['requirements'] as &$item ) { |
166 | if ( is_string( $item ) ) { |
167 | $item = [ |
168 | 'type' => 'hook', |
169 | 'name' => "$item", |
170 | ]; |
171 | } |
172 | } |
173 | unset( $item ); |
174 | } |
175 | } |
176 | } |