OLD | NEW |
---|---|
(Empty) | |
1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file | |
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. | |
4 | |
5 library protoc.benchmark.html_view; | |
Søren Gjesse
2015/09/08 06:43:54
Might have been easier to put more of this into th
skybrian
2015/09/08 17:25:11
It's because I wanted to keep each view self-conta
| |
6 | |
7 import 'dart:async' show Stream, StreamController; | |
8 import 'dart:html'; | |
9 | |
10 import 'generated/benchmark.pb.dart' as pb; | |
11 import 'dashboard_model.dart'; | |
12 import 'report.dart' show encodeReport; | |
13 | |
14 /// A dashboard allowing the user to run a benchmark suite and compare the | |
15 /// results to any saved report. | |
16 class DashboardView { | |
17 final elt = new DivElement(); | |
18 | |
19 final _runButton = new _Button(); | |
20 final _status = new _Label(new SpanElement()..style.height = "1em"); | |
21 final _envElt = new PreElement(); | |
22 final _menu = new _Menu(); | |
23 final _responseTable = new TableElement(); | |
24 final _jsonView = new _JsonView(); | |
25 | |
26 String _renderedPlatform; | |
27 final rows = <_ResponseView>[]; | |
28 | |
29 DashboardView() { | |
30 // Fill in "template" elements that never change. | |
31 elt.children.addAll([ | |
32 new DivElement() | |
33 ..children.addAll([_runButton.elt, new Text(" "), _status.elt,]), | |
34 _envElt, | |
35 new Text("Choose baseline: "), | |
36 _menu.elt, | |
37 _responseTable | |
38 ..children.addAll([ | |
39 new TableRowElement() | |
40 ..children.addAll([ | |
41 _th("Benchmark"), | |
42 _th("Params"), | |
43 _th("1000 * int32 reads / second")..colSpan = 4 | |
44 ]), | |
45 new TableRowElement() | |
46 ..children.addAll([ | |
47 _th(""), | |
48 _th(""), | |
49 _thRight("Baseline"), | |
50 _thRight("Median"), | |
51 _thRight("Max"), | |
52 _thRight("Count") | |
53 ]) | |
54 ]), | |
55 _jsonView.elt | |
56 ]); | |
57 } | |
58 | |
59 Stream get onRunButtonClick => _runButton.onClick; | |
60 Stream<String> get onMenuChange => _menu.onChange; | |
61 | |
62 void render(DashboardModel model) { | |
63 _runButton.render("Run", model.canRun); | |
64 if (!model.latest.hasStatus() || model.latest.status == pb.Status.DONE) { | |
65 _status.render(""); | |
66 } else { | |
67 _status.render(model.latest.status.name); | |
68 } | |
69 | |
70 _renderEnv(model.latest); | |
71 _menu.render(model.savedReports.keys, model.baselineName); | |
72 _renderResponses(model.getBaselineSamples(), model.latest); | |
73 _jsonView.render(model.latest); | |
74 } | |
75 | |
76 void _renderEnv(pb.Report r) { | |
77 String newPlatform = r.env.platform.toString(); | |
78 if (newPlatform == _renderedPlatform) return; | |
79 _envElt.text = newPlatform; | |
80 _renderedPlatform = newPlatform; | |
81 } | |
82 | |
83 /// Renders a table with one row for each benchmark. | |
84 void _renderResponses(BaselineSamples baseline, pb.Report r) { | |
85 var it = r.responses.iterator; | |
86 | |
87 // Update existing rows | |
88 for (var row in rows) { | |
89 var hasNext = it.moveNext(); | |
90 assert(hasNext); // assume that the table only grows | |
91 var left = baseline.getSample(it.current.request); | |
92 row.render(left, it.current); | |
93 } | |
94 | |
95 // Add any new rows | |
96 while (it.moveNext()) { | |
97 var left = baseline.getSample(it.current.request); | |
98 var row = new _ResponseView()..render(left, it.current); | |
99 _responseTable.append(row.elt); | |
100 rows.add(row); | |
101 } | |
102 } | |
103 | |
104 static _th(String columnName) => new Element.th() | |
105 ..style.textAlign = "left" | |
106 ..text = columnName; | |
107 | |
108 static _thRight(String columnName) => new Element.th() | |
109 ..style.textAlign = "right" | |
110 ..text = columnName; | |
111 } | |
112 | |
113 /// A single row in the benchmark table. | |
114 /// | |
115 /// Displays how many samples were collected and the median and max samples. | |
116 /// Also displays a baseline sample for comparison. | |
117 class _ResponseView { | |
118 final elt = new TableRowElement(); | |
119 final _name = new _Label(new TableCellElement()); | |
120 final _params = new _Label(new TableCellElement()); | |
121 final _baseline = new _SampleView(); | |
122 final _median = new _SampleView(); | |
123 final _max = new _SampleView(); | |
124 final _count = new _Label(new TableCellElement()..style.textAlign = "right"); | |
125 | |
126 _ResponseView() { | |
127 elt.children.addAll([ | |
128 _name.elt, | |
129 _params.elt, | |
130 _baseline.elt, | |
131 _median.elt, | |
132 _max.elt, | |
133 _count.elt | |
134 ]); | |
135 } | |
136 | |
137 void render(pb.Sample baseline, pb.Response response) { | |
138 _name.render(response.request.id.name); | |
139 _params.render(response.request.params.toString()); | |
140 _baseline.render(baseline); | |
141 _median.render(medianSample(response)); | |
142 _max.render(maxSample(response)); | |
143 _count.render("${response.samples.length}"); | |
144 } | |
145 } | |
146 | |
147 /// A table cell holding one sample. | |
148 class _SampleView { | |
149 final elt = new TableCellElement()..style.textAlign = "right"; | |
150 pb.Sample _rendered; | |
151 | |
152 void render(pb.Sample s) { | |
153 if (_rendered == s) return; | |
154 elt.text = _renderInt32Reads(s); | |
155 _rendered = s; | |
156 } | |
157 | |
158 static String _renderInt32Reads(pb.Sample s) { | |
159 if (s == null) return "*"; | |
160 double kIntsPerSecond = s.counts.int32Reads * 1000 / s.duration; | |
161 return kIntsPerSecond.toStringAsFixed(1); | |
162 } | |
163 } | |
164 | |
165 /// Renders the benchmark report as JSON so it can be copied to a file. | |
166 class _JsonView { | |
167 final DivElement elt = new DivElement(); | |
168 String _rendered; | |
169 | |
170 void render(pb.Report r) { | |
171 // Don't show JSON while benchmarks are in progress. | |
172 String json = ""; | |
173 if (r.status == pb.Status.DONE) { | |
174 json = encodeReport(r); | |
175 } | |
176 | |
177 if (json == _rendered) return; | |
178 | |
179 elt.children.clear(); | |
180 if (json == "") return; | |
181 elt.children.addAll([ | |
182 new HeadingElement.h2()..text = "Report data as JSON:", | |
183 new PreElement()..text = json | |
184 ]); | |
185 _rendered = json; | |
186 } | |
187 } | |
188 | |
189 /// A menu of selectable text items. | |
190 class _Menu { | |
191 final elt = new SelectElement(); | |
192 final _changes = new StreamController.broadcast(); | |
193 final _options = new List<_MenuOption>(); | |
194 | |
195 _Menu() { | |
196 elt.onChange.listen((e) => _changes.add(elt.value)); | |
197 } | |
198 | |
199 Stream<String> get onChange => _changes.stream; | |
200 | |
201 void render(List<String> items, String selected) { | |
202 var it = items.iterator; | |
203 | |
204 // Update existing items | |
205 for (var opt in _options) { | |
206 var hasNext = it.moveNext(); | |
207 assert(hasNext); // assume menu never shrinks | |
208 opt.render(it.current, it.current == selected); | |
209 } | |
210 | |
211 // Add any new items | |
212 while (it.moveNext()) { | |
213 var opt = new _MenuOption(); | |
214 opt.render(it.current, it.current == selected); | |
215 elt.append(opt.elt); | |
216 _options.add(opt); | |
217 } | |
218 } | |
219 } | |
220 | |
221 class _MenuOption { | |
222 final elt = new OptionElement(); | |
223 String _renderedItem; | |
224 bool _renderedSelected; | |
225 | |
226 void render(String item, selected) { | |
227 if (_renderedItem != item) { | |
228 elt.text = item; | |
229 elt.value = item; | |
230 _renderedItem = item; | |
231 } | |
232 if (_renderedSelected != selected) { | |
233 elt.selected = selected; | |
234 _renderedSelected = selected; | |
235 } | |
236 } | |
237 } | |
238 | |
239 class _Label { | |
240 final HtmlElement elt; | |
241 String _rendered; | |
242 _Label(this.elt); | |
243 | |
244 void render(String text) { | |
245 if (_rendered == text) return; | |
246 elt.text = text; | |
247 _rendered = text; | |
248 } | |
249 } | |
250 | |
251 class _Button { | |
252 final elt = new ButtonElement(); | |
253 final _clicks = new StreamController.broadcast(); | |
254 String _renderedLabel; | |
255 bool _renderedEnabled; | |
256 _Button() { | |
257 elt.onClick.listen((e) => _clicks.add(true)); | |
258 } | |
259 | |
260 Stream get onClick => _clicks.stream; | |
261 | |
262 void render(String label, bool enabled) { | |
263 if (label != _renderedLabel) { | |
264 elt.text = label; | |
265 _renderedLabel = label; | |
266 } | |
267 if (_renderedEnabled != enabled) { | |
268 elt.disabled = !enabled; | |
269 _renderedEnabled = enabled; | |
270 } | |
271 } | |
272 } | |
OLD | NEW |