xref: /aosp_15_r20/external/perfetto/ui/src/base/high_precision_time_unittest.ts (revision 6dbdd20afdafa5e3ca9b8809fa73465d530080dc)
1// Copyright (C) 2024 The Android Open Source Project
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//      http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15import {Time, time} from './time';
16import {HighPrecisionTime as HPTime} from './high_precision_time';
17
18const t = Time.fromRaw;
19
20// Quick 'n' dirty function to convert a string to a HPtime
21// Used to make tests more readable
22// E.g. '1.3' -> {base: 1, offset: 0.3}
23// E.g. '-0.3' -> {base: -1, offset: 0.7}
24function mkTime(time: string): HPTime {
25  const array = time.split('.');
26  if (array.length > 2) throw new Error(`Bad time format ${time}`);
27  const [base, fractions] = array;
28  const negative = time.startsWith('-');
29  const numBase = BigInt(base);
30
31  if (fractions) {
32    const numFractions = Number(`0.${fractions}`);
33    if (negative) {
34      return new HPTime(t(numBase - 1n), 1.0 - numFractions);
35    } else {
36      return new HPTime(t(numBase), numFractions);
37    }
38  } else {
39    return new HPTime(t(numBase));
40  }
41}
42
43describe('Time', () => {
44  it('should create a new Time object with the given base and offset', () => {
45    const time = new HPTime(t(136n), 0.3);
46    expect(time.integral).toBe(136n);
47    expect(time.fractional).toBeCloseTo(0.3);
48  });
49
50  it('should normalize when offset is >= 1', () => {
51    let time = new HPTime(t(1n), 2.3);
52    expect(time.integral).toBe(3n);
53    expect(time.fractional).toBeCloseTo(0.3);
54
55    time = new HPTime(t(1n), 1);
56    expect(time.integral).toBe(2n);
57    expect(time.fractional).toBeCloseTo(0);
58  });
59
60  it('should normalize when offset is < 0', () => {
61    const time = new HPTime(t(1n), -0.4);
62    expect(time.integral).toBe(0n);
63    expect(time.fractional).toBeCloseTo(0.6);
64  });
65
66  it('should store timestamps without losing precision', () => {
67    const time = new HPTime(t(1152921504606846976n));
68    expect(time.toTime()).toBe(1152921504606846976n as time);
69  });
70
71  it('should store and manipulate timestamps without losing precision', () => {
72    let time = new HPTime(t(2315700508990407843n));
73    time = time.addTime(2315718101717517451n as time);
74    expect(time.toTime()).toBe(4631418610707925294n);
75  });
76
77  test('add', () => {
78    const result = mkTime('1.3').add(mkTime('3.1'));
79    expect(result.integral).toEqual(4n);
80    expect(result.fractional).toBeCloseTo(0.4);
81  });
82
83  test('addTime', () => {
84    const result = mkTime('200.334').addTime(t(150n));
85    expect(result.integral).toBe(350n);
86    expect(result.fractional).toBeCloseTo(0.334);
87  });
88
89  test('addNumber', () => {
90    const result = mkTime('200.334').addNumber(150.5);
91    expect(result.integral).toBe(350n);
92    expect(result.fractional).toBeCloseTo(0.834);
93  });
94
95  test('sub', () => {
96    const result = mkTime('1.3').sub(mkTime('3.1'));
97    expect(result.integral).toEqual(-2n);
98    expect(result.fractional).toBeCloseTo(0.2);
99  });
100
101  test('addTime', () => {
102    const result = mkTime('200.334').subTime(t(150n));
103    expect(result.integral).toBe(50n);
104    expect(result.fractional).toBeCloseTo(0.334);
105  });
106
107  test('subNumber', () => {
108    const result = mkTime('200.334').subNumber(150.5);
109    expect(result.integral).toBe(49n);
110    expect(result.fractional).toBeCloseTo(0.834);
111  });
112
113  test('gte', () => {
114    expect(mkTime('1.0').gte(t(1n))).toBe(true);
115    expect(mkTime('1.0').gte(t(2n))).toBe(false);
116    expect(mkTime('1.2').gte(t(1n))).toBe(true);
117    expect(mkTime('1.2').gte(t(2n))).toBe(false);
118  });
119
120  test('gt', () => {
121    expect(mkTime('1.0').gt(t(1n))).toBe(false);
122    expect(mkTime('1.0').gt(t(2n))).toBe(false);
123    expect(mkTime('1.2').gt(t(1n))).toBe(true);
124    expect(mkTime('1.2').gt(t(2n))).toBe(false);
125  });
126
127  test('lte', () => {
128    expect(mkTime('1.0').lte(t(0n))).toBe(false);
129    expect(mkTime('1.0').lte(t(1n))).toBe(true);
130    expect(mkTime('1.0').lte(t(2n))).toBe(true);
131    expect(mkTime('1.2').lte(t(1n))).toBe(false);
132    expect(mkTime('1.2').lte(t(2n))).toBe(true);
133  });
134
135  test('lt', () => {
136    expect(mkTime('1.0').lt(t(0n))).toBe(false);
137    expect(mkTime('1.0').lt(t(1n))).toBe(false);
138    expect(mkTime('1.0').lt(t(2n))).toBe(true);
139    expect(mkTime('1.2').lt(t(1n))).toBe(false);
140    expect(mkTime('1.2').lt(t(2n))).toBe(true);
141  });
142
143  test('equals', () => {
144    const time = new HPTime(t(1n), 0.2);
145    expect(time.equals(new HPTime(t(1n), 0.2))).toBeTruthy();
146    expect(time.equals(new HPTime(t(0n), 1.2))).toBeTruthy();
147    expect(time.equals(new HPTime(t(-100n), 101.2))).toBeTruthy();
148    expect(time.equals(new HPTime(t(1n), 0.3))).toBeFalsy();
149    expect(time.equals(new HPTime(t(2n), 0.2))).toBeFalsy();
150  });
151
152  test('containedWithin', () => {
153    expect(mkTime('0.9').containedWithin(t(1n), t(2n))).toBe(false);
154    expect(mkTime('1.0').containedWithin(t(1n), t(2n))).toBe(true);
155    expect(mkTime('1.2').containedWithin(t(1n), t(2n))).toBe(true);
156    expect(mkTime('2.0').containedWithin(t(1n), t(2n))).toBe(false);
157    expect(mkTime('2.1').containedWithin(t(1n), t(2n))).toBe(false);
158  });
159
160  test('clamp', () => {
161    let result = mkTime('1.2').clamp(t(1n), t(2n));
162    expect(result.integral).toBe(1n);
163    expect(result.fractional).toBeCloseTo(0.2);
164
165    result = mkTime('2.2').clamp(t(1n), t(2n));
166    expect(result.integral).toBe(2n);
167    expect(result.fractional).toBeCloseTo(0);
168
169    result = mkTime('0.2').clamp(t(1n), t(2n));
170    expect(result.integral).toBe(1n);
171    expect(result.fractional).toBeCloseTo(0);
172  });
173
174  test('toNumber', () => {
175    expect(new HPTime(t(1n), 0.2).toNumber()).toBeCloseTo(1.2);
176    expect(new HPTime(t(1000000000n), 0.0).toNumber()).toBeCloseTo(1e9);
177  });
178
179  test('toTime', () => {
180    expect(new HPTime(t(1n), 0.2).toTime('round')).toBe(1n);
181    expect(new HPTime(t(1n), 0.5).toTime('round')).toBe(2n);
182    expect(new HPTime(t(1n), 0.2).toTime('floor')).toBe(1n);
183    expect(new HPTime(t(1n), 0.5).toTime('floor')).toBe(1n);
184    expect(new HPTime(t(1n), 0.2).toTime('ceil')).toBe(2n);
185    expect(new HPTime(t(1n), 0.5).toTime('ceil')).toBe(2n);
186  });
187
188  test('toString', () => {
189    expect(mkTime('1.3').toString()).toBe('1.3');
190    expect(mkTime('12983423847.332533').toString()).toBe('12983423847.332533');
191    expect(new HPTime(t(234n)).toString()).toBe('234');
192  });
193
194  test('abs', () => {
195    let result = mkTime('-0.7').abs();
196    expect(result.integral).toEqual(0n);
197    expect(result.fractional).toBeCloseTo(0.7);
198
199    result = mkTime('-1.3').abs();
200    expect(result.integral).toEqual(1n);
201    expect(result.fractional).toBeCloseTo(0.3);
202
203    result = mkTime('-100').abs();
204    expect(result.integral).toEqual(100n);
205    expect(result.fractional).toBeCloseTo(0);
206
207    result = mkTime('34.5345').abs();
208    expect(result.integral).toEqual(34n);
209    expect(result.fractional).toBeCloseTo(0.5345);
210  });
211});
212