xref: /aosp_15_r20/development/tools/winscope/src/common/timestamp_converter_test.ts (revision 90c8c64db3049935a07c6143d7fd006e26f8ecca)
1/*
2 * Copyright (C) 2022 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17import {TimestampConverterUtils} from 'test/unit/timestamp_converter_utils';
18import {UnitTestUtils} from 'test/unit/utils';
19import {TimestampConverter} from './timestamp_converter';
20import {TIME_UNIT_TO_NANO} from './time_units';
21
22describe('TimestampConverter', () => {
23  const MILLISECOND = BigInt(TIME_UNIT_TO_NANO.ms);
24  const SECOND = BigInt(TIME_UNIT_TO_NANO.s);
25  const MINUTE = BigInt(TIME_UNIT_TO_NANO.m);
26  const HOUR = BigInt(TIME_UNIT_TO_NANO.h);
27  const DAY = BigInt(TIME_UNIT_TO_NANO.d);
28
29  const testElapsedNs = 100n;
30  const testRealNs = 1659243341051481088n; // Sun, 31 Jul 2022 04:55:41 GMT to test timestamp conversion between different days
31  const testMonotonicTimeOffsetNs = 5n * MILLISECOND;
32  const testRealToBootTimeOffsetNs = MILLISECOND;
33
34  beforeAll(() => {
35    jasmine.addCustomEqualityTester(UnitTestUtils.timestampEqualityTester);
36  });
37
38  describe('makes timestamps from ns without timezone info', () => {
39    const converterWithMonotonicOffset = new TimestampConverter(
40      TimestampConverterUtils.UTC_TIMEZONE_INFO,
41    );
42    converterWithMonotonicOffset.setRealToMonotonicTimeOffsetNs(
43      testMonotonicTimeOffsetNs,
44    );
45
46    const converterWithBootTimeOffset = new TimestampConverter(
47      TimestampConverterUtils.UTC_TIMEZONE_INFO,
48    );
49    converterWithBootTimeOffset.setRealToBootTimeOffsetNs(
50      testRealToBootTimeOffsetNs,
51    );
52
53    it('can create real-formatted timestamp without real-time offset set', () => {
54      const timestamp = new TimestampConverter(
55        TimestampConverterUtils.UTC_TIMEZONE_INFO,
56      ).makeTimestampFromRealNs(testRealNs);
57      expect(timestamp.getValueNs()).toBe(testRealNs);
58      expect(timestamp.format()).toEqual('2022-07-31, 04:55:41.051');
59    });
60
61    it('can create real-formatted timestamp with real to monotonic offset', () => {
62      const timestamp =
63        converterWithMonotonicOffset.makeTimestampFromMonotonicNs(testRealNs);
64      expect(timestamp.getValueNs()).toBe(
65        testRealNs + testMonotonicTimeOffsetNs,
66      );
67      expect(timestamp.format()).toEqual('2022-07-31, 04:55:41.056');
68    });
69
70    it('can create real-formatted timestamp with real to boot time offset', () => {
71      const timestamp =
72        converterWithBootTimeOffset.makeTimestampFromBootTimeNs(testRealNs);
73      expect(timestamp.getValueNs()).toBe(
74        testRealNs + testRealToBootTimeOffsetNs,
75      );
76      expect(timestamp.format()).toEqual('2022-07-31, 04:55:41.052');
77    });
78
79    it('can create elapsed-formatted timestamp', () => {
80      const timestamp = new TimestampConverter(
81        TimestampConverterUtils.UTC_TIMEZONE_INFO,
82      ).makeTimestampFromMonotonicNs(testElapsedNs);
83      expect(timestamp.getValueNs()).toBe(testElapsedNs);
84      expect(timestamp.format()).toEqual('100ns');
85    });
86
87    it('formats real-formatted timestamp with offset correctly', () => {
88      expect(
89        converterWithMonotonicOffset
90          .makeTimestampFromMonotonicNs(100n * MILLISECOND)
91          .format(),
92      ).toEqual('1970-01-01, 00:00:00.105');
93      expect(
94        converterWithMonotonicOffset
95          .makeTimestampFromRealNs(100n * MILLISECOND)
96          .format(),
97      ).toEqual('1970-01-01, 00:00:00.100');
98    });
99  });
100
101  describe('makes timestamps from ns with timezone info', () => {
102    const converterWithMonotonicOffset = new TimestampConverter(
103      TimestampConverterUtils.ASIA_TIMEZONE_INFO,
104    );
105    converterWithMonotonicOffset.setRealToMonotonicTimeOffsetNs(
106      testMonotonicTimeOffsetNs,
107    );
108    converterWithMonotonicOffset.initializeUTCOffset(
109      converterWithMonotonicOffset.makeTimestampFromRealNs(testRealNs),
110    );
111
112    const converterWithBootTimeOffset = new TimestampConverter(
113      TimestampConverterUtils.ASIA_TIMEZONE_INFO,
114    );
115    converterWithBootTimeOffset.setRealToBootTimeOffsetNs(
116      testRealToBootTimeOffsetNs,
117    );
118    converterWithBootTimeOffset.initializeUTCOffset(
119      converterWithBootTimeOffset.makeTimestampFromRealNs(testRealNs),
120    );
121
122    it('can create real-formatted timestamp without real-time offset set', () => {
123      const converter = new TimestampConverter(
124        TimestampConverterUtils.ASIA_TIMEZONE_INFO,
125      );
126      converter.initializeUTCOffset(
127        converter.makeTimestampFromRealNs(testRealNs),
128      );
129
130      const timestamp = converter.makeTimestampFromRealNs(testRealNs);
131      expect(timestamp.getValueNs()).toBe(testRealNs);
132      expect(timestamp.format()).toEqual('2022-07-31, 10:25:41.051');
133    });
134
135    it('can create real-formatted timestamp with monotonic offset', () => {
136      const timestamp =
137        converterWithMonotonicOffset.makeTimestampFromMonotonicNs(testRealNs);
138      expect(timestamp.getValueNs()).toBe(
139        testRealNs + testMonotonicTimeOffsetNs,
140      );
141      expect(timestamp.format()).toEqual('2022-07-31, 10:25:41.056');
142    });
143
144    it('can create real-formatted timestamp with real to boot time offset', () => {
145      const timestamp =
146        converterWithBootTimeOffset.makeTimestampFromBootTimeNs(testRealNs);
147      expect(timestamp.getValueNs()).toBe(
148        testRealNs + testRealToBootTimeOffsetNs,
149      );
150      expect(timestamp.format()).toEqual('2022-07-31, 10:25:41.052');
151    });
152
153    it('can create elapsed-formatted timestamp', () => {
154      const timestamp = new TimestampConverter(
155        TimestampConverterUtils.ASIA_TIMEZONE_INFO,
156      ).makeTimestampFromMonotonicNs(testElapsedNs);
157      expect(timestamp.getValueNs()).toBe(testElapsedNs);
158      expect(timestamp.format()).toEqual('100ns');
159    });
160
161    describe('adds correct offset for different timezones', () => {
162      it('creates correct real-formatted timestamps for different timezones', () => {
163        const londonConverter = new TimestampConverter(
164          {
165            timezone: 'Europe/London',
166            locale: 'en-US',
167          },
168          0n,
169        );
170        londonConverter.initializeUTCOffset(
171          londonConverter.makeTimestampFromRealNs(testRealNs),
172        );
173        expect(
174          londonConverter.makeTimestampFromRealNs(testRealNs).format(),
175        ).toEqual('2022-07-31, 05:55:41.051');
176
177        const zurichConverter = new TimestampConverter(
178          {
179            timezone: 'Europe/Zurich',
180            locale: 'en-US',
181          },
182          0n,
183        );
184        zurichConverter.initializeUTCOffset(
185          zurichConverter.makeTimestampFromRealNs(testRealNs),
186        );
187        expect(
188          zurichConverter.makeTimestampFromRealNs(testRealNs).format(),
189        ).toEqual('2022-07-31, 06:55:41.051');
190
191        const westCoastConverter = new TimestampConverter(
192          {
193            timezone: 'America/Los_Angeles',
194            locale: 'en-US',
195          },
196          0n,
197        );
198        westCoastConverter.initializeUTCOffset(
199          westCoastConverter.makeTimestampFromRealNs(testRealNs),
200        );
201        expect(
202          westCoastConverter.makeTimestampFromRealNs(testRealNs).format(),
203        ).toEqual('2022-07-30, 21:55:41.051');
204
205        const indiaConverter = new TimestampConverter(
206          {
207            timezone: 'Asia/Kolkata',
208            locale: 'en-US',
209          },
210          0n,
211        );
212        indiaConverter.initializeUTCOffset(
213          indiaConverter.makeTimestampFromRealNs(testRealNs),
214        );
215        expect(
216          indiaConverter.makeTimestampFromRealNs(testRealNs).format(),
217        ).toEqual('2022-07-31, 10:25:41.051');
218      });
219    });
220  });
221
222  describe('makes timestamps from string without timezone info', () => {
223    const converterWithoutOffsets = new TimestampConverter(
224      TimestampConverterUtils.UTC_TIMEZONE_INFO,
225    );
226
227    const converterWithMonotonicOffset = new TimestampConverter(
228      TimestampConverterUtils.UTC_TIMEZONE_INFO,
229    );
230    converterWithMonotonicOffset.setRealToMonotonicTimeOffsetNs(
231      testMonotonicTimeOffsetNs,
232    );
233
234    it('makeTimestampfromHumanElapsed', () => {
235      expect(converterWithoutOffsets.makeTimestampFromHuman('0ns')).toEqual(
236        converterWithoutOffsets.makeTimestampFromMonotonicNs(0n),
237      );
238      expect(converterWithoutOffsets.makeTimestampFromHuman('1000ns')).toEqual(
239        converterWithoutOffsets.makeTimestampFromMonotonicNs(1000n),
240      );
241      expect(converterWithoutOffsets.makeTimestampFromHuman('0ms')).toEqual(
242        converterWithoutOffsets.makeTimestampFromMonotonicNs(0n),
243      );
244      expect(converterWithoutOffsets.makeTimestampFromHuman('1ms')).toEqual(
245        converterWithoutOffsets.makeTimestampFromMonotonicNs(MILLISECOND),
246      );
247      expect(converterWithoutOffsets.makeTimestampFromHuman('10ms')).toEqual(
248        converterWithoutOffsets.makeTimestampFromMonotonicNs(10n * MILLISECOND),
249      );
250
251      expect(converterWithoutOffsets.makeTimestampFromHuman('999ms')).toEqual(
252        converterWithoutOffsets.makeTimestampFromMonotonicNs(
253          999n * MILLISECOND,
254        ),
255      );
256      expect(converterWithoutOffsets.makeTimestampFromHuman('1s')).toEqual(
257        converterWithoutOffsets.makeTimestampFromMonotonicNs(SECOND),
258      );
259      expect(converterWithoutOffsets.makeTimestampFromHuman('1s0ms')).toEqual(
260        converterWithoutOffsets.makeTimestampFromMonotonicNs(SECOND),
261      );
262      expect(
263        converterWithoutOffsets.makeTimestampFromHuman('1s0ms0ns'),
264      ).toEqual(converterWithoutOffsets.makeTimestampFromMonotonicNs(SECOND));
265      expect(
266        converterWithoutOffsets.makeTimestampFromHuman('1s0ms1ns'),
267      ).toEqual(
268        converterWithoutOffsets.makeTimestampFromMonotonicNs(SECOND + 1n),
269      );
270      expect(converterWithoutOffsets.makeTimestampFromHuman('0d1s1ms')).toEqual(
271        converterWithoutOffsets.makeTimestampFromMonotonicNs(
272          SECOND + MILLISECOND,
273        ),
274      );
275
276      expect(converterWithoutOffsets.makeTimestampFromHuman('1m0s0ms')).toEqual(
277        converterWithoutOffsets.makeTimestampFromMonotonicNs(MINUTE),
278      );
279      expect(converterWithoutOffsets.makeTimestampFromHuman('1m1s1ms')).toEqual(
280        converterWithoutOffsets.makeTimestampFromMonotonicNs(
281          MINUTE + SECOND + MILLISECOND,
282        ),
283      );
284
285      expect(converterWithoutOffsets.makeTimestampFromHuman('1h0m')).toEqual(
286        converterWithoutOffsets.makeTimestampFromMonotonicNs(HOUR),
287      );
288      expect(
289        converterWithoutOffsets.makeTimestampFromHuman('1h1m1s1ms'),
290      ).toEqual(
291        converterWithoutOffsets.makeTimestampFromMonotonicNs(
292          HOUR + MINUTE + SECOND + MILLISECOND,
293        ),
294      );
295
296      expect(converterWithoutOffsets.makeTimestampFromHuman('1d0s1ms')).toEqual(
297        converterWithoutOffsets.makeTimestampFromMonotonicNs(DAY + MILLISECOND),
298      );
299      expect(
300        converterWithoutOffsets.makeTimestampFromHuman('1d1h1m1s1ms'),
301      ).toEqual(
302        converterWithoutOffsets.makeTimestampFromMonotonicNs(
303          DAY + HOUR + MINUTE + SECOND + MILLISECOND,
304        ),
305      );
306
307      expect(converterWithoutOffsets.makeTimestampFromHuman('1d')).toEqual(
308        converterWithoutOffsets.makeTimestampFromMonotonicNs(DAY),
309      );
310      expect(converterWithoutOffsets.makeTimestampFromHuman('1d1ms')).toEqual(
311        converterWithoutOffsets.makeTimestampFromMonotonicNs(DAY + MILLISECOND),
312      );
313    });
314
315    it('makeTimestampfromHumanElapsed throws on invalid input format', () => {
316      const invalidFormatError = new Error('Invalid timestamp format');
317      expect(() =>
318        converterWithoutOffsets.makeTimestampFromHuman('1d1h1m1s0ns1ms'),
319      ).toThrow(invalidFormatError);
320      expect(() =>
321        converterWithoutOffsets.makeTimestampFromHuman('1dns'),
322      ).toThrow(invalidFormatError);
323      expect(() =>
324        converterWithoutOffsets.makeTimestampFromHuman('100'),
325      ).toThrow(invalidFormatError);
326      expect(() => converterWithoutOffsets.makeTimestampFromHuman('')).toThrow(
327        invalidFormatError,
328      );
329    });
330
331    it('makeTimestampFromHumanReal', () => {
332      const NOV_10_2022 = 1668038400000n * MILLISECOND;
333      expect(
334        converterWithMonotonicOffset.makeTimestampFromHuman(
335          '2022-11-10T22:04:54.186123212',
336        ),
337      ).toEqual(
338        converterWithMonotonicOffset.makeTimestampFromRealNs(
339          NOV_10_2022 + 22n * HOUR + 4n * MINUTE + 54n * SECOND + 186123212n,
340        ),
341      );
342      expect(
343        converterWithMonotonicOffset.makeTimestampFromHuman(
344          '2022-11-10T22:04:54.186123212Z',
345        ),
346      ).toEqual(
347        converterWithMonotonicOffset.makeTimestampFromRealNs(
348          NOV_10_2022 +
349            22n * HOUR +
350            4n * MINUTE +
351            54n * SECOND +
352            186n * MILLISECOND +
353            123212n,
354        ),
355      );
356      expect(
357        converterWithMonotonicOffset.makeTimestampFromHuman(
358          '2022-11-10T22:04:54.186000212',
359        ),
360      ).toEqual(
361        converterWithMonotonicOffset.makeTimestampFromRealNs(
362          NOV_10_2022 +
363            22n * HOUR +
364            4n * MINUTE +
365            54n * SECOND +
366            186n * MILLISECOND +
367            212n,
368        ),
369      );
370      expect(
371        converterWithMonotonicOffset.makeTimestampFromHuman(
372          '2022-11-10T22:04:54.006000002',
373        ),
374      ).toEqual(
375        converterWithMonotonicOffset.makeTimestampFromRealNs(
376          NOV_10_2022 + 22n * HOUR + 4n * MINUTE + 54n * SECOND + 6000002n,
377        ),
378      );
379      expect(
380        converterWithMonotonicOffset.makeTimestampFromHuman(
381          '2022-11-10T06:04:54.006000002',
382        ),
383      ).toEqual(
384        converterWithMonotonicOffset.makeTimestampFromRealNs(
385          NOV_10_2022 + 6n * HOUR + 4n * MINUTE + 54n * SECOND + 6000002n,
386        ),
387      );
388      expect(
389        converterWithMonotonicOffset.makeTimestampFromHuman(
390          '2022-11-10T06:04:54',
391        ),
392      ).toEqual(
393        converterWithMonotonicOffset.makeTimestampFromRealNs(
394          NOV_10_2022 + 6n * HOUR + 4n * MINUTE + 54n * SECOND,
395        ),
396      );
397      expect(
398        converterWithMonotonicOffset.makeTimestampFromHuman(
399          '2022-11-10T06:04:54.0',
400        ),
401      ).toEqual(
402        converterWithMonotonicOffset.makeTimestampFromRealNs(
403          NOV_10_2022 + 6n * HOUR + 4n * MINUTE + 54n * SECOND,
404        ),
405      );
406      expect(
407        converterWithMonotonicOffset.makeTimestampFromHuman(
408          '2022-11-10T06:04:54.0100',
409        ),
410      ).toEqual(
411        converterWithMonotonicOffset.makeTimestampFromRealNs(
412          NOV_10_2022 +
413            6n * HOUR +
414            4n * MINUTE +
415            54n * SECOND +
416            10n * MILLISECOND,
417        ),
418      );
419      expect(
420        converterWithMonotonicOffset.makeTimestampFromHuman(
421          '2022-11-10T06:04:54.0175328',
422        ),
423      ).toEqual(
424        converterWithMonotonicOffset.makeTimestampFromRealNs(
425          NOV_10_2022 + 6n * HOUR + 4n * MINUTE + 54n * SECOND + 17532800n,
426        ),
427      );
428    });
429
430    it('makeTimestampFromHumanReal throws on invalid input format', () => {
431      const invalidFormatError = new Error('Invalid timestamp format');
432      expect(() =>
433        converterWithMonotonicOffset.makeTimestampFromHuman('100'),
434      ).toThrow(invalidFormatError);
435      expect(() =>
436        converterWithMonotonicOffset.makeTimestampFromHuman(
437          '06h4m54s, 10 Nov 2022',
438        ),
439      ).toThrow(invalidFormatError);
440      expect(() =>
441        converterWithMonotonicOffset.makeTimestampFromHuman(''),
442      ).toThrow(invalidFormatError);
443      expect(() =>
444        converterWithMonotonicOffset.makeTimestampFromHuman(
445          '2022-11-10T06:04:54.',
446        ),
447      ).toThrow(invalidFormatError);
448      expect(() =>
449        converterWithMonotonicOffset.makeTimestampFromHuman(
450          '2022-11-10T06:04:54.1234567890',
451        ),
452      ).toThrow(invalidFormatError);
453      expect(() =>
454        converterWithMonotonicOffset.makeTimestampFromHuman(
455          '06:04:54.1234567890',
456        ),
457      ).toThrow(invalidFormatError);
458    });
459
460    it('can reverse-date format', () => {
461      expect(
462        converterWithMonotonicOffset
463          .makeTimestampFromHuman('2022-11-10, 22:04:54.186123212')
464          .format(),
465      ).toEqual('2022-11-10, 22:04:54.186');
466    });
467  });
468
469  describe('makes timestamps from string with timezone info', () => {
470    const converter = new TimestampConverter(
471      TimestampConverterUtils.ASIA_TIMEZONE_INFO,
472    );
473    converter.setRealToMonotonicTimeOffsetNs(testMonotonicTimeOffsetNs);
474    converter.initializeUTCOffset(
475      converter.makeTimestampFromRealNs(testRealNs),
476    );
477
478    it('makeTimestampFromHumanReal', () => {
479      const NOV_10_2022 = 1668038400000n * MILLISECOND;
480      testMakeTimestampFromHumanReal(
481        '2022-11-11T03:34:54.186123212',
482        NOV_10_2022 +
483          22n * HOUR +
484          4n * MINUTE +
485          54n * SECOND +
486          186n * MILLISECOND +
487          123212n,
488        '2022-11-11, 03:34:54.186',
489      );
490
491      testMakeTimestampFromHumanReal(
492        '2022-11-11T03:34:54.186123212Z',
493        NOV_10_2022 +
494          22n * HOUR +
495          4n * MINUTE +
496          54n * SECOND +
497          186n * MILLISECOND +
498          123212n,
499        '2022-11-11, 03:34:54.186',
500      );
501
502      testMakeTimestampFromHumanReal(
503        '2022-11-10T11:34:54',
504        NOV_10_2022 + 6n * HOUR + 4n * MINUTE + 54n * SECOND,
505        '2022-11-10, 11:34:54.000',
506      );
507
508      testMakeTimestampFromHumanReal(
509        '2022-11-10T11:34:54.0',
510        NOV_10_2022 + 6n * HOUR + 4n * MINUTE + 54n * SECOND,
511        '2022-11-10, 11:34:54.000',
512      );
513
514      testMakeTimestampFromHumanReal(
515        '2022-11-10T11:34:54.0100',
516        NOV_10_2022 +
517          6n * HOUR +
518          4n * MINUTE +
519          54n * SECOND +
520          10n * MILLISECOND,
521        '2022-11-10, 11:34:54.010',
522      );
523
524      testMakeTimestampFromHumanReal(
525        '2022-11-10T11:34:54.0175328',
526        NOV_10_2022 + 6n * HOUR + 4n * MINUTE + 54n * SECOND + 17532800n,
527        '2022-11-10, 11:34:54.018',
528      );
529    });
530
531    it('can reverse-date format', () => {
532      expect(
533        converter
534          .makeTimestampFromHuman('2022-11-11, 03:34:54.186123212')
535          .format(),
536      ).toEqual('2022-11-11, 03:34:54.186');
537    });
538
539    function testMakeTimestampFromHumanReal(
540      timestampHuman: string,
541      expectedNs: bigint,
542      expectedFormattedTimestamp: string,
543    ) {
544      const timestamp = converter.makeTimestampFromHuman(timestampHuman);
545      expect(timestamp.getValueNs()).toEqual(expectedNs);
546      expect(timestamp.format()).toEqual(expectedFormattedTimestamp);
547    }
548  });
549});
550