OLD | NEW |
1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file |
2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
4 | 4 |
5 // The following set of variables should be set by the caller that | 5 // The following set of variables should be set by the caller that |
6 // #sources this file. | 6 // #sources this file. |
7 /** Whether to include elapsed time. */ | 7 /** Whether to include elapsed time. */ |
8 | 8 |
9 part of test_controller; | 9 part of test_controller; |
10 | 10 |
(...skipping 106 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
117 } | 117 } |
118 } | 118 } |
119 | 119 |
120 complete() { | 120 complete() { |
121 if (summarize) { | 121 if (summarize) { |
122 printSummary(testfile, passCount, failCount, errorCount); | 122 printSummary(testfile, passCount, failCount, errorCount); |
123 } | 123 } |
124 notifyDone(failCount > 0 ? -1 : 0); | 124 notifyDone(failCount > 0 ? -1 : 0); |
125 } | 125 } |
126 | 126 |
| 127 /* |
| 128 * Run an external process [cmd] with command line arguments [args]. |
| 129 * [timeout] can be used to forcefully terminate the process after |
| 130 * some number of seconds. This is used by runCommand and startProcess. |
| 131 * If [procId] is non-zero (i.e. called from startProcess) then a reference |
| 132 * to the [Process] will be put in a map with key [procId]; in this case |
| 133 * the process can be terminated later by calling [stopProcess] and |
| 134 * passing in the [procId]. |
| 135 * [outputMonitor] is an optional function that will be called back with each |
| 136 * line of output from the process. |
| 137 * Returns a [Future] for when the process terminates. |
| 138 */ |
| 139 Future _processHelper(String command, List<String> args, |
| 140 List stdout, List stderr, |
| 141 int timeout, int procId, Function outputMonitor, bool raw) { |
| 142 var timer = null; |
| 143 return Process.start(command, args).then((process) { |
| 144 |
| 145 timer = new Timer(new Duration(seconds: timeout), () { |
| 146 timer = null; |
| 147 process.kill(); |
| 148 }); |
| 149 |
| 150 if (raw) { |
| 151 process.stdout.listen((c) { stdout.addAll(c); }); |
| 152 } else { |
| 153 _pipeStream(process.stdout, stdout, outputMonitor); |
| 154 } |
| 155 _pipeStream(process.stderr, stderr, outputMonitor); |
| 156 return process.exitCode; |
| 157 }).then((exitCode) { |
| 158 if (timer != null) { |
| 159 timer.cancel(); |
| 160 } |
| 161 return exitCode; |
| 162 }) |
| 163 .catchError((e) { |
| 164 stderr.add("#Error starting process $command: ${e.error}"); |
| 165 }); |
| 166 } |
| 167 |
| 168 void _pipeStream(Stream stream, List<String> destination, |
| 169 Function outputMonitor) { |
| 170 stream |
| 171 .transform(new StringDecoder()) |
| 172 .transform(new LineTransformer()) |
| 173 .listen((String line) { |
| 174 if (outputMonitor != null) { |
| 175 outputMonitor(line); |
| 176 } |
| 177 destination.add(line); |
| 178 }); |
| 179 } |
| 180 |
| 181 /** |
| 182 * Run an external process [cmd] with command line arguments [args]. |
| 183 * [timeout] can be used to forcefully terminate the process after |
| 184 * some number of seconds. |
| 185 * Returns a [Future] for when the process terminates. |
| 186 */ |
| 187 Future runCommand(String command, List<String> args, |
| 188 List stdout, List stderr, |
| 189 {int timeout: 300, Function outputMonitor, |
| 190 bool raw: false}) { |
| 191 return _processHelper(command, args, stdout, stderr, |
| 192 timeout, 0, outputMonitor, raw); |
| 193 } |
| 194 |
| 195 String parseLabel(String line) { |
| 196 if (line.startsWith('CONSOLE MESSAGE')) { |
| 197 var idx = line.indexOf('#TEST '); |
| 198 if (idx > 0) { |
| 199 return line.substring(idx + 6); |
| 200 } |
| 201 } |
| 202 return null; |
| 203 } |
| 204 |
127 runTextLayoutTest(testNum) { | 205 runTextLayoutTest(testNum) { |
128 var url = '$baseUrl?test=$testNum'; | 206 var url = '$baseUrl?test=$testNum'; |
129 var stdout = new List(); | 207 var stdout = new List(); |
| 208 var stderr = new List(); |
130 start = new DateTime.now(); | 209 start = new DateTime.now(); |
131 Process.start(drt, [url]).then((process) { | 210 runCommand(drt, [url], stdout, stderr).then((e) { |
132 // Drain stderr to not leak resources. | 211 if (stdout.length > 0 && stdout[stdout.length-1].startsWith('#EOF')) { |
133 process.stderr.onData = process.stderr.read; | 212 stdout.removeLast(); |
134 StringInputStream stdoutStringStream = | 213 } |
135 new StringInputStream(process.stdout); | 214 var done = false; |
136 stdoutStringStream.onLine = () { | 215 var i = 0; |
137 if (stdoutStringStream.closed) return; | 216 var label = null; |
138 var line = stdoutStringStream.readLine(); | 217 var contentMarker = 'layer at '; |
139 while (null != line) { | 218 while (i < stdout.length) { |
140 stdout.add(line); | 219 if (label == null && (label = parseLabel(stdout[i])) != null) { |
141 line = stdoutStringStream.readLine(); | 220 if (label == 'NONEXISTENT') { |
| 221 complete(); |
| 222 return; |
| 223 } |
| 224 } else if (stdout[i].startsWith(contentMarker)) { |
| 225 if (label == null) { |
| 226 complete(); |
| 227 return; |
| 228 } |
| 229 var expectedFileName = |
| 230 '$sourceDir${Platform.pathSeparator}' |
| 231 '${label.replaceAll("###", "_") |
| 232 .replaceAll(new RegExp("[^A-Za-z0-9]"),"_")}.txt'; |
| 233 var expected = new File(expectedFileName); |
| 234 if (regenerate) { |
| 235 var osink = expected.openWrite(); |
| 236 while (i < stdout.length) { |
| 237 osink.write(stdout[i]); |
| 238 osink.write('\n'); |
| 239 i++; |
| 240 } |
| 241 osink.close(); |
| 242 pass(start, label); |
| 243 } else if (!expected.existsSync()) { |
| 244 fail(start, label, 'No expectation file'); |
| 245 } else { |
| 246 var lines = expected.readAsLinesSync(); |
| 247 var actualLength = stdout.length - i; |
| 248 var compareCount = min(lines.length, actualLength); |
| 249 var match = true; |
| 250 for (var j = 0; j < compareCount; j++) { |
| 251 if (lines[j] != stdout[i + j]) { |
| 252 fail(start, label, 'Expectation differs at line ${j + 1}'); |
| 253 match = false; |
| 254 break; |
| 255 } |
| 256 } |
| 257 if (match) { |
| 258 if (lines.length != actualLength) { |
| 259 fail(start, label, 'Expectation file has wrong length'); |
| 260 } else { |
| 261 pass(start, label); |
| 262 } |
| 263 } |
| 264 } |
| 265 done = true; |
| 266 break; |
142 } | 267 } |
143 }; | 268 i++; |
144 process.onExit = (exitCode) { | 269 } |
145 process.close(); | 270 if (label != null) { |
146 if (stdout.length > 0 && stdout[stdout.length-1].startsWith('#EOF')) { | 271 if (!done) error(start, label, 'Failed to parse output'); |
147 stdout.removeLast(); | 272 runTextLayoutTest(testNum + 1); |
| 273 } |
| 274 }); |
| 275 } |
| 276 |
| 277 runPixelLayoutTest(int testNum) { |
| 278 var url = '$baseUrl?test=$testNum'; |
| 279 var stdout = new List(); |
| 280 var stderr = new List(); |
| 281 start = new DateTime.now(); |
| 282 runCommand(drt, ["$url'-p"], stdout, stderr, raw:true).then((exitCode) { |
| 283 var contentMarker = 'Content-Length: '; |
| 284 var eol = '\n'.codeUnitAt(0); |
| 285 var pos = 0; |
| 286 var label = null; |
| 287 var done = false; |
| 288 |
| 289 while(pos < stdout.length) { |
| 290 StringBuffer sb = new StringBuffer(); |
| 291 while (pos < stdout.length && stdout[pos] != eol) { |
| 292 sb.writeCharCode(stdout[pos++]); |
148 } | 293 } |
149 var done = false; | 294 if (++pos >= stdout.length && line == '') break; |
150 var i = 0; | 295 var line = sb.toString(); |
151 var label = null; | 296 |
152 var labelMarker = 'CONSOLE MESSAGE: #TEST '; | 297 if (label == null && (label = parseLabel(line)) != null) { |
153 var contentMarker = 'layer at '; | 298 if (label == 'NONEXISTENT') { |
154 while (i < stdout.length) { | 299 complete(); |
155 if (label == null && stdout[i].startsWith(labelMarker)) { | 300 } |
156 label = stdout[i].substring(labelMarker.length); | 301 } else if (line.startsWith(contentMarker)) { |
157 if (label == 'NONEXISTENT') { | 302 if (label == null) { |
158 complete(); | 303 complete(); |
159 } | 304 } |
160 } else if (stdout[i].startsWith(contentMarker)) { | 305 var len = int.parse(line.substring(contentMarker.length)); |
161 if (label == null) { | 306 var expectedFileName = |
162 complete(); | 307 '$sourceDir${Platform.pathSeparator}' |
163 } | 308 '${label.replaceAll("###","_"). |
164 var expectedFileName = | 309 replaceAll(new RegExp("[^A-Za-z0-9]"),"_")}.png'; |
165 '$sourceDir${Platform.pathSeparator}' | 310 var expected = new File(expectedFileName); |
166 '${label.replaceAll("###", "_") | 311 if (regenerate) { |
167 .replaceAll(new RegExp("[^A-Za-z0-9]"),"_")}.txt'; | 312 var osink = expected.openWrite(); |
168 var expected = new File(expectedFileName); | 313 stdout.removeRange(0, pos); |
169 if (regenerate) { | 314 stdout.length = len; |
170 var ostream = expected.openOutputStream(FileMode.WRITE); | 315 osink.add(stdout); |
171 while (i < stdout.length) { | 316 osink.close(); |
172 ostream.writeString(stdout[i]); | 317 pass(start, label); |
173 ostream.writeString('\n'); | 318 } else if (!expected.existsSync()) { |
174 i++; | 319 fail(start, label, 'No expectation file'); |
175 } | 320 } else { |
176 ostream.close(); | 321 var bytes = expected.readAsBytesSync(); |
177 pass(start, label); | 322 if (bytes.length != len) { |
178 } else if (!expected.existsSync()) { | 323 fail(start, label, 'Expectation file has wrong length'); |
179 fail(start, label, 'No expectation file'); | |
180 } else { | 324 } else { |
181 var lines = expected.readAsLinesSync(); | |
182 var actualLength = stdout.length - i; | |
183 var compareCount = min(lines.length, actualLength); | |
184 var match = true; | 325 var match = true; |
185 for (var j = 0; j < compareCount; j++) { | 326 for (var j = 0; j < len; j++) { |
186 if (lines[j] != stdout[i + j]) { | 327 if (bytes[j] != stdout[pos + j]) { |
187 fail(start, label, 'Expectation differs at line ${j + 1}'); | 328 fail(start, label, 'Expectation differs at byte ${j + 1}'); |
188 match = false; | 329 match = false; |
189 break; | 330 break; |
190 } | 331 } |
191 } | 332 } |
192 if (match) { | 333 if (match) pass(start, label); |
193 if (lines.length != actualLength) { | 334 } |
194 fail(start, label, 'Expectation file has wrong length'); | 335 } |
195 } else { | 336 done = true; |
196 pass(start, label); | 337 break; |
197 } | |
198 } | |
199 } | |
200 done = true; | |
201 break; | |
202 } | |
203 i++; | |
204 } | 338 } |
205 if (label != null) { | 339 } |
206 if (!done) error(start, label, 'Failed to parse output'); | 340 if (label != null) { |
207 runTextLayoutTest(testNum + 1); | 341 if (!done) error(start, label, 'Failed to parse output'); |
208 } | 342 runPixelLayoutTest(testNum + 1); |
209 }; | 343 } |
210 }); | 344 }); |
211 } | 345 } |
212 | 346 |
213 runPixelLayoutTest(int testNum) { | |
214 var url = '$baseUrl?test=$testNum'; | |
215 var stdout = new List(); | |
216 start = new DateTime.now(); | |
217 Process.start(drt, ["$url'-p"]).then((process) { | |
218 // Drain stderr to not leak resources. | |
219 process.stderr.onData = process.stderr.read; | |
220 ListInputStream stdoutStream = process.stdout; | |
221 stdoutStream.onData = () { | |
222 if (!stdoutStream.closed) { | |
223 var data = stdoutStream.read(); | |
224 stdout.addAll(data); | |
225 } | |
226 }; | |
227 stdoutStream.onError = (e) { | |
228 print(e); | |
229 }; | |
230 process.onExit = (exitCode) { | |
231 stdout.addAll(process.stdout.read()); | |
232 process.close(); | |
233 var labelMarker = 'CONSOLE MESSAGE: #TEST '; | |
234 var contentMarker = 'Content-Length: '; | |
235 var eol = '\n'.codeUnitAt(0); | |
236 var pos = -1; | |
237 var label = null; | |
238 var done = false; | |
239 | |
240 while(pos < stdout.length) { | |
241 var idx = stdout.indexOf(eol, ++pos); | |
242 if (idx < 0) break; | |
243 StringBuffer sb = new StringBuffer(); | |
244 for (var i = pos; i < idx; i++) { | |
245 sb.writeCharCode(stdout[i]); | |
246 } | |
247 var line = sb.toString(); | |
248 | |
249 if (label == null && line.startsWith(labelMarker)) { | |
250 label = line.substring(labelMarker.length); | |
251 if (label == 'NONEXISTENT') { | |
252 complete(); | |
253 } | |
254 } else if (line.startsWith(contentMarker)) { | |
255 if (label == null) { | |
256 complete(); | |
257 } | |
258 var len = int.parse(line.substring(contentMarker.length)); | |
259 pos = idx + 1; | |
260 var expectedFileName = | |
261 '$sourceDir${Platform.pathSeparator}' | |
262 '${label.replaceAll("###","_"). | |
263 replaceAll(new RegExp("[^A-Za-z0-9]"),"_")}.png'; | |
264 var expected = new File(expectedFileName); | |
265 if (regenerate) { | |
266 var ostream = expected.openOutputStream(FileMode.WRITE); | |
267 ostream.writeFrom(stdout, pos, len); | |
268 ostream.close(); | |
269 pass(start, label); | |
270 } else if (!expected.existsSync()) { | |
271 fail(start, label, 'No expectation file'); | |
272 } else { | |
273 var bytes = expected.readAsBytesSync(); | |
274 if (bytes.length != len) { | |
275 fail(start, label, 'Expectation file has wrong length'); | |
276 } else { | |
277 var match = true; | |
278 for (var j = 0; j < len; j++) { | |
279 if (bytes[j] != stdout[pos + j]) { | |
280 fail(start, label, 'Expectation differs at byte ${j + 1}'); | |
281 match = false; | |
282 break; | |
283 } | |
284 } | |
285 if (match) pass(start, label); | |
286 } | |
287 } | |
288 done = true; | |
289 break; | |
290 } | |
291 pos = idx; | |
292 } | |
293 if (label != null) { | |
294 if (!done) error(start, label, 'Failed to parse output'); | |
295 runPixelLayoutTest(testNum + 1); | |
296 } | |
297 }; | |
298 }); | |
299 } | |
300 | |
301 void init() { | 347 void init() { |
302 // Get the name of the directory that has the expectation files | 348 // Get the name of the directory that has the expectation files |
303 // (by stripping .dart suffix from test file path). | 349 // (by stripping .dart suffix from test file path). |
304 // Create it if it does not exist. | 350 // Create it if it does not exist. |
305 sourceDir = testfile.substring(0, testfile.length - 5); | 351 sourceDir = testfile.substring(0, testfile.length - 5); |
306 if (regenerate) { | 352 if (regenerate) { |
307 var d = new Directory(sourceDir); | 353 var d = new Directory(sourceDir); |
308 if (!d.existsSync()) { | 354 if (!d.existsSync()) { |
309 d.createSync(); | 355 d.createSync(); |
310 } | 356 } |
311 } | 357 } |
312 } | 358 } |
313 | 359 |
314 void runPixelLayoutTests() { | 360 void runPixelLayoutTests() { |
315 init(); | 361 init(); |
316 runPixelLayoutTest(0); | 362 runPixelLayoutTest(0); |
317 } | 363 } |
318 | 364 |
319 void runTextLayoutTests() { | 365 void runTextLayoutTests() { |
320 init(); | 366 init(); |
321 runTextLayoutTest(0); | 367 runTextLayoutTest(0); |
322 } | 368 } |
OLD | NEW |