REGRESSION(r212853): Comparisons to baseline no longer shows up
[WebKit-https.git] / Websites / perf.webkit.org / unit-tests / time-series-tests.js
1 'use strict';
2
3 const assert = require('assert');
4 if (!assert.almostEqual)
5     assert.almostEqual = require('./resources/almost-equal.js');
6
7 const MockRemoteAPI = require('./resources/mock-remote-api.js').MockRemoteAPI;
8 require('../tools/js/v3-models.js');
9
10 let threePoints;
11 let fivePoints;
12 beforeEach(() => {
13     threePoints = [
14         {id: 910, time: 101, value: 110},
15         {id: 902, time: 220, value: 102},
16         {id: 930, time: 303, value: 130},
17     ];
18     fivePoints = [...threePoints,
19         {id: 904, time: 400, value: 114},
20         {id: 950, time: 505, value: 105},
21         {id: 960, time: 600, value: 116},
22     ];
23 });
24
25 function addPointsToSeries(timeSeries, list = threePoints)
26 {
27     for (let point of list)
28         timeSeries.append(point);
29 }
30
31 describe('TimeSeries', () => {
32
33     describe('length', () => {
34         it('should return the length', () => {
35             const timeSeries = new TimeSeries();
36             addPointsToSeries(timeSeries);
37             assert.equal(timeSeries.length(), 3);
38         });
39
40         it('should return 0 when there are no points', () => {
41             const timeSeries = new TimeSeries();
42             assert.equal(timeSeries.length(), 0);
43         });
44     });
45
46     describe('firstPoint', () => {
47         it('should return the first point', () => {
48             const timeSeries = new TimeSeries();
49             addPointsToSeries(timeSeries);
50             assert.equal(timeSeries.firstPoint(), threePoints[0]);
51         });
52
53         it('should return null when there are no points', () => {
54             const timeSeries = new TimeSeries();
55             assert.equal(timeSeries.firstPoint(), null);
56         });
57     });
58
59     describe('lastPoint', () => {
60         it('should return the first point', () => {
61             const timeSeries = new TimeSeries();
62             addPointsToSeries(timeSeries);
63             assert.equal(timeSeries.lastPoint(), threePoints[2]);
64         });
65
66         it('should return null when there are no points', () => {
67             const timeSeries = new TimeSeries();
68             assert.equal(timeSeries.lastPoint(), null);
69         });
70     });
71
72     describe('nextPoint', () => {
73         it('should return the next point when called on the first point', () => {
74             const timeSeries = new TimeSeries();
75             addPointsToSeries(timeSeries);
76             assert.equal(timeSeries.nextPoint(threePoints[0]), threePoints[1]);
77         });
78
79         it('should return the next point when called on a mid-point', () => {
80             const timeSeries = new TimeSeries();
81             addPointsToSeries(timeSeries);
82             assert.equal(timeSeries.nextPoint(threePoints[1]), threePoints[2]);
83         });
84
85         it('should return null when called on the last point', () => {
86             const timeSeries = new TimeSeries();
87             addPointsToSeries(timeSeries);
88             assert.equal(timeSeries.nextPoint(threePoints[2]), null);
89         });
90     });
91
92     describe('previousPoint', () => {
93         it('should return null when called on the first point', () => {
94             const timeSeries = new TimeSeries();
95             addPointsToSeries(timeSeries);
96             assert.equal(timeSeries.previousPoint(threePoints[0]), null);
97         });
98
99         it('should return the previous point when called on a mid-point', () => {
100             const timeSeries = new TimeSeries();
101             addPointsToSeries(timeSeries);
102             assert.equal(timeSeries.previousPoint(threePoints[1]), threePoints[0]);
103         });
104
105         it('should return the previous point when called on the last point', () => {
106             const timeSeries = new TimeSeries();
107             addPointsToSeries(timeSeries);
108             assert.equal(timeSeries.previousPoint(threePoints[2]), threePoints[1]);
109         });
110     });
111
112     describe('findPointByIndex', () => {
113         it('should return null the index is less than 0', () => {
114             const timeSeries = new TimeSeries();
115             addPointsToSeries(timeSeries);
116             assert.equal(timeSeries.findPointByIndex(-10), null);
117             assert.equal(timeSeries.findPointByIndex(-1), null);
118         });
119
120         it('should return null when the index is greater than or equal to the length', () => {
121             const timeSeries = new TimeSeries();
122             addPointsToSeries(timeSeries);
123             assert.equal(timeSeries.findPointByIndex(10), null);
124             assert.equal(timeSeries.findPointByIndex(3), null);
125         });
126
127         it('should return null when the index is not a number', () => {
128             const timeSeries = new TimeSeries();
129             addPointsToSeries(timeSeries);
130             assert.equal(timeSeries.findPointByIndex(undefined), null);
131             assert.equal(timeSeries.findPointByIndex(NaN), null);
132             assert.equal(timeSeries.findPointByIndex('a'), null);
133         });
134
135         it('should return the point at the specified index when it is in the valid range', () => {
136             const timeSeries = new TimeSeries();
137             addPointsToSeries(timeSeries);
138             assert.equal(timeSeries.findPointByIndex(0), threePoints[0]);
139             assert.equal(timeSeries.findPointByIndex(1), threePoints[1]);
140             assert.equal(timeSeries.findPointByIndex(2), threePoints[2]);
141         });
142     });
143
144     describe('findById', () => {
145         it('should return the point with the specified ID', () => {
146             const timeSeries = new TimeSeries();
147             addPointsToSeries(timeSeries);
148             assert.equal(timeSeries.findById(threePoints[0].id), threePoints[0]);
149             assert.equal(timeSeries.findById(threePoints[1].id), threePoints[1]);
150             assert.equal(timeSeries.findById(threePoints[2].id), threePoints[2]);
151         });
152
153         it('should return null for a non-existent ID', () => {
154             const timeSeries = new TimeSeries();
155             addPointsToSeries(timeSeries);
156             assert.equal(timeSeries.findById(null), null);
157             assert.equal(timeSeries.findById(undefined), null);
158             assert.equal(timeSeries.findById(NaN), null);
159             assert.equal(timeSeries.findById('a'), null);
160             assert.equal(timeSeries.findById(4231563246), null);
161         });
162     });
163
164     describe('findPointAfterTime', () => {
165         it('should return the point at the specified time', () => {
166             const timeSeries = new TimeSeries();
167             addPointsToSeries(timeSeries);
168             assert.equal(timeSeries.findPointAfterTime(threePoints[0].time), threePoints[0]);
169             assert.equal(timeSeries.findPointAfterTime(threePoints[1].time), threePoints[1]);
170             assert.equal(timeSeries.findPointAfterTime(threePoints[2].time), threePoints[2]);
171         });
172
173         it('should return the point after the specified time', () => {
174             const timeSeries = new TimeSeries();
175             addPointsToSeries(timeSeries);
176             assert.equal(timeSeries.findPointAfterTime(threePoints[0].time - 0.1), threePoints[0]);
177             assert.equal(timeSeries.findPointAfterTime(threePoints[1].time - 0.1), threePoints[1]);
178             assert.equal(timeSeries.findPointAfterTime(threePoints[2].time - 0.1), threePoints[2]);
179         });
180
181         it('should return null when there are no points after the specified time', () => {
182             const timeSeries = new TimeSeries();
183             addPointsToSeries(timeSeries);
184             assert.equal(timeSeries.findPointAfterTime(threePoints[2].time + 0.1), null);
185         });
186
187         it('should return the first point when there are multiple points at the specified time', () => {
188             const timeSeries = new TimeSeries();
189             const points = [
190                 {id: 909, time:  99, value: 105},
191                 {id: 910, time: 100, value: 110},
192                 {id: 902, time: 100, value: 102},
193                 {id: 930, time: 101, value: 130},
194             ];
195             addPointsToSeries(timeSeries, points);
196             assert.equal(timeSeries.findPointAfterTime(points[1].time), points[1]);
197         });
198     });
199
200     describe('viewBetweenPoints', () => {
201
202         it('should return a view between two points', () => {
203             const timeSeries = new TimeSeries();
204             addPointsToSeries(timeSeries, fivePoints);
205             const view = timeSeries.viewBetweenPoints(fivePoints[1], fivePoints[3]);
206             assert.equal(view.length(), 3);
207             assert.equal(view.firstPoint(), fivePoints[1]);
208             assert.equal(view.lastPoint(), fivePoints[3]);
209
210             assert.equal(view.nextPoint(fivePoints[1]), fivePoints[2]);
211             assert.equal(view.nextPoint(fivePoints[2]), fivePoints[3]);
212             assert.equal(view.nextPoint(fivePoints[3]), null);
213
214             assert.equal(view.previousPoint(fivePoints[1]), null);
215             assert.equal(view.previousPoint(fivePoints[2]), fivePoints[1]);
216             assert.equal(view.previousPoint(fivePoints[3]), fivePoints[2]);
217
218             assert.equal(view.findPointByIndex(0), fivePoints[1]);
219             assert.equal(view.findPointByIndex(1), fivePoints[2]);
220             assert.equal(view.findPointByIndex(2), fivePoints[3]);
221             assert.equal(view.findPointByIndex(3), null);
222
223             assert.equal(view.findById(fivePoints[0].id), null);
224             assert.equal(view.findById(fivePoints[1].id), fivePoints[1]);
225             assert.equal(view.findById(fivePoints[2].id), fivePoints[2]);
226             assert.equal(view.findById(fivePoints[3].id), fivePoints[3]);
227             assert.equal(view.findById(fivePoints[4].id), null);
228
229             assert.deepEqual(view.values(), [fivePoints[1].value, fivePoints[2].value, fivePoints[3].value]);
230
231             assert.deepEqual(view.firstPointInTimeRange(fivePoints[0].time, fivePoints[4].time), fivePoints[1]);
232             assert.deepEqual(view.firstPointInTimeRange(fivePoints[1].time, fivePoints[4].time), fivePoints[1]);
233             assert.deepEqual(view.firstPointInTimeRange(fivePoints[1].time, fivePoints[1].time), fivePoints[1]);
234             assert.deepEqual(view.firstPointInTimeRange(fivePoints[0].time, fivePoints[0].time), null);
235             assert.deepEqual(view.firstPointInTimeRange(fivePoints[1].time + 0.1, fivePoints[2].time), fivePoints[2]);
236             assert.deepEqual(view.firstPointInTimeRange(fivePoints[3].time - 0.1, fivePoints[4].time), fivePoints[3]);
237             assert.deepEqual(view.firstPointInTimeRange(fivePoints[4].time, fivePoints[4].time), null);
238
239             assert.deepEqual([...view], fivePoints.slice(1, 4));
240         });
241
242         it('should return a view with exactly one point for when the starting point is identical to the ending point', () => {
243             const timeSeries = new TimeSeries();
244             addPointsToSeries(timeSeries, fivePoints);
245             const view = timeSeries.viewBetweenPoints(fivePoints[2], fivePoints[2]);
246             assert.equal(view.length(), 1);
247             assert.equal(view.firstPoint(), fivePoints[2]);
248             assert.equal(view.lastPoint(), fivePoints[2]);
249
250             assert.equal(view.findPointByIndex(0), fivePoints[2]);
251             assert.equal(view.findPointByIndex(1), null);
252
253             assert.equal(view.findById(fivePoints[0].id), null);
254             assert.equal(view.findById(fivePoints[1].id), null);
255             assert.equal(view.findById(fivePoints[2].id), fivePoints[2]);
256             assert.equal(view.findById(fivePoints[3].id), null);
257             assert.equal(view.findById(fivePoints[4].id), null);
258
259             assert.deepEqual(view.values(), [fivePoints[2].value]);
260
261             assert.deepEqual(view.firstPointInTimeRange(fivePoints[0].time, fivePoints[4].time), fivePoints[2]);
262             assert.deepEqual(view.firstPointInTimeRange(fivePoints[1].time, fivePoints[4].time), fivePoints[2]);
263             assert.deepEqual(view.firstPointInTimeRange(fivePoints[1].time, fivePoints[1].time), null);
264             assert.deepEqual(view.firstPointInTimeRange(fivePoints[0].time, fivePoints[0].time), null);
265             assert.deepEqual(view.firstPointInTimeRange(fivePoints[1].time + 0.1, fivePoints[2].time), fivePoints[2]);
266             assert.deepEqual(view.firstPointInTimeRange(fivePoints[3].time - 0.1, fivePoints[4].time), null);
267             assert.deepEqual(view.firstPointInTimeRange(fivePoints[4].time, fivePoints[4].time), null);
268
269             assert.deepEqual([...view], [fivePoints[2]]);
270         });
271
272     });
273
274 });
275
276 describe('TimeSeriesView', () => {
277
278     describe('filter', () => {
279         it('should call callback with an element in the view and its index', () => {
280             const timeSeries = new TimeSeries();
281             addPointsToSeries(timeSeries, fivePoints);
282             const originalView = timeSeries.viewBetweenPoints(fivePoints[1], fivePoints[3]);
283             const points = [];
284             const indices = [];
285             const view = originalView.filter((point, index) => {
286                 points.push(point);
287                 indices.push(index);
288             });
289             assert.deepEqual(points, [fivePoints[1], fivePoints[2], fivePoints[3]]);
290             assert.deepEqual(indices, [0, 1, 2]);
291         });
292
293         it('should create a filtered view', () => {
294             const timeSeries = new TimeSeries();
295             addPointsToSeries(timeSeries, fivePoints);
296             const originalView = timeSeries.viewBetweenPoints(fivePoints[0], fivePoints[4]);
297             const view = originalView.filter((point) => { return point == fivePoints[1] || point == fivePoints[3]; });
298
299             assert.equal(view.length(), 2);
300             assert.equal(view.firstPoint(), fivePoints[1]);
301             assert.equal(view.lastPoint(), fivePoints[3]);
302
303             assert.equal(view.nextPoint(fivePoints[1]), fivePoints[3]);
304             assert.equal(view.nextPoint(fivePoints[3]), null);
305
306             assert.equal(view.previousPoint(fivePoints[1]), null);
307             assert.equal(view.previousPoint(fivePoints[3]), fivePoints[1]);
308
309             assert.equal(view.findPointByIndex(0), fivePoints[1]);
310             assert.equal(view.findPointByIndex(1), fivePoints[3]);
311             assert.equal(view.findPointByIndex(2), null);
312
313             assert.equal(view.findById(fivePoints[0].id), null);
314             assert.equal(view.findById(fivePoints[1].id), fivePoints[1]);
315             assert.equal(view.findById(fivePoints[2].id), null);
316             assert.equal(view.findById(fivePoints[3].id), fivePoints[3]);
317             assert.equal(view.findById(fivePoints[4].id), null);
318
319             assert.deepEqual(view.values(), [fivePoints[1].value, fivePoints[3].value]);
320
321             assert.deepEqual(view.firstPointInTimeRange(fivePoints[0].time, fivePoints[4].time), fivePoints[1]);
322             assert.deepEqual(view.firstPointInTimeRange(fivePoints[1].time, fivePoints[4].time), fivePoints[1]);
323             assert.deepEqual(view.firstPointInTimeRange(fivePoints[1].time, fivePoints[1].time), fivePoints[1]);
324             assert.deepEqual(view.firstPointInTimeRange(fivePoints[0].time, fivePoints[0].time), null);
325             assert.deepEqual(view.firstPointInTimeRange(fivePoints[1].time + 0.1, fivePoints[2].time), null);
326             assert.deepEqual(view.firstPointInTimeRange(fivePoints[3].time - 0.1, fivePoints[4].time), fivePoints[3]);
327             assert.deepEqual(view.firstPointInTimeRange(fivePoints[4].time, fivePoints[4].time), null);
328
329             assert.deepEqual(view.lastPointInTimeRange(fivePoints[0].time, fivePoints[4].time), fivePoints[3]);
330             assert.deepEqual(view.lastPointInTimeRange(fivePoints[1].time, fivePoints[4].time), fivePoints[3]);
331             assert.deepEqual(view.lastPointInTimeRange(fivePoints[1].time, fivePoints[1].time), fivePoints[1]);
332             assert.deepEqual(view.lastPointInTimeRange(fivePoints[0].time, fivePoints[0].time), null);
333             assert.deepEqual(view.lastPointInTimeRange(fivePoints[1].time + 0.1, fivePoints[2].time), null);
334             assert.deepEqual(view.lastPointInTimeRange(fivePoints[3].time - 0.1, fivePoints[4].time), fivePoints[3]);
335             assert.deepEqual(view.lastPointInTimeRange(fivePoints[4].time, fivePoints[4].time), null);
336
337             assert.deepEqual([...view], [fivePoints[1], fivePoints[3]]);
338         });
339     });
340
341     describe('viewTimeRange', () => {
342         it('should create a view filtered by the specified time range', () => {
343             const timeSeries = new TimeSeries();
344             addPointsToSeries(timeSeries, fivePoints);
345             const originalView = timeSeries.viewBetweenPoints(fivePoints[0], fivePoints[4]);
346             const view = originalView.viewTimeRange(fivePoints[1].time - 0.1, fivePoints[4].time - 0.1);
347
348             assert.equal(view.length(), 3);
349             assert.equal(view.firstPoint(), fivePoints[1]);
350             assert.equal(view.lastPoint(), fivePoints[3]);
351
352             assert.equal(view.nextPoint(fivePoints[1]), fivePoints[2]);
353             assert.equal(view.nextPoint(fivePoints[2]), fivePoints[3]);
354             assert.equal(view.nextPoint(fivePoints[3]), null);
355
356             assert.equal(view.previousPoint(fivePoints[1]), null);
357             assert.equal(view.previousPoint(fivePoints[2]), fivePoints[1]);
358             assert.equal(view.previousPoint(fivePoints[3]), fivePoints[2]);
359
360             assert.equal(view.findPointByIndex(0), fivePoints[1]);
361             assert.equal(view.findPointByIndex(1), fivePoints[2]);
362             assert.equal(view.findPointByIndex(2), fivePoints[3]);
363             assert.equal(view.findPointByIndex(3), null);
364
365             assert.equal(view.findById(fivePoints[0].id), null);
366             assert.equal(view.findById(fivePoints[1].id), fivePoints[1]);
367             assert.equal(view.findById(fivePoints[2].id), fivePoints[2]);
368             assert.equal(view.findById(fivePoints[3].id), fivePoints[3]);
369             assert.equal(view.findById(fivePoints[4].id), null);
370
371             assert.deepEqual(view.values(), [fivePoints[1].value, fivePoints[2].value, fivePoints[3].value]);
372
373             assert.deepEqual(view.firstPointInTimeRange(fivePoints[0].time, fivePoints[4].time), fivePoints[1]);
374             assert.deepEqual(view.firstPointInTimeRange(fivePoints[1].time, fivePoints[4].time), fivePoints[1]);
375             assert.deepEqual(view.firstPointInTimeRange(fivePoints[1].time, fivePoints[1].time), fivePoints[1]);
376             assert.deepEqual(view.firstPointInTimeRange(fivePoints[0].time, fivePoints[0].time), null);
377             assert.deepEqual(view.firstPointInTimeRange(fivePoints[1].time + 0.1, fivePoints[2].time), fivePoints[2]);
378             assert.deepEqual(view.firstPointInTimeRange(fivePoints[3].time - 0.1, fivePoints[4].time), fivePoints[3]);
379             assert.deepEqual(view.firstPointInTimeRange(fivePoints[4].time, fivePoints[4].time), null);
380
381             assert.deepEqual(view.lastPointInTimeRange(fivePoints[0].time, fivePoints[4].time), fivePoints[3]);
382             assert.deepEqual(view.lastPointInTimeRange(fivePoints[1].time, fivePoints[4].time), fivePoints[3]);
383             assert.deepEqual(view.lastPointInTimeRange(fivePoints[1].time, fivePoints[1].time), fivePoints[1]);
384             assert.deepEqual(view.lastPointInTimeRange(fivePoints[0].time, fivePoints[0].time), null);
385             assert.deepEqual(view.lastPointInTimeRange(fivePoints[1].time + 0.1, fivePoints[2].time), fivePoints[2]);
386             assert.deepEqual(view.lastPointInTimeRange(fivePoints[3].time - 0.1, fivePoints[4].time), fivePoints[3]);
387             assert.deepEqual(view.lastPointInTimeRange(fivePoints[4].time, fivePoints[4].time), null);
388
389             assert.deepEqual([...view], fivePoints.slice(1, 4));
390         });
391
392         it('should create a view filtered by the specified time range on a view already filtered by a time range', () => {
393             const timeSeries = new TimeSeries();
394             addPointsToSeries(timeSeries, fivePoints);
395             const originalView = timeSeries.viewBetweenPoints(fivePoints[0], fivePoints[4]);
396             const prefilteredView = originalView.viewTimeRange(fivePoints[1].time - 0.1, fivePoints[4].time - 0.1);
397             const view = prefilteredView.viewTimeRange(fivePoints[3].time - 0.1, fivePoints[3].time + 0.1);
398
399             assert.equal(view.length(), 1);
400             assert.equal(view.firstPoint(), fivePoints[3]);
401             assert.equal(view.lastPoint(), fivePoints[3]);
402
403             assert.equal(view.nextPoint(fivePoints[3]), null);
404             assert.equal(view.previousPoint(fivePoints[3]), null);
405
406             assert.equal(view.findPointByIndex(0), fivePoints[3]);
407             assert.equal(view.findPointByIndex(1), null);
408
409             assert.equal(view.findById(fivePoints[0].id), null);
410             assert.equal(view.findById(fivePoints[1].id), null);
411             assert.equal(view.findById(fivePoints[2].id), null);
412             assert.equal(view.findById(fivePoints[3].id), fivePoints[3]);
413             assert.equal(view.findById(fivePoints[4].id), null);
414
415             assert.deepEqual(view.values(), [fivePoints[3].value]);
416
417             assert.deepEqual(view.firstPointInTimeRange(fivePoints[0].time, fivePoints[4].time), fivePoints[3]);
418             assert.deepEqual(view.firstPointInTimeRange(fivePoints[1].time, fivePoints[4].time), fivePoints[3]);
419             assert.deepEqual(view.firstPointInTimeRange(fivePoints[1].time, fivePoints[1].time), null);
420             assert.deepEqual(view.firstPointInTimeRange(fivePoints[0].time, fivePoints[0].time), null);
421             assert.deepEqual(view.firstPointInTimeRange(fivePoints[1].time + 0.1, fivePoints[2].time), null);
422             assert.deepEqual(view.firstPointInTimeRange(fivePoints[3].time - 0.1, fivePoints[4].time), fivePoints[3]);
423             assert.deepEqual(view.firstPointInTimeRange(fivePoints[4].time, fivePoints[4].time), null);
424
425             assert.deepEqual(view.lastPointInTimeRange(fivePoints[0].time, fivePoints[4].time), fivePoints[3]);
426             assert.deepEqual(view.lastPointInTimeRange(fivePoints[1].time, fivePoints[4].time), fivePoints[3]);
427             assert.deepEqual(view.lastPointInTimeRange(fivePoints[1].time, fivePoints[1].time), null);
428             assert.deepEqual(view.lastPointInTimeRange(fivePoints[0].time, fivePoints[0].time), null);
429             assert.deepEqual(view.lastPointInTimeRange(fivePoints[1].time + 0.1, fivePoints[2].time), null);
430             assert.deepEqual(view.lastPointInTimeRange(fivePoints[3].time - 0.1, fivePoints[4].time), fivePoints[3]);
431             assert.deepEqual(view.lastPointInTimeRange(fivePoints[4].time, fivePoints[4].time), null);
432
433             assert.deepEqual([...view], [fivePoints[3]]);
434         });
435
436         it('should create a view filtered by the specified time range on a view already filtered', () => {
437             const timeSeries = new TimeSeries();
438             addPointsToSeries(timeSeries, fivePoints);
439             const originalView = timeSeries.viewBetweenPoints(fivePoints[0], fivePoints[4]);
440             const prefilteredView = originalView.filter((point) => { return point == fivePoints[1] || point == fivePoints[3]; });
441             const view = prefilteredView.viewTimeRange(fivePoints[3].time - 0.1, fivePoints[3].time + 0.1);
442
443             assert.equal(view.length(), 1);
444             assert.equal(view.firstPoint(), fivePoints[3]);
445             assert.equal(view.lastPoint(), fivePoints[3]);
446
447             assert.equal(view.nextPoint(fivePoints[3]), null);
448             assert.equal(view.previousPoint(fivePoints[3]), null);
449
450             assert.equal(view.findPointByIndex(0), fivePoints[3]);
451             assert.equal(view.findPointByIndex(1), null);
452
453             assert.equal(view.findById(fivePoints[0].id), null);
454             assert.equal(view.findById(fivePoints[1].id), null);
455             assert.equal(view.findById(fivePoints[2].id), null);
456             assert.equal(view.findById(fivePoints[3].id), fivePoints[3]);
457             assert.equal(view.findById(fivePoints[4].id), null);
458
459             assert.deepEqual(view.values(), [fivePoints[3].value]);
460
461             assert.deepEqual(view.firstPointInTimeRange(fivePoints[0].time, fivePoints[4].time), fivePoints[3]);
462             assert.deepEqual(view.firstPointInTimeRange(fivePoints[1].time, fivePoints[4].time), fivePoints[3]);
463             assert.deepEqual(view.firstPointInTimeRange(fivePoints[1].time, fivePoints[1].time), null);
464             assert.deepEqual(view.firstPointInTimeRange(fivePoints[0].time, fivePoints[0].time), null);
465             assert.deepEqual(view.firstPointInTimeRange(fivePoints[1].time + 0.1, fivePoints[2].time), null);
466             assert.deepEqual(view.firstPointInTimeRange(fivePoints[3].time - 0.1, fivePoints[4].time), fivePoints[3]);
467             assert.deepEqual(view.firstPointInTimeRange(fivePoints[4].time, fivePoints[4].time), null);
468
469             assert.deepEqual(view.lastPointInTimeRange(fivePoints[0].time, fivePoints[4].time), fivePoints[3]);
470             assert.deepEqual(view.lastPointInTimeRange(fivePoints[1].time, fivePoints[4].time), fivePoints[3]);
471             assert.deepEqual(view.lastPointInTimeRange(fivePoints[1].time, fivePoints[1].time), null);
472             assert.deepEqual(view.lastPointInTimeRange(fivePoints[0].time, fivePoints[0].time), null);
473             assert.deepEqual(view.lastPointInTimeRange(fivePoints[1].time + 0.1, fivePoints[2].time), null);
474             assert.deepEqual(view.lastPointInTimeRange(fivePoints[3].time - 0.1, fivePoints[4].time), fivePoints[3]);
475             assert.deepEqual(view.lastPointInTimeRange(fivePoints[4].time, fivePoints[4].time), null);
476
477             assert.deepEqual([...view], [fivePoints[3]]);
478         });
479     });
480
481 });