OLD | NEW |
| (Empty) |
1 library dromaeo_test; | |
2 | |
3 import 'dart:html'; | |
4 import 'dart:async'; | |
5 import "dart:convert"; | |
6 import 'dart:math' as Math; | |
7 import 'Suites.dart'; | |
8 | |
9 main() { | |
10 new Dromaeo().run(); | |
11 } | |
12 | |
13 class SuiteController { | |
14 final SuiteDescription _suiteDescription; | |
15 final IFrameElement _suiteIframe; | |
16 | |
17 DivElement _element; | |
18 double _meanProduct; | |
19 int _nTests; | |
20 | |
21 SuiteController(this._suiteDescription, this._suiteIframe) | |
22 : _meanProduct = 1.0, | |
23 _nTests = 0 { | |
24 _make(); | |
25 _init(); | |
26 } | |
27 | |
28 start() { | |
29 _suiteIframe.contentWindow.postMessage('start', '*'); | |
30 } | |
31 | |
32 update(String testName, num mean, num error, double percent) { | |
33 _meanProduct *= mean; | |
34 _nTests++; | |
35 | |
36 final meanAsString = mean.toStringAsFixed(2); | |
37 final errorAsString = error.toStringAsFixed(2); | |
38 final Element progressDisplay = _element.nextNode.nextNode; | |
39 progressDisplay.innerHtml = | |
40 '${progressDisplay.innerHtml}<li><b>${testName}:</b>' | |
41 '${meanAsString}<small> runs/s ±${errorAsString}%<small></li>'; | |
42 _updateTestPos(percent); | |
43 } | |
44 | |
45 _make() { | |
46 _element = _createDiv('test'); | |
47 // TODO(antonm): add an onclick functionality. | |
48 _updateTestPos(); | |
49 } | |
50 | |
51 _updateTestPos([double percent = 1.0]) { | |
52 String suiteName = _suiteDescription.name; | |
53 final done = percent >= 100.0; | |
54 String info = ''; | |
55 if (done) { | |
56 final parent = _element.parent; | |
57 parent.attributes['class'] = '${parent.attributes["class"]} done'; | |
58 final mean = Math.pow(_meanProduct, 1.0 / _nTests).toStringAsFixed(2); | |
59 info = '<span>${mean} runs/s</span>'; | |
60 } | |
61 _element.innerHtml = | |
62 '<b>${suiteName}:</b>' | |
63 '<div class="bar"><div style="width:${percent}%;">${info}</div></div>'; | |
64 } | |
65 | |
66 _init() { | |
67 final div = _createDiv('result-item'); | |
68 div.nodes.add(_element); | |
69 final description = _suiteDescription.description; | |
70 final originUrl = _suiteDescription.origin.url; | |
71 final testUrl = '${_suiteDescription.file}'; | |
72 div.innerHtml = | |
73 '${div.innerHtml}<p>${description}<br/><a href="${originUrl}">Origin</a' | |
74 '>, <a href="${testUrl}">Source</a>' | |
75 '<ol class="results"></ol>'; | |
76 // Reread the element, as the previous wrapper get disconnected thanks | |
77 // to .innerHtml update above. | |
78 _element = div.nodes[0]; | |
79 | |
80 document.querySelector('#main').nodes.add(div); | |
81 } | |
82 | |
83 DivElement _createDiv(String clazz) { | |
84 final div = new DivElement(); | |
85 div.attributes['class'] = clazz; | |
86 return div; | |
87 } | |
88 } | |
89 | |
90 class Dromaeo { | |
91 final List<SuiteController> _suiteControllers; | |
92 Function _handler; | |
93 | |
94 Dromaeo() | |
95 : _suiteControllers = new List<SuiteController>() | |
96 { | |
97 _handler = _createHandler(); | |
98 window.onMessage.listen( | |
99 (MessageEvent event) { | |
100 try { | |
101 final response = JSON.decode(event.data); | |
102 _handler = _handler(response['command'], response['data']); | |
103 } catch (e, stacktrace) { | |
104 if (!(e is FormatException && | |
105 (event.data.toString().startsWith('unittest') || | |
106 event.data.toString().startsWith('dart')))) { | |
107 // Hack because unittest also uses post messages to communicate. | |
108 // So the fact that the event.data is not proper json is not | |
109 // always an error. | |
110 print('Exception: ${e}: ${stacktrace}'); | |
111 print(event.data); | |
112 } | |
113 } | |
114 }); | |
115 } | |
116 | |
117 run() { | |
118 // TODO(vsm): Initial page should not run. For now, run all | |
119 // tests by default. | |
120 var tags = window.location.search; | |
121 if (tags.length > 1) { | |
122 tags = tags.substring(1); | |
123 } else if (window.navigator.userAgent.contains('(Dart)')) { | |
124 // TODO(vsm): Update when we change Dart VM detection. | |
125 tags = 'js|dart&html'; | |
126 } else { | |
127 tags = 'js|dart2js&html'; | |
128 } | |
129 | |
130 // TODO(antonm): create Re-run tests href. | |
131 final Element suiteNameElement = _byId('overview').nodes[0]; | |
132 final category = Suites.getCategory(tags); | |
133 if (category != null) { | |
134 suiteNameElement.innerHtml = category; | |
135 } | |
136 _css(_byId('tests'), 'display', 'none'); | |
137 for (SuiteDescription suite in Suites.getSuites(tags)) { | |
138 final iframe = new IFrameElement(); | |
139 _css(iframe, 'height', '1px'); | |
140 _css(iframe, 'width', '1px'); | |
141 iframe.src = '${suite.file}'; | |
142 document.body.nodes.add(iframe); | |
143 | |
144 _suiteControllers.add(new SuiteController(suite, iframe)); | |
145 } | |
146 } | |
147 | |
148 static const double _SECS_PER_TEST = 5.0; | |
149 | |
150 Function _createHandler() { | |
151 int suitesLoaded = 0; | |
152 int totalTests = 0; | |
153 int currentSuite; | |
154 double totalTimeSecs, estimatedTimeSecs; | |
155 | |
156 // TODO(jat): Remove void type below. Bug 5269037. | |
157 void _updateTime() { | |
158 final mins = (estimatedTimeSecs / 60).floor(); | |
159 final secs = (estimatedTimeSecs - mins * 60).round(); | |
160 final secsAsString = '${(secs < 10 ? "0" : "")}$secs'; | |
161 _byId('left').innerHtml = '${mins}:${secsAsString}'; | |
162 | |
163 final elapsed = totalTimeSecs - estimatedTimeSecs; | |
164 final percent = (100 * elapsed / totalTimeSecs).toStringAsFixed(2); | |
165 _css(_byId('timebar'), 'width', '${percent}%'); | |
166 } | |
167 | |
168 Function loading, running, done; | |
169 | |
170 loading = (String command, var data) { | |
171 assert(command == 'inited'); | |
172 suitesLoaded++; | |
173 totalTests += data['nTests']; | |
174 if (suitesLoaded == _suitesTotal) { | |
175 totalTimeSecs = estimatedTimeSecs = _SECS_PER_TEST * totalTests; | |
176 _updateTime(); | |
177 currentSuite = 0; | |
178 _suiteControllers[currentSuite].start(); | |
179 return running; | |
180 } | |
181 | |
182 return loading; | |
183 }; | |
184 | |
185 running = (String command, var data) { | |
186 switch (command) { | |
187 case 'result': | |
188 final testName = data['testName']; | |
189 final mean = data['mean']; | |
190 final error = data['error']; | |
191 final percent = data['percent']; | |
192 _suiteControllers[currentSuite].update(testName, mean, error, percent)
; | |
193 estimatedTimeSecs -= _SECS_PER_TEST; | |
194 _updateTime(); | |
195 return running; | |
196 | |
197 case 'over': | |
198 currentSuite++; | |
199 if (currentSuite < _suitesTotal) { | |
200 _suiteControllers[currentSuite].start(); | |
201 return running; | |
202 } | |
203 document.body.attributes['class'] = 'alldone'; | |
204 return done; | |
205 | |
206 default: | |
207 throw 'Unknown command ${command} [${data}]'; | |
208 } | |
209 }; | |
210 | |
211 done = (String command, var data) { | |
212 }; | |
213 | |
214 return loading; | |
215 } | |
216 | |
217 _css(Element element, String property, String value) { | |
218 // TODO(antonm): remove the last argument when CallWithDefaultValue | |
219 // is implemented. | |
220 element.style.setProperty(property, value, ''); | |
221 } | |
222 | |
223 Element _byId(String id) { | |
224 return document.querySelector('#$id'); | |
225 } | |
226 | |
227 int get _suitesTotal { | |
228 return _suiteControllers.length; | |
229 } | |
230 } | |
OLD | NEW |