dd28a07ed438d9d568997ed76cf02f6779453f2f
[WebKit.git] / Websites / perf.webkit.org / unit-tests / measurement-set-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 const MockModels = require('./resources/mock-v3-models.js').MockModels;
10
11 describe('MeasurementSet', () => {
12     MockModels.inject();
13     let requests = MockRemoteAPI.inject();
14
15     beforeEach(() => {
16         MeasurementSet._set = null;
17     });
18
19     function waitForMeasurementSet()
20     {
21         return Promise.resolve().then(() => {
22             return Promise.resolve();
23         }).then(() => {
24             return Promise.resolve();
25         });
26     }
27
28     describe('findSet', () => {
29         it('should create a new MeasurementSet for a new pair of platform and matric', () => {
30             assert.notEqual(MeasurementSet.findSet(1, 1, 3000), MeasurementSet.findSet(1, 2, 3000));
31             assert.notEqual(MeasurementSet.findSet(1, 1, 3000), MeasurementSet.findSet(2, 1, 3000));
32         });
33
34         it('should not create a new MeasurementSet when the same pair of platform and matric are requested', () => {
35             assert.equal(MeasurementSet.findSet(1, 1), MeasurementSet.findSet(1, 1));
36         });
37     });
38
39     describe('findClusters', () => {
40
41         it('should return clusters that exist', () => {
42             const set = MeasurementSet.findSet(1, 1, 1467852503940);
43             let callCount = 0;
44             const promise = set.fetchBetween(1465084800000, 1470268800000, () => {
45                 callCount++;
46             });
47             assert.equal(requests.length, 1);
48             assert.equal(requests[0].url, '../data/measurement-set-1-1.json');
49
50             requests[0].resolve({
51                 'clusterStart': 946684800000,
52                 'clusterSize': 5184000000,
53                 'formatMap': [],
54                 'configurations': {current: []},
55                 'startTime': 1465084800000,
56                 'endTime': 1470268800000,
57                 'lastModified': 1467852503940,
58                 'clusterCount': 5,
59                 'status': 'OK'});
60
61             return promise.then(() => {
62                 assert.deepEqual(set.findClusters(0, Date.now()), [1449532800000, 1454716800000, 1459900800000, 1465084800000, 1470268800000]);
63             });
64         });
65
66     });
67
68     describe('fetchBetween', () => {
69         it('should always request the cached primary cluster first', () => {
70             const set = MeasurementSet.findSet(1, 1, 3000);
71             let callCount = 0;
72             set.fetchBetween(1000, 2000, () => callCount++);
73             assert.equal(requests.length, 1);
74             assert.equal(requests[0].url, '../data/measurement-set-1-1.json');
75             assert.equal(callCount, 0);
76         });
77
78         it('should not request the cached primary cluster twice', () => {
79             const set = MeasurementSet.findSet(1, 1, 3000);
80             let callCount = 0;
81             set.fetchBetween(1000, 2000, () => callCount++);
82             assert.equal(requests.length, 1);
83             assert.equal(callCount, 0);
84             assert.equal(requests[0].url, '../data/measurement-set-1-1.json');
85             set.fetchBetween(2000, 3000, () => callCount++);
86             assert.equal(requests.length, 1);
87             assert.equal(callCount, 0);
88         });
89
90         it('should invoke the callback when the requested range is in the cached primary cluster', () => {
91             const set = MeasurementSet.findSet(1, 1, 3000);
92             let callCount = 0;
93             const promise = set.fetchBetween(2000, 3000, () => callCount++);
94             assert.equal(requests.length, 1);
95             assert.equal(requests[0].url, '../data/measurement-set-1-1.json');
96
97             requests[0].resolve({
98                 'clusterStart': 1000,
99                 'clusterSize': 1000,
100                 'formatMap': [],
101                 'configurations': {current: []},
102                 'startTime': 2000,
103                 'endTime': 3000,
104                 'lastModified': 3000,
105                 'clusterCount': 2,
106                 'status': 'OK'});
107
108             return promise.then(() => {
109                 assert.equal(callCount, 1);
110                 assert.equal(requests.length, 1);
111             });
112         });
113
114         it('should invoke the callback and fetch a secondary cluster when the cached primary cluster is within the requested range', () => {
115             const set = MeasurementSet.findSet(1, 1, 3000);
116             let callCount = 0;
117             const promise = set.fetchBetween(1000, 3000, () => callCount++);
118             assert.equal(requests.length, 1);
119             assert.equal(requests[0].url, '../data/measurement-set-1-1.json');
120
121             requests[0].resolve({
122                 'clusterStart': 1000,
123                 'clusterSize': 1000,
124                 'formatMap': [],
125                 'configurations': {current: []},
126                 'startTime': 2000,
127                 'endTime': 3000,
128                 'lastModified': 3000,
129                 'clusterCount': 2,
130                 'status': 'OK'});
131
132             return waitForMeasurementSet().then(() => {
133                 assert.equal(callCount, 1);
134                 assert.equal(requests.length, 2);
135                 assert.equal(requests[1].url, '../data/measurement-set-1-1-2000.json');
136             });
137         });
138
139         it('should request additional secondary clusters as requested', () => {
140             const set = MeasurementSet.findSet(1, 1, 5000);
141             let callCountForWaitingCallback = 0;
142             set.fetchBetween(2000, 3000, () => callCountForWaitingCallback++);
143             assert.equal(requests.length, 1);
144             assert.equal(requests[0].url, '../data/measurement-set-1-1.json');
145
146             requests[0].resolve({
147                 'clusterStart': 1000,
148                 'clusterSize': 1000,
149                 'formatMap': [],
150                 'configurations': {current: []},
151                 'startTime': 4000,
152                 'endTime': 5000,
153                 'lastModified': 5000,
154                 'clusterCount': 4,
155                 'status': 'OK'});
156
157             let callCount = 0;
158             return waitForMeasurementSet().then(() => {
159                 assert.equal(requests.length, 2);
160                 assert.equal(requests[1].url, '../data/measurement-set-1-1-3000.json');
161
162                 set.fetchBetween(0, 7000, () => callCount++);
163
164                 return waitForMeasurementSet();
165             }).then(() => {
166                 assert.equal(callCountForWaitingCallback, 0);
167                 assert.equal(callCount, 1);
168                 assert.equal(requests.length, 4);
169                 assert.equal(requests[2].url, '../data/measurement-set-1-1-2000.json');
170                 assert.equal(requests[3].url, '../data/measurement-set-1-1-4000.json');
171             });
172         });
173
174         it('should request secondary clusters which forms a superset of the requested range', () => {
175             const set = MeasurementSet.findSet(1, 1, 5000);
176             let callCount = 0;
177             const promise = set.fetchBetween(2707, 4207, () => callCount++);
178             assert.equal(requests.length, 1);
179             assert.equal(requests[0].url, '../data/measurement-set-1-1.json');
180
181             requests[0].resolve({
182                 'clusterStart': 1000,
183                 'clusterSize': 1000,
184                 'formatMap': [],
185                 'configurations': {current: []},
186                 'startTime': 4000,
187                 'endTime': 5000,
188                 'lastModified': 5000,
189                 'clusterCount': 3,
190                 'status': 'OK'});
191
192             return waitForMeasurementSet().then(() => {
193                 assert.equal(requests.length, 3);
194                 assert.equal(requests[1].url, '../data/measurement-set-1-1-3000.json');
195                 assert.equal(requests[2].url, '../data/measurement-set-1-1-4000.json');
196                 assert.equal(callCount, 1); // 4000-4207
197             });
198         });
199
200         it('should not request secondary clusters that are not requested', () => {
201             const set = MeasurementSet.findSet(1, 1, 5000);
202             let callCountForWaitingCallback = 0;
203             set.fetchBetween(3200, 3700, () => callCountForWaitingCallback++);
204             assert.equal(requests.length, 1);
205             assert.equal(requests[0].url, '../data/measurement-set-1-1.json');
206
207             requests[0].resolve({
208                 'clusterStart': 1000,
209                 'clusterSize': 1000,
210                 'formatMap': [],
211                 'configurations': {current: []},
212                 'startTime': 4000,
213                 'endTime': 5000,
214                 'lastModified': 5000,
215                 'clusterCount': 4,
216                 'status': 'OK'});
217
218             let callCount = 0;
219             return waitForMeasurementSet().then(() => {
220                 assert.equal(requests.length, 2);
221                 assert.equal(requests[1].url, '../data/measurement-set-1-1-4000.json');
222                 set.fetchBetween(1207, 1293, () => callCount++);
223                 return waitForMeasurementSet();
224             }).then(() => {
225                 assert.equal(callCountForWaitingCallback, 0);
226                 assert.equal(callCount, 0);
227                 assert.equal(requests.length, 3);
228                 assert.equal(requests[2].url, '../data/measurement-set-1-1-2000.json');
229                 set.fetchBetween(1964, 3401, () => callCount++);
230                 return waitForMeasurementSet();
231             }).then(() => {
232                 assert.equal(callCountForWaitingCallback, 0);
233                 assert.equal(callCount, 0);
234                 assert.equal(requests.length, 4);
235                 assert.equal(requests[3].url, '../data/measurement-set-1-1-3000.json');
236             });
237         });
238
239         it('should not request a cluster before the very first cluster', () => {
240             const set = MeasurementSet.findSet(1, 1, 5000);
241             let callCount = 0;
242             set.fetchBetween(0, 3000, () => callCount++);
243             assert.equal(requests.length, 1);
244             assert.equal(requests[0].url, '../data/measurement-set-1-1.json');
245
246             requests[0].resolve({
247                 'clusterStart': 2000,
248                 'clusterSize': 1000,
249                 'formatMap': [],
250                 'configurations': {current: []},
251                 'startTime': 2000,
252                 'endTime': 3000,
253                 'lastModified': 5000,
254                 'clusterCount': 1,
255                 'status': 'OK'});
256
257             return waitForMeasurementSet().then(() => {
258                 assert.equal(requests.length, 1);
259                 assert.equal(callCount, 1);
260             });
261         });
262
263         it('should invoke the callback when the fetching of the primray cluster fails', () => {
264             const set = MeasurementSet.findSet(1, 1, 3000);
265             let callCount = 0;
266             let rejected = false;
267             set.fetchBetween(1000, 3000, () => callCount++).catch(() => rejected = true);
268             assert.equal(requests.length, 1);
269             assert.equal(requests[0].url, '../data/measurement-set-1-1.json');
270
271             requests[0].reject(500);
272
273             return waitForMeasurementSet().then(() => {
274                 assert.equal(callCount, 1);
275                 assert.equal(requests.length, 1);
276                 assert(rejected);
277             });
278         });
279
280         it('should request the uncached primary cluster when the cached cluster is outdated', () => {
281             const set = MeasurementSet.findSet(1, 1, 3005);
282             let callCount = 0;
283             set.fetchBetween(1000, 2000, () => callCount++);
284             assert.equal(requests.length, 1);
285             assert.equal(requests[0].url, '../data/measurement-set-1-1.json');
286
287             requests[0].resolve({
288                 'clusterStart': 1000,
289                 'clusterSize': 1000,
290                 'formatMap': [],
291                 'configurations': {current: []},
292                 'startTime': 2000,
293                 'endTime': 3000,
294                 'lastModified': 3000,
295                 'clusterCount': 2,
296                 'status': 'OK'});
297
298             return waitForMeasurementSet().then(() => {
299                 assert.equal(callCount, 0);
300                 assert.equal(requests.length, 2);
301                 assert.equal(requests[1].url, '../api/measurement-set?platform=1&metric=1');
302             });
303         });
304
305         it('should request the uncached primary cluster when the cached cluster is 404', () => {
306             const set = MeasurementSet.findSet(1, 1, 3005);
307             let callCount = 0;
308             set.fetchBetween(1000, 2000, () => callCount++);
309             assert.equal(requests.length, 1);
310             assert.equal(requests[0].url, '../data/measurement-set-1-1.json');
311
312             requests[0].reject(404);
313
314             return waitForMeasurementSet().then(() => {
315                 assert.equal(callCount, 0);
316                 assert.equal(requests.length, 2);
317                 assert.equal(requests[1].url, '../api/measurement-set?platform=1&metric=1');
318             });
319         });
320
321         it('should request the uncached primary cluster when noCache is true', () => {
322             const set = MeasurementSet.findSet(1, 1, 3000);
323             let callCount = 0;
324             set.fetchBetween(1000, 3000, () => callCount++);
325             assert.equal(requests.length, 1);
326             assert.equal(requests[0].url, '../data/measurement-set-1-1.json');
327
328             requests[0].resolve({
329                 'clusterStart': 1000,
330                 'clusterSize': 1000,
331                 'formatMap': [],
332                 'configurations': {current: []},
333                 'startTime': 2000,
334                 'endTime': 3000,
335                 'lastModified': 3000,
336                 'clusterCount': 2,
337                 'status': 'OK'});
338
339             let noCacheFetchCount = 0;
340             return waitForMeasurementSet().then(() => {
341                 assert.equal(callCount, 1);
342                 assert.equal(noCacheFetchCount, 0);
343                 assert.equal(set._sortedClusters.length, 1);
344                 assert.equal(requests.length, 2);
345                 assert.equal(requests[1].url, '../data/measurement-set-1-1-2000.json');
346
347                 requests[1].resolve({
348                     'clusterStart': 1000,
349                     'clusterSize': 1000,
350                     'formatMap': [],
351                     'configurations': {current: []},
352                     'startTime': 1000,
353                     'endTime': 2000,
354                     'lastModified': 3000,
355                     'clusterCount': 2,
356                     'status': 'OK'});
357
358                 set.fetchBetween(1000, 3000, () => noCacheFetchCount++, true /* noCache */);
359
360                 return waitForMeasurementSet();
361             }).then(() => {
362                 assert.equal(callCount, 2);
363                 assert.equal(noCacheFetchCount, 0);
364                 assert.equal(set._sortedClusters.length, 2);
365                 assert.equal(requests.length, 3);
366                 assert.equal(requests[2].url, '../api/measurement-set?platform=1&metric=1');
367
368                 requests[2].resolve({
369                     'clusterStart': 1000,
370                     'clusterSize': 1000,
371                     'formatMap': [],
372                     'configurations': {current: []},
373                     'startTime': 2000,
374                     'endTime': 3000,
375                     'lastModified': 3000,
376                     'clusterCount': 2,
377                     'status': 'OK'});
378
379                 return waitForMeasurementSet();
380             }).then(() => {
381                 assert.equal(callCount, 2);
382                 assert.equal(noCacheFetchCount, 1);
383                 assert.equal(set._sortedClusters.length, 2);
384                 assert.equal(requests.length, 4);
385                 assert.equal(requests[3].url, '../data/measurement-set-1-1-2000.json');
386
387                 requests[3].resolve({
388                     'clusterStart': 1000,
389                     'clusterSize': 1000,
390                     'formatMap': [],
391                     'configurations': {current: []},
392                     'startTime': 1000,
393                     'endTime': 2000,
394                     'lastModified': 3000,
395                     'clusterCount': 2,
396                     'status': 'OK'});
397
398                 return waitForMeasurementSet();
399             }).then(() => {
400                 assert.equal(callCount, 3);
401                 assert.equal(noCacheFetchCount, 2);
402                 assert.equal(set._sortedClusters.length, 2);
403                 assert.equal(requests.length, 4);
404             });
405         });
406
407         it('should not request the primary cluster twice when multiple clients request it but should invoke all callbacks', () => {
408             const set = MeasurementSet.findSet(1, 1, 3000);
409             let callCount = 0;
410             set.fetchBetween(2000, 3000, () => callCount++);
411             assert.equal(requests.length, 1);
412             assert.equal(requests[0].url, '../data/measurement-set-1-1.json');
413
414             let alternativeCallCount = 0;
415             set.fetchBetween(2000, 3000, () => alternativeCallCount++);
416
417             requests[0].resolve({
418                 'clusterStart': 1000,
419                 'clusterSize': 1000,
420                 'formatMap': [],
421                 'configurations': {current: []},
422                 'startTime': 2000,
423                 'endTime': 3000,
424                 'lastModified': 3000,
425                 'clusterCount': 2,
426                 'status': 'OK'});
427
428             return waitForMeasurementSet().then(() => {
429                 assert.equal(callCount, 1);
430                 assert.equal(alternativeCallCount, 1);
431                 assert.equal(requests.length, 1);
432             });
433         });
434
435         it('should invoke callback for each secondary clusters that are fetched or rejected', () => {
436             const set = MeasurementSet.findSet(1, 1, 5000);
437             let callCountFor4000 = 0;
438             set.fetchBetween(3200, 3700, () => callCountFor4000++);
439             assert.equal(requests.length, 1);
440             assert.equal(requests[0].url, '../data/measurement-set-1-1.json');
441
442             requests[0].resolve({
443                 'clusterStart': 1000,
444                 'clusterSize': 1000,
445                 'formatMap': [],
446                 'configurations': {current: []},
447                 'startTime': 4000,
448                 'endTime': 5000,
449                 'lastModified': 5000,
450                 'clusterCount': 4,
451                 'status': 'OK'});
452
453             let callCountFor4000To5000 = 0;
454             let callCountFor2000 = 0;
455             let callCountFor2000To4000 = 0;
456             return waitForMeasurementSet().then(() => {
457                 assert.equal(callCountFor4000, 0);
458                 assert.equal(requests.length, 2);
459                 assert.equal(requests[1].url, '../data/measurement-set-1-1-4000.json');
460
461                 set.fetchBetween(3708, 4800, () => callCountFor4000To5000++);
462                 return waitForMeasurementSet();
463             }).then(() => {
464                 assert.equal(callCountFor4000To5000, 1);
465                 assert.equal(requests.length, 2);
466
467                 set.fetchBetween(1207, 1293, () => callCountFor2000++);
468                 return waitForMeasurementSet();
469             }).then(() => {
470                 assert.equal(callCountFor2000, 0);
471                 assert.equal(requests.length, 3);
472                 assert.equal(requests[2].url, '../data/measurement-set-1-1-2000.json');
473
474                 requests[2].resolve({
475                     'formatMap': [],
476                     'configurations': {current: []},
477                     'startTime': 1000,
478                     'endTime': 2000,
479                     'lastModified': 5000,
480                     'status': 'OK'});
481                 return waitForMeasurementSet();
482             }).then(() => {
483                 assert.equal(requests.length, 3);
484                 assert.equal(callCountFor4000, 0);
485                 assert.equal(callCountFor4000To5000, 1);
486                 assert.equal(callCountFor2000, 1);
487
488                 set.fetchBetween(1964, 3401, () => { callCountFor2000To4000++; });
489                 return waitForMeasurementSet();
490             }).then(() => {
491                 assert.equal(callCountFor2000To4000, 1);
492                 assert.equal(requests.length, 4);
493                 assert.equal(requests[3].url, '../data/measurement-set-1-1-3000.json');
494
495                 requests[3].resolve({
496                     'formatMap': [],
497                     'configurations': {current: []},
498                     'startTime': 2000,
499                     'endTime': 3000,
500                     'lastModified': 5000,
501                     'status': 'OK'});
502                 return waitForMeasurementSet();
503             }).then(() => {
504                 assert.equal(callCountFor4000, 0);
505                 assert.equal(callCountFor4000To5000, 1);
506                 assert.equal(callCountFor2000, 1);
507                 assert.equal(callCountFor2000To4000, 2);
508                 assert.equal(requests.length, 4);
509
510                 requests[1].resolve({
511                     'formatMap': [],
512                     'configurations': {current: []},
513                     'startTime': 3000,
514                     'endTime': 4000,
515                     'lastModified': 5000,
516                     'status': 'OK'});
517                 return waitForMeasurementSet();
518             }).then(() => {
519                 assert.equal(callCountFor4000, 1);
520                 assert.equal(callCountFor4000To5000, 2);
521                 assert.equal(callCountFor2000, 1);
522                 assert.equal(callCountFor2000To4000, 3);
523                 assert.equal(requests.length, 4);
524             });
525         });
526
527     });
528
529     describe('hasFetchedRange', () => {
530
531         it('should return false when no clusters had been fetched', () => {
532             var set = MeasurementSet.findSet(1, 1, 3000);
533             assert(!set.hasFetchedRange(2000, 3000));
534         });
535
536         it('should return true when a single cluster contains the entire range', () => {
537             const set = MeasurementSet.findSet(1, 1, 3000);
538             const promise = set.fetchBetween(2000, 3000);
539             assert.equal(requests.length, 1);
540             assert.equal(requests[0].url, '../data/measurement-set-1-1.json');
541
542             requests[0].resolve({
543                 'clusterStart': 1000,
544                 'clusterSize': 1000,
545                 'formatMap': [],
546                 'configurations': {current: []},
547                 'startTime': 2000,
548                 'endTime': 3000,
549                 'lastModified': 3000,
550                 'clusterCount': 2,
551                 'status': 'OK'});
552
553             return promise.then(() => {
554                 assert(set.hasFetchedRange(2001, 2999));
555                 assert(set.hasFetchedRange(2000, 3000));
556             });
557         });
558
559         it('should return false when the range starts before the fetched cluster', () => {
560             const set = MeasurementSet.findSet(1, 1, 3000);
561             const promise = set.fetchBetween(2000, 3000);
562             assert.equal(requests.length, 1);
563             assert.equal(requests[0].url, '../data/measurement-set-1-1.json');
564
565             requests[0].resolve({
566                 'clusterStart': 1000,
567                 'clusterSize': 1000,
568                 'formatMap': [],
569                 'configurations': {current: []},
570                 'startTime': 2000,
571                 'endTime': 3000,
572                 'lastModified': 3000,
573                 'clusterCount': 2,
574                 'status': 'OK'});
575
576             return promise.then(() => {
577                 assert(!set.hasFetchedRange(1500, 3000));
578             });
579         });
580
581         it('should return false when the range ends after the fetched cluster', () => {
582             const set = MeasurementSet.findSet(1, 1, 5000);
583             const promise = set.fetchBetween(2000, 3000);
584             assert.equal(requests.length, 1);
585             assert.equal(requests[0].url, '../data/measurement-set-1-1.json');
586
587             requests[0].resolve({
588                 'clusterStart': 1000,
589                 'clusterSize': 1000,
590                 'formatMap': [],
591                 'configurations': {current: []},
592                 'startTime': 4000,
593                 'endTime': 5000,
594                 'lastModified': 5000,
595                 'clusterCount': 3,
596                 'status': 'OK'});
597
598             return waitForMeasurementSet().then(() => {
599                 assert.equal(requests.length, 2);
600                 assert.equal(requests[1].url, '../data/measurement-set-1-1-3000.json');
601                 requests[1].resolve({
602                     'clusterStart': 1000,
603                     'clusterSize': 1000,
604                     'formatMap': [],
605                     'configurations': {current: []},
606                     'startTime': 2000,
607                     'endTime': 3000,
608                     'lastModified': 5000,
609                     'clusterCount': 3,
610                     'status': 'OK'});
611             }).then(() => {
612                 assert(!set.hasFetchedRange(2500, 3500));
613             });
614         });
615
616         it('should return true when the range is within two fetched clusters', () => {
617             const set = MeasurementSet.findSet(1, 1, 5000);
618             set.fetchBetween(2000, 3000);
619             assert.equal(requests.length, 1);
620             assert.equal(requests[0].url, '../data/measurement-set-1-1.json');
621
622             requests[0].resolve({
623                 'clusterStart': 1000,
624                 'clusterSize': 1000,
625                 'formatMap': [],
626                 'configurations': {current: []},
627                 'startTime': 3000,
628                 'endTime': 4000,
629                 'lastModified': 5000,
630                 'clusterCount': 2,
631                 'status': 'OK'});
632
633             return waitForMeasurementSet().then(() => {
634                 assert.equal(requests.length, 2);
635                 assert.equal(requests[1].url, '../data/measurement-set-1-1-3000.json');
636                 requests[1].resolve({
637                     'clusterStart': 1000,
638                     'clusterSize': 1000,
639                     'formatMap': [],
640                     'configurations': {current: []},
641                     'startTime': 2000,
642                     'endTime': 3000,
643                     'lastModified': 5000,
644                     'clusterCount': 2,
645                     'status': 'OK'});                
646             }).then(() => {
647                 assert(set.hasFetchedRange(2500, 3500));
648             });
649         });
650
651         it('should return false when there is a cluster missing in the range', () => {
652             const set = MeasurementSet.findSet(1, 1, 5000);
653             set.fetchBetween(2000, 5000);
654             assert.equal(requests.length, 1);
655             assert.equal(requests[0].url, '../data/measurement-set-1-1.json');
656
657             requests[0].resolve({
658                 'clusterStart': 1000,
659                 'clusterSize': 1000,
660                 'formatMap': [],
661                 'configurations': {current: []},
662                 'startTime': 4000,
663                 'endTime': 5000,
664                 'lastModified': 5000,
665                 'clusterCount': 4,
666                 'status': 'OK'});
667
668             return waitForMeasurementSet().then(() => {
669                 assert.equal(requests.length, 3);
670                 assert.equal(requests[1].url, '../data/measurement-set-1-1-3000.json');
671                 assert.equal(requests[2].url, '../data/measurement-set-1-1-4000.json');
672                 requests[1].resolve({
673                     'clusterStart': 1000,
674                     'clusterSize': 1000,
675                     'formatMap': [],
676                     'configurations': {current: []},
677                     'startTime': 2000,
678                     'endTime': 3000,
679                     'lastModified': 5000,
680                     'clusterCount': 2,
681                     'status': 'OK'});
682             }).then(() => {
683                 assert(!set.hasFetchedRange(2500, 4500));
684                 assert(set.hasFetchedRange(2100, 2300));
685                 assert(set.hasFetchedRange(4000, 4800));
686             });
687         });
688
689         it('should return true even when the range extends beyond the primary cluster', () => {
690             const set = MeasurementSet.findSet(1, 1, 3000);
691             const promise = set.fetchBetween(2000, 3000);
692             assert.equal(requests.length, 1);
693             assert.equal(requests[0].url, '../data/measurement-set-1-1.json');
694
695             requests[0].resolve({
696                 'clusterStart': 1000,
697                 'clusterSize': 1000,
698                 'formatMap': [],
699                 'configurations': {current: []},
700                 'startTime': 2000,
701                 'endTime': 3000,
702                 'lastModified': 3000,
703                 'clusterCount': 2,
704                 'status': 'OK'});
705
706             return promise.then(() => {
707                 assert(set.hasFetchedRange(2001, 5000));
708             });
709         });
710
711     });
712
713     describe('fetchedTimeSeries', () => {
714
715         // Data from https://perf.webkit.org/v3/#/charts?paneList=((15-769))&since=1476426488465
716         const sampleCluster = {
717             "clusterStart": 946684800000,
718             "clusterSize": 5184000000,
719             "configurations": {
720                 "current": [
721                     [
722                         26530031, 135.26375, 80, 10821.1, 1481628.13, false,
723                         [ [27173, 1, "210096", 1482398562950] ],
724                         1482398562950, 52999, 1482413222311, "10877", 7
725                     ],
726                     [
727                         26530779, 153.2675, 80, 12261.4, 1991987.4, true, // changed to true.
728                         [ [27174,1,"210097",1482424870729] ],
729                         1482424870729, 53000, 1482424992735, "10878", 7
730                     ],
731                     [
732                         26532275, 134.2725, 80, 10741.8, 1458311.88, false,
733                         [ [ 27176, 1, "210102", 1482431464371 ] ],
734                         1482431464371, 53002, 1482436041865, "10879", 7
735                     ],
736                     [
737                         26547226, 150.9625, 80, 12077, 1908614.94, false,
738                         [ [ 27195, 1, "210168", 1482852412735 ] ],
739                         1482852412735, 53022, 1482852452143, "10902", 7
740                     ],
741                     [
742                         26559915, 141.72, 80, 11337.6, 1633126.8, false,
743                         [ [ 27211, 1, "210222", 1483347732051 ] ],
744                         1483347732051, 53039, 1483347926429, "10924", 7
745                     ],
746                     [
747                         26564388, 138.13125, 80, 11050.5, 1551157.93, false,
748                         [ [ 27217, 1, "210231", 1483412171531 ] ],
749                         1483412171531, 53045, 1483415426049, "10930", 7
750                     ],
751                     [
752                         26568867, 144.16, 80, 11532.8, 1694941.1, false,
753                         [ [ 27222, 1, "210240", 1483469584347 ] ],
754                         1483469584347, 53051, 1483469642993, "10935", 7
755                     ]
756                 ]
757             },
758             "formatMap": [
759                 "id", "mean", "iterationCount", "sum", "squareSum", "markedOutlier",
760                 "revisions",
761                 "commitTime", "build", "buildTime", "buildNumber", "builder"
762             ],
763             "startTime": 1480636800000,
764             "endTime": 1485820800000,
765             "lastModified": 1484105738736,
766             "clusterCount": 1,
767             "elapsedTime": 56.421995162964,
768             "status": "OK"
769         };
770
771         let builder;
772         let webkit;
773         beforeEach(() => {
774             builder = new Builder(7, {name: 'EFL Linux 64-bit Release WK2 (Perf)', buildUrl: 'http://build.webkit.org/builders/$builderName/$buildNumber'});
775             webkit = new Repository(1, {name: 'WebKit', url: 'http://trac.webkit.org/changeset/$1'});
776         });
777
778         function fetchSampleCluster() {
779             const set = MeasurementSet.findSet(15, 769, 1484105738736);
780             const promise = set.fetchBetween(1476426488465, 1484203801573);
781             assert.equal(requests.length, 1);
782             assert.equal(requests[0].url, '../data/measurement-set-15-769.json');
783
784             requests[0].resolve(sampleCluster);
785
786             return waitForMeasurementSet().then(() => { return set; })
787         }
788
789         it('should include all data points from the fetched cluster when includeOutliers is true', () => {
790             let points;
791             return fetchSampleCluster().then((set) => {
792                 const includeOutliers = true;
793                 const extendToFuture = false;
794                 const fullSeries = set.fetchedTimeSeries('current', includeOutliers, extendToFuture);
795                 assert.equal(fullSeries.length(), 7);
796
797                 points = new Array(7);
798                 for (let i = 0; i < 7; i++)
799                     points[i] = fullSeries.findPointByIndex(i);
800             }).then(() => {
801                 const point = points[0];
802                 assert.equal(point.id, 26530031);
803
804                 const commitTime = 1482398562950;
805                 const commitSet = point.commitSet();
806                 assert.equal(point.commitSet(), commitSet);
807                 assert.deepEqual(commitSet.repositories(), [webkit]);
808                 assert.equal(commitSet.revisionForRepository(webkit), '210096');
809                 const commit = commitSet.commitForRepository(webkit);
810                 assert.equal(commit.repository(), webkit);
811                 assert.equal(+commit.time(), commitTime);
812                 assert.equal(commit.author(), null);
813                 assert.equal(commit.revision(), '210096');
814                 assert.equal(commit.message(), null);
815                 assert.equal(commit.url(), 'http://trac.webkit.org/changeset/210096');
816                 assert.equal(commit.label(), 'r210096');
817                 assert.equal(commit.title(), 'WebKit at r210096');
818                 assert.equal(commitSet.latestCommitTime(), commitTime);
819
820                 const build = point.build();
821                 assert.equal(point.build(), build);
822                 assert.equal(build.builder(), builder);
823                 assert.equal(build.buildNumber(), 10877);
824                 const buildTime = build.buildTime();
825                 assert(buildTime instanceof Date);
826                 assert.equal(+buildTime, 1482413222311);
827
828                 assert.equal(point.time, commitTime);
829                 assert.equal(point.value, 135.26375);
830                 assert.equal(point.sum, 10821.1);
831                 assert.equal(point.squareSum, 1481628.13);
832                 assert.equal(point.iterationCount, 80);
833                 assert.equal(point.markedOutlier, false);
834             }).then(() => {
835                 const point = points[1];
836                 assert.equal(point.id, 26530779);
837
838                 const commitTime = 1482424870729;
839                 const commitSet = point.commitSet();
840                 assert.equal(commitSet.revisionForRepository(webkit), '210097');
841                 const commit = commitSet.commitForRepository(webkit);
842                 assert.equal(+commit.time(), commitTime);
843                 assert.equal(commit.revision(), '210097');
844                 assert.equal(commitSet.latestCommitTime(), commitTime);
845
846                 const build = point.build();
847                 assert.equal(build.builder(), builder);
848                 assert.equal(build.buildNumber(), 10878);
849                 assert.equal(+build.buildTime(), 1482424992735);
850
851                 assert.equal(point.time, commitTime);
852                 assert.equal(point.value, 153.2675);
853                 assert.equal(point.sum, 12261.4);
854                 assert.equal(point.squareSum, 1991987.4);
855                 assert.equal(point.iterationCount, 80);
856                 assert.equal(point.markedOutlier, true);
857             }).then(() => {
858                 assert.equal(points[2].id, 26532275);
859                 assert.equal(points[2].commitSet().revisionForRepository(webkit), '210102');
860                 assert.equal(+points[2].build().buildTime(), 1482436041865);
861                 assert.equal(points[2].build().buildNumber(), 10879);
862                 assert.equal(points[2].time, 1482431464371);
863                 assert.equal(points[2].value, 134.2725);
864
865                 assert.equal(points[3].id, 26547226);
866                 assert.equal(points[3].commitSet().revisionForRepository(webkit), '210168');
867                 assert.equal(+points[3].build().buildTime(), 1482852452143);
868                 assert.equal(points[3].build().buildNumber(), 10902);
869                 assert.equal(points[3].time, 1482852412735);
870                 assert.equal(points[3].value, 150.9625);
871
872                 assert.equal(points[4].id, 26559915);
873                 assert.equal(points[4].commitSet().revisionForRepository(webkit), '210222');
874                 assert.equal(+points[4].build().buildTime(), 1483347926429);
875                 assert.equal(points[4].build().buildNumber(), 10924);
876                 assert.equal(points[4].time, 1483347732051);
877                 assert.equal(points[4].value, 141.72);
878
879                 assert.equal(points[5].id, 26564388);
880                 assert.equal(points[5].commitSet().revisionForRepository(webkit), '210231');
881                 assert.equal(+points[5].build().buildTime(), 1483415426049);
882                 assert.equal(points[5].build().buildNumber(), 10930);
883                 assert.equal(points[5].time, 1483412171531);
884                 assert.equal(points[5].value, 138.13125);
885
886                 assert.equal(points[6].id, 26568867);
887                 assert.equal(points[6].commitSet().revisionForRepository(webkit), '210240');
888                 assert.equal(+points[6].build().buildTime(), 1483469642993);
889                 assert.equal(points[6].build().buildNumber(), 10935);
890                 assert.equal(points[6].time, 1483469584347);
891                 assert.equal(points[6].value, 144.16);
892             });
893         });
894
895         it('should include all data points from the fetched cluster when includeOutliers is false', () => {
896             return fetchSampleCluster().then((set) => {
897                 const includeOutliers = false;
898                 const extendToFuture = false;
899                 const fullSeries = set.fetchedTimeSeries('current', includeOutliers, extendToFuture);
900                 assert.equal(fullSeries.length(), 6);
901
902                 const points = new Array(6);
903                 for (let i = 0; i < 6; i++)
904                     points[i] = fullSeries.findPointByIndex(i);
905
906                 assert.equal(points[0].id, 26530031);
907                 assert.equal(points[0].commitSet().revisionForRepository(webkit), '210096');
908                 assert.equal(+points[0].build().buildTime(), 1482413222311);
909                 assert.equal(points[0].build().buildNumber(), 10877);
910                 assert.equal(points[0].time, 1482398562950);
911                 assert.equal(points[0].value, 135.26375);
912
913                 assert.equal(points[1].id, 26532275);
914                 assert.equal(points[1].commitSet().revisionForRepository(webkit), '210102');
915                 assert.equal(+points[1].build().buildTime(), 1482436041865);
916                 assert.equal(points[1].build().buildNumber(), 10879);
917                 assert.equal(points[1].time, 1482431464371);
918                 assert.equal(points[1].value, 134.2725);
919
920                 assert.equal(points[2].id, 26547226);
921                 assert.equal(points[2].commitSet().revisionForRepository(webkit), '210168');
922                 assert.equal(+points[2].build().buildTime(), 1482852452143);
923                 assert.equal(points[2].build().buildNumber(), 10902);
924                 assert.equal(points[2].time, 1482852412735);
925                 assert.equal(points[2].value, 150.9625);
926
927                 assert.equal(points[3].id, 26559915);
928                 assert.equal(points[3].commitSet().revisionForRepository(webkit), '210222');
929                 assert.equal(+points[3].build().buildTime(), 1483347926429);
930                 assert.equal(points[3].build().buildNumber(), 10924);
931                 assert.equal(points[3].time, 1483347732051);
932                 assert.equal(points[3].value, 141.72);
933
934                 assert.equal(points[4].id, 26564388);
935                 assert.equal(points[4].commitSet().revisionForRepository(webkit), '210231');
936                 assert.equal(+points[4].build().buildTime(), 1483415426049);
937                 assert.equal(points[4].build().buildNumber(), 10930);
938                 assert.equal(points[4].time, 1483412171531);
939                 assert.equal(points[4].value, 138.13125);
940
941                 assert.equal(points[5].id, 26568867);
942                 assert.equal(points[5].commitSet().revisionForRepository(webkit), '210240');
943                 assert.equal(+points[5].build().buildTime(), 1483469642993);
944                 assert.equal(points[5].build().buildNumber(), 10935);
945                 assert.equal(points[5].time, 1483469584347);
946                 assert.equal(points[5].value, 144.16);
947             });
948         });
949
950     });
951
952     describe('fetchSegmentation', () => {
953
954         var simpleSegmentableValues = [
955             1546.5603, 1548.1536, 1563.5452, 1539.7823, 1546.4184, 1548.9299, 1532.5444, 1546.2800, 1547.1760, 1551.3507,
956             1548.3277, 1544.7673, 1542.7157, 1538.1700, 1538.0948, 1543.0364, 1537.9737, 1542.2611, 1543.9685, 1546.4901,
957             1544.4080, 1540.8671, 1537.3353, 1549.4331, 1541.4436, 1544.1299, 1550.1770, 1553.1872, 1549.3417, 1542.3788,
958             1543.5094, 1541.7905, 1537.6625, 1547.3840, 1538.5185, 1549.6764, 1556.6138, 1552.0476, 1541.7629, 1544.7006,
959             /* segments changes here */
960             1587.1390, 1594.5451, 1586.2430, 1596.7310, 1548.1423
961         ];
962
963         function makeSampleRuns(values, startRunId, startTime, timeIncrement)
964         {
965             var runId = startRunId;
966             var buildId = 3400;
967             var buildNumber = 1;
968             var makeRun = function (value, commitTime) {
969                 return [runId++, value, 1, value, value, false, [], commitTime, commitTime + 10, buildId++, buildNumber++, MockModels.builder.id()];
970             }
971
972             timeIncrement = Math.floor(timeIncrement);
973             var runs = values.map(function (value, index) { return makeRun(value, startTime + index * timeIncrement); })
974             
975             return runs;
976         }
977
978         it('should be able to segment a single cluster', () => {
979             const set = MeasurementSet.findSet(1, 1, 5000);
980             const promise = set.fetchBetween(4000, 5000);
981             assert.equal(requests.length, 1);
982             assert.equal(requests[0].url, '../data/measurement-set-1-1.json');
983
984             requests[0].resolve({
985                 'clusterStart': 1000,
986                 'clusterSize': 1000,
987                 'formatMap': ['id', 'mean', 'iterationCount', 'sum', 'squareSum', 'markedOutlier', 'revisions', 'commitTime', 'build', 'buildTime', 'buildNumber', 'builder'],
988                 'configurations': {current: makeSampleRuns(simpleSegmentableValues, 6400, 4000, 1000 / 50)},
989                 'startTime': 4000,
990                 'endTime': 5000,
991                 'lastModified': 5000,
992                 'clusterCount': 4,
993                 'status': 'OK'});
994
995             var timeSeries;
996             assert.equal(set.fetchedTimeSeries('current', false, false).length(), 0);
997             return waitForMeasurementSet().then(() => {
998                 timeSeries = set.fetchedTimeSeries('current', false, false);
999                 assert.equal(timeSeries.length(), 45);
1000                 assert.equal(timeSeries.firstPoint().time, 4000);
1001                 assert.equal(timeSeries.lastPoint().time, 4880);
1002                 return set.fetchSegmentation('segmentTimeSeriesByMaximizingSchwarzCriterion', [], 'current', false);
1003             }).then((segmentation) => {
1004                 assert.equal(segmentation.length, 4);
1005
1006                 assert.equal(segmentation[0].time, 4000);
1007                 assert.almostEqual(segmentation[0].value, 1545.082);
1008                 assert.equal(segmentation[0].value, segmentation[1].value);
1009                 assert.equal(segmentation[1].time, timeSeries.findPointByIndex(39).time);
1010
1011                 assert.equal(segmentation[2].time, timeSeries.findPointByIndex(39).time);
1012                 assert.almostEqual(segmentation[2].value, 1581.872);
1013                 assert.equal(segmentation[2].value, segmentation[3].value);
1014                 assert.equal(segmentation[3].time, 4880);
1015             });
1016         });
1017
1018         it('should be able to segment two clusters', () => {
1019             const set = MeasurementSet.findSet(1, 1, 5000);
1020             const promise = set.fetchBetween(3000, 5000);
1021             assert.equal(requests.length, 1);
1022             assert.equal(requests[0].url, '../data/measurement-set-1-1.json');
1023
1024             requests[0].resolve({
1025                 'clusterStart': 1000,
1026                 'clusterSize': 1000,
1027                 'formatMap': ['id', 'mean', 'iterationCount', 'sum', 'squareSum', 'markedOutlier', 'revisions', 'commitTime', 'build', 'buildTime', 'buildNumber', 'builder'],
1028                 'configurations': {current: makeSampleRuns(simpleSegmentableValues.slice(30), 6400, 4000, 1000 / 30)},
1029                 'startTime': 4000,
1030                 'endTime': 5000,
1031                 'lastModified': 5000,
1032                 'clusterCount': 4,
1033                 'status': 'OK'});
1034
1035             return waitForMeasurementSet().then(() => {
1036                 assert.equal(requests.length, 2);
1037                 assert.equal(requests[1].url, '../data/measurement-set-1-1-4000.json');
1038                 return set.fetchSegmentation('segmentTimeSeriesByMaximizingSchwarzCriterion', [], 'current', false);
1039             }).then((segmentation) => {
1040                 const timeSeries = set.fetchedTimeSeries('current', false, false);
1041                 assert.equal(timeSeries.length(), 15);
1042                 assert.equal(timeSeries.firstPoint().time, 4000);
1043                 assert.equal(timeSeries.lastPoint().time, 4462);
1044
1045                 assert.equal(segmentation.length, 4);
1046                 assert.equal(segmentation[0].time, timeSeries.firstPoint().time);
1047                 assert.almostEqual(segmentation[0].value, 1545.441);
1048                 assert.equal(segmentation[0].value, segmentation[1].value);
1049                 assert.equal(segmentation[1].time, timeSeries.findPointByIndex(9).time);
1050
1051                 assert.equal(segmentation[2].time, timeSeries.findPointByIndex(9).time);
1052                 assert.almostEqual(segmentation[2].value, 1581.872);
1053                 assert.equal(segmentation[2].value, segmentation[3].value);
1054                 assert.equal(segmentation[3].time, timeSeries.lastPoint().time);
1055
1056                 requests[1].resolve({
1057                     'clusterStart': 1000,
1058                     'clusterSize': 1000,
1059                     'formatMap': ['id', 'mean', 'iterationCount', 'sum', 'squareSum', 'markedOutlier', 'revisions', 'commitTime', 'build', 'buildTime', 'buildNumber', 'builder'],
1060                     'configurations': {current: makeSampleRuns(simpleSegmentableValues.slice(0, 30), 6500, 3000, 1000 / 30)},
1061                     'startTime': 3000,
1062                     'endTime': 4000,
1063                     'lastModified': 5000,
1064                     'clusterCount': 4,
1065                     'status': 'OK'});
1066                 return waitForMeasurementSet();
1067             }).then(() => {
1068                 return set.fetchSegmentation('segmentTimeSeriesByMaximizingSchwarzCriterion', [], 'current', false);
1069             }).then((segmentation) => {
1070                 var timeSeries = set.fetchedTimeSeries('current', false, false);
1071                 assert.equal(timeSeries.length(), 45);
1072                 assert.equal(timeSeries.firstPoint().time, 3000);
1073                 assert.equal(timeSeries.lastPoint().time, 4462);
1074                 assert.equal(segmentation.length, 4);
1075
1076                 assert.equal(segmentation[0].time, timeSeries.firstPoint().time);
1077                 assert.almostEqual(segmentation[0].value, 1545.082);
1078                 assert.equal(segmentation[0].value, segmentation[1].value);
1079                 assert.equal(segmentation[1].time, timeSeries.findPointByIndex(39).time);
1080
1081                 assert.equal(segmentation[2].time, timeSeries.findPointByIndex(39).time);
1082                 assert.almostEqual(segmentation[2].value, 1581.872);
1083                 assert.equal(segmentation[2].value, segmentation[3].value);
1084                 assert.equal(segmentation[3].time, timeSeries.lastPoint().time);
1085             });
1086         });
1087
1088     });
1089 });