| OLD | NEW |
| 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2013, 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 /** | 5 /** |
| 6 * A configuration for running layoutr tests with testrunner. | 6 * A configuration for running layout tests with testrunner. |
| 7 * This configuration is similar to the interactive_html_config | 7 * This configuration is similar to the interactive_html_config |
| 8 * as it runs each test in its own IFrame. However, where the former | 8 * as it runs each test in its own IFrame. However, where the former |
| 9 * recreated the IFrame for each test, here the IFrames are preserved. | 9 * recreated the IFrame for each test, here the IFrames are preserved. |
| 10 * Furthermore we post a message on completion. | 10 * Furthermore we post a message on completion. |
| 11 */ | 11 */ |
| 12 library html_layout_config; | 12 library html_layout_config; |
| 13 | 13 |
| 14 import 'dart:async'; |
| 14 import 'dart:html'; | 15 import 'dart:html'; |
| 15 import 'dart:math'; | 16 import 'dart:math'; |
| 16 import 'unittest.dart'; | 17 import 'unittest.dart'; |
| 17 | 18 |
| 18 /** The messages exchanged between parent and child. */ | 19 /** The messages exchanged between parent and child. */ |
| 19 // TODO(gram) At some point postMessage was supposed to support | 20 // TODO(gram) At some point postMessage was supposed to support |
| 20 // sending arrays and maps. When it does we can get rid of the encoding/ | 21 // sending arrays and maps. When it does we can get rid of the encoding/ |
| 21 // decoding of messages as string. | 22 // decoding of messages as string. |
| 22 class _Message { | 23 class _Message { |
| 23 static final START = 'start'; | 24 static final START = 'start'; |
| (...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 57 } | 58 } |
| 58 | 59 |
| 59 /** | 60 /** |
| 60 * The child configuration that is used to run individual tests in | 61 * The child configuration that is used to run individual tests in |
| 61 * an IFrame and post the results back to the parent. In principle | 62 * an IFrame and post the results back to the parent. In principle |
| 62 * this can run more than one test in the IFrame but currently only | 63 * this can run more than one test in the IFrame but currently only |
| 63 * one is used. | 64 * one is used. |
| 64 */ | 65 */ |
| 65 class ChildHtmlConfiguration extends Configuration { | 66 class ChildHtmlConfiguration extends Configuration { |
| 66 get name => 'ChildHtmlConfiguration'; | 67 get name => 'ChildHtmlConfiguration'; |
| 67 // TODO(rnystrom): Get rid of this if we get canonical closures for methods. | 68 |
| 68 EventListener _onErrorClosure; | 69 StreamSubscription _errorSubscription; |
| 69 | 70 |
| 70 /** The window to which results must be posted. */ | 71 /** The window to which results must be posted. */ |
| 71 Window parentWindow; | 72 Window parentWindow; |
| 72 | 73 |
| 73 /** The time at which tests start. */ | 74 /** The time at which tests start. */ |
| 74 Map<int,DateTime> _testStarts; | 75 Map<int,DateTime> _testStarts; |
| 75 | 76 |
| 76 ChildHtmlConfiguration() : | 77 ChildHtmlConfiguration() : |
| 77 _testStarts = new Map<int,DateTime>(); | 78 _testStarts = new Map<int,DateTime>(); |
| 78 | 79 |
| 79 /** Don't start running tests automatically. */ | 80 /** Don't start running tests automatically. */ |
| 80 get autoStart => false; | 81 get autoStart => false; |
| 81 | 82 |
| 82 void onInit() { | 83 void onInit() { |
| 83 _onErrorClosure = | |
| 84 (e) => handleExternalError(e, '(DOM callback has errors)'); | |
| 85 | |
| 86 /** | 84 /** |
| 87 * The parent posts a 'start' message to kick things off, | 85 * The parent posts a 'start' message to kick things off, |
| 88 * which is handled by this handler. It saves the parent | 86 * which is handled by this handler. It saves the parent |
| 89 * window, gets the test ID from the query parameter in the | 87 * window, gets the test ID from the query parameter in the |
| 90 * IFrame URL, sets that as a solo test and starts test execution. | 88 * IFrame URL, sets that as a solo test and starts test execution. |
| 91 */ | 89 */ |
| 92 window.on.message.add((MessageEvent e) { | 90 window.onMessage.listen((MessageEvent e) { |
| 93 var m = new _Message.fromString(e.data); | 91 var m = new _Message.fromString(e.data); |
| 94 if (m.messageType == _Message.START) { | 92 if (m.messageType == _Message.START) { |
| 95 parentWindow = e.source; | 93 parentWindow = e.source; |
| 96 String search = window.location.search; | 94 String search = window.location.search; |
| 97 int pos = search.indexOf('t='); | 95 int pos = search.indexOf('t='); |
| 98 String ids = search.substring(pos+2); | 96 String ids = search.substring(pos+2); |
| 99 int id = int.parse(ids); | 97 int id = int.parse(ids); |
| 100 setSoloTest(id); | 98 setSoloTest(id); |
| 101 runTests(); | 99 runTests(); |
| 102 } | 100 } |
| 103 }); | 101 }); |
| 104 } | 102 } |
| 105 | 103 |
| 106 void onStart() { | 104 void onStart() { |
| 107 // Listen for uncaught errors. | 105 _errorSubscription = window.onError.listen((e) { |
| 108 window.on.error.add(_onErrorClosure); | 106 handleExternalError(e, '(DOM callback has errors)'); |
| 107 }); |
| 109 } | 108 } |
| 110 | 109 |
| 111 /** Record the start time of the test. */ | 110 /** Record the start time of the test. */ |
| 112 void onTestStart(TestCase testCase) { | 111 void onTestStart(TestCase testCase) { |
| 113 super.onTestStart(testCase); | 112 super.onTestStart(testCase); |
| 114 _testStarts[testCase.id]= new DateTime.now(); | 113 _testStarts[testCase.id]= new DateTime.now(); |
| 115 } | 114 } |
| 116 | 115 |
| 117 /** | 116 /** |
| 118 * Tests can call [log] for diagnostic output. These log | 117 * Tests can call [log] for diagnostic output. These log |
| (...skipping 27 matching lines...) Expand all Loading... |
| 146 } | 145 } |
| 147 parentWindow.postMessage( | 146 parentWindow.postMessage( |
| 148 _Message.text(testCase.result, elapsed, testCase.message), '*'); | 147 _Message.text(testCase.result, elapsed, testCase.message), '*'); |
| 149 } | 148 } |
| 150 | 149 |
| 151 void onSummary(int passed, int failed, int errors, List<TestCase> results, | 150 void onSummary(int passed, int failed, int errors, List<TestCase> results, |
| 152 String uncaughtError) { | 151 String uncaughtError) { |
| 153 } | 152 } |
| 154 | 153 |
| 155 void onDone(bool success) { | 154 void onDone(bool success) { |
| 156 window.on.error.remove(_onErrorClosure); | 155 assert(_errorSubscription != null); |
| 156 _errorSubscription.cancel(); |
| 157 _errorSubscription = null; |
| 157 } | 158 } |
| 158 } | 159 } |
| 159 | 160 |
| 160 /** | 161 /** |
| 161 * The parent configuration runs in the top-level window; it wraps the tests | 162 * The parent configuration runs in the top-level window; it wraps the tests |
| 162 * in new functions that create child IFrames and run the real tests. | 163 * in new functions that create child IFrames and run the real tests. |
| 163 */ | 164 */ |
| 164 class ParentHtmlConfiguration extends Configuration { | 165 class ParentHtmlConfiguration extends Configuration { |
| 165 get autoStart => false; | 166 get autoStart => false; |
| 166 get name => 'ParentHtmlConfiguration'; | 167 get name => 'ParentHtmlConfiguration'; |
| 167 Map<int,DateTime> _testStarts; | 168 Map<int,DateTime> _testStarts; |
| 168 // TODO(rnystrom): Get rid of this if we get canonical closures for methods. | |
| 169 EventListener _onErrorClosure; | |
| 170 | 169 |
| 171 /** The stack that was posted back from the child, if any. */ | 170 /** The stack that was posted back from the child, if any. */ |
| 172 String _stack; | 171 String _stack; |
| 173 | 172 |
| 174 int _testTime; | 173 int _testTime; |
| 175 /** | 174 /** |
| 176 * Whether or not we have already wrapped the TestCase test functions | 175 * Whether or not we have already wrapped the TestCase test functions |
| 177 * in new closures that instead create an IFrame and get it to run the | 176 * in new closures that instead create an IFrame and get it to run the |
| 178 * test. | 177 * test. |
| 179 */ | 178 */ |
| 180 bool _doneWrap = false; | 179 bool _doneWrap = false; |
| 181 | 180 |
| 182 /** | 181 StreamSubscription _messageSubscription, _errorSubscription; |
| 183 * We use this to make a single closure from _handleMessage so we | |
| 184 * can remove the handler later. | |
| 185 */ | |
| 186 Function _messageHandler; | |
| 187 | 182 |
| 188 ParentHtmlConfiguration() : | 183 ParentHtmlConfiguration() : |
| 189 _testStarts = new Map<int,DateTime>(); | 184 _testStarts = new Map<int,DateTime>(); |
| 190 | 185 |
| 191 // We need to block until the test is done, so we make a | 186 // We need to block until the test is done, so we make a |
| 192 // dummy async callback that we will use to flag completion. | 187 // dummy async callback that we will use to flag completion. |
| 193 Function completeTest = null; | 188 Function completeTest = null; |
| 194 | 189 |
| 195 wrapTest(TestCase testCase) { | 190 wrapTest(TestCase testCase) { |
| 196 String baseUrl = window.location.toString(); | 191 String baseUrl = window.location.toString(); |
| 197 String url = '${baseUrl}?t=${testCase.id}'; | 192 String url = '${baseUrl}?t=${testCase.id}'; |
| 198 return () { | 193 return () { |
| 199 // Add the child IFrame. | 194 // Add the child IFrame. |
| 200 Element childDiv = document.query('#child'); | 195 Element childDiv = document.query('#child'); |
| 201 var label = new Element.html( | 196 var label = new Element.html( |
| 202 "<pre id='result${testCase.id}'>${testCase.description}</pre>"); | 197 "<pre id='result${testCase.id}'>${testCase.description}</pre>"); |
| 203 IFrameElement child = new Element.html(""" | 198 IFrameElement child = new Element.html(""" |
| 204 <iframe id='childFrame${testCase.id}' src='$url'> | 199 <iframe id='childFrame${testCase.id}' src='$url'> |
| 205 </iframe>"""); | 200 </iframe>"""); |
| 206 childDiv.nodes.add(label); | 201 childDiv.nodes.add(label); |
| 207 childDiv.nodes.add(child); | 202 childDiv.nodes.add(child); |
| 208 completeTest = expectAsync0((){ }); | 203 completeTest = expectAsync0((){ }); |
| 209 // Kick off the test when the IFrame is loaded. | 204 // Kick off the test when the IFrame is loaded. |
| 210 child.on.load.add((e) { | 205 child.onLoad.listen((e) { |
| 211 child.contentWindow.postMessage(_Message.text(_Message.START), '*'); | 206 child.contentWindow.postMessage(_Message.text(_Message.START), '*'); |
| 212 }); | 207 }); |
| 213 }; | 208 }; |
| 214 } | 209 } |
| 215 | 210 |
| 216 void _handleMessage(MessageEvent e) { | 211 void _handleMessage(MessageEvent e) { |
| 217 // Get the result, do any logging, then do a pass/fail. | 212 // Get the result, do any logging, then do a pass/fail. |
| 218 var msg = new _Message.fromString(e.data); | 213 var msg = new _Message.fromString(e.data); |
| 219 if (msg.messageType == _Message.LOG) { | 214 if (msg.messageType == _Message.LOG) { |
| 220 logMessage(e.data); | 215 logMessage(e.data); |
| 221 } else if (msg.messageType == _Message.STACK) { | 216 } else if (msg.messageType == _Message.STACK) { |
| 222 _stack = msg.body; | 217 _stack = msg.body; |
| 223 } else { | 218 } else { |
| 224 _testTime = msg.elapsed; | 219 _testTime = msg.elapsed; |
| 225 if (msg.messageType == _Message.PASS) { | 220 if (msg.messageType == _Message.PASS) { |
| 226 currentTestCase.pass(); | 221 currentTestCase.pass(); |
| 227 } else if (msg.messageType == _Message.FAIL) { | 222 } else if (msg.messageType == _Message.FAIL) { |
| 228 currentTestCase.fail(msg.body, _stack); | 223 currentTestCase.fail(msg.body, _stack); |
| 229 } else if (msg.messageType == _Message.ERROR) { | 224 } else if (msg.messageType == _Message.ERROR) { |
| 230 currentTestCase.error(msg.body, _stack); | 225 currentTestCase.error(msg.body, _stack); |
| 231 } | 226 } |
| 232 completeTest(); | 227 completeTest(); |
| 233 } | 228 } |
| 234 } | 229 } |
| 235 | 230 |
| 236 void onInit() { | 231 void onInit() { |
| 237 _messageHandler = _handleMessage; // We need to make just one closure. | |
| 238 _onErrorClosure = | |
| 239 (e) => handleExternalError(e, '(DOM callback has errors)'); | |
| 240 } | 232 } |
| 241 | 233 |
| 242 void onStart() { | 234 void onStart() { |
| 243 // Listen for uncaught errors. | 235 // Listen for uncaught errors. |
| 244 window.on.error.add(_onErrorClosure); | 236 assert(_errorSubscription == null); |
| 237 _errorSubscription = window.onError.listen((e) { |
| 238 handleExternalError(e, '(DOM callback has errors)'); |
| 239 }); |
| 240 |
| 245 if (!_doneWrap) { | 241 if (!_doneWrap) { |
| 246 _doneWrap = true; | 242 _doneWrap = true; |
| 247 for (int i = 0; i < testCases.length; i++) { | 243 for (int i = 0; i < testCases.length; i++) { |
| 248 testCases[i].test = wrapTest(testCases[i]); | 244 testCases[i].test = wrapTest(testCases[i]); |
| 249 testCases[i].setUp = null; | 245 testCases[i].setUp = null; |
| 250 testCases[i].tearDown = null; | 246 testCases[i].tearDown = null; |
| 251 } | 247 } |
| 252 } | 248 } |
| 253 window.on.message.add(_messageHandler); | 249 |
| 250 _messageSubscription = window.onMessage.listen(_handleMessage); |
| 254 } | 251 } |
| 255 | 252 |
| 256 void onTestStart(TestCase testCase) { | 253 void onTestStart(TestCase testCase) { |
| 257 var id = testCase.id; | 254 var id = testCase.id; |
| 258 _testStarts[testCase.id]= new DateTime.now(); | 255 _testStarts[testCase.id]= new DateTime.now(); |
| 259 super.onTestStart(testCase); | 256 super.onTestStart(testCase); |
| 260 _stack = null; | 257 _stack = null; |
| 261 } | 258 } |
| 262 | 259 |
| 263 // Actually test logging is handled by the child, then posted | 260 // Actually test logging is handled by the child, then posted |
| (...skipping 10 matching lines...) Expand all Loading... |
| 274 super.onTestResult(testCase); | 271 super.onTestResult(testCase); |
| 275 document.query('#result${testCase.id}').text = | 272 document.query('#result${testCase.id}').text = |
| 276 '${testCase.result}:${testCase.runningTime.inMilliseconds}:' | 273 '${testCase.result}:${testCase.runningTime.inMilliseconds}:' |
| 277 '${testCase.description}//${testCase.message}'; | 274 '${testCase.description}//${testCase.message}'; |
| 278 } | 275 } |
| 279 | 276 |
| 280 void onSummary(int passed, int failed, int errors, List<TestCase> results, | 277 void onSummary(int passed, int failed, int errors, List<TestCase> results, |
| 281 String uncaughtError) { | 278 String uncaughtError) { |
| 282 } | 279 } |
| 283 void onDone(bool success) { | 280 void onDone(bool success) { |
| 284 window.on.message.remove(_messageHandler); | 281 _messageSubscription.cancel(); |
| 285 window.on.error.remove(_onErrorClosure); | 282 _messageSubscription = null; |
| 283 |
| 284 _errorSubscription.cancel(); |
| 285 _errorSubscription = null; |
| 286 |
| 286 window.postMessage('done', '*'); // Unblock DRT | 287 window.postMessage('done', '*'); // Unblock DRT |
| 287 } | 288 } |
| 288 } | 289 } |
| 289 | 290 |
| 290 /** | 291 /** |
| 291 * Add the divs to the DOM if they are not present. | 292 * Add the divs to the DOM if they are not present. |
| 292 */ | 293 */ |
| 293 void _prepareDom() { | 294 void _prepareDom() { |
| 294 if (document.query('#otherlogs') == null) { | 295 if (document.query('#otherlogs') == null) { |
| 295 document.body.nodes.add(new Element.html( | 296 document.body.nodes.add(new Element.html( |
| (...skipping 11 matching lines...) Expand all Loading... |
| 307 void useHtmlLayoutConfiguration() { | 308 void useHtmlLayoutConfiguration() { |
| 308 if (config != null) return; | 309 if (config != null) return; |
| 309 if (window.location.search == '') { // This is the parent. | 310 if (window.location.search == '') { // This is the parent. |
| 310 _prepareDom(); | 311 _prepareDom(); |
| 311 configure(new ParentHtmlConfiguration()); | 312 configure(new ParentHtmlConfiguration()); |
| 312 } else { | 313 } else { |
| 313 configure(new ChildHtmlConfiguration()); | 314 configure(new ChildHtmlConfiguration()); |
| 314 } | 315 } |
| 315 } | 316 } |
| 316 | 317 |
| OLD | NEW |