• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// Copyright (C) 2023 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 '../base/time';
16
17import {
18  HighPrecisionTime as HPTime,
19  HighPrecisionTimeSpan as HPTimeInterval,
20} from './high_precision_time';
21
22// Quick 'n' dirty function to convert a string to a HPtime
23// Used to make tests more readable
24// E.g. '1.3' -> {base: 1, offset: 0.3}
25// E.g. '-0.3' -> {base: -1, offset: 0.7}
26function mkTime(time: string): HPTime {
27  const array = time.split('.');
28  if (array.length > 2) throw new Error(`Bad time format ${time}`);
29  const [base, fractions] = array;
30  const negative = time.startsWith('-');
31  const numBase = BigInt(base);
32
33  if (fractions) {
34    const numFractions = Number(`0.${fractions}`);
35    if (negative) {
36      return new HPTime(numBase - 1n, 1.0 - numFractions);
37    } else {
38      return new HPTime(numBase, numFractions);
39    }
40  } else {
41    return new HPTime(numBase);
42  }
43}
44
45function mkSpan(t1: string, t2: string): HPTimeInterval {
46  return new HPTimeInterval(mkTime(t1), mkTime(t2));
47}
48
49describe('Time', () => {
50  it('should create a new Time object with the given base and offset', () => {
51    const time = new HPTime(136n, 0.3);
52    expect(time.base).toBe(136n);
53    expect(time.offset).toBeCloseTo(0.3);
54  });
55
56  it('should normalize when offset is >= 1', () => {
57    let time = new HPTime(1n, 2.3);
58    expect(time.base).toBe(3n);
59    expect(time.offset).toBeCloseTo(0.3);
60
61    time = new HPTime(1n, 1);
62    expect(time.base).toBe(2n);
63    expect(time.offset).toBeCloseTo(0);
64  });
65
66  it('should normalize when offset is < 0', () => {
67    const time = new HPTime(1n, -0.4);
68    expect(time.base).toBe(0n);
69    expect(time.offset).toBeCloseTo(0.6);
70  });
71
72  it('should store timestamps without losing precision', () => {
73    let time = HPTime.fromTime(123n as time);
74    expect(time.toTime()).toBe(123n as time);
75
76    time = HPTime.fromTime(1152921504606846976n as time);
77    expect(time.toTime()).toBe(1152921504606846976n as time);
78  });
79
80  it('should store and manipulate timestamps without losing precision', () => {
81    let time = HPTime.fromTime(123n as time);
82    time = time.addTime(Time.fromRaw(456n));
83    expect(time.toTime()).toBe(579n);
84
85    time = HPTime.fromTime(2315700508990407843n as time);
86    time = time.addTime(2315718101717517451n as time);
87    expect(time.toTime()).toBe(4631418610707925294n);
88  });
89
90  it('should add time', () => {
91    const time1 = mkTime('1.3');
92    const time2 = mkTime('3.1');
93    const result = time1.add(time2);
94    expect(result.base).toEqual(4n);
95    expect(result.offset).toBeCloseTo(0.4);
96  });
97
98  it('should subtract time', () => {
99    const time1 = mkTime('3.1');
100    const time2 = mkTime('1.3');
101    const result = time1.sub(time2);
102    expect(result.base).toEqual(1n);
103    expect(result.offset).toBeCloseTo(0.8);
104  });
105
106  it('should add nanoseconds', () => {
107    const time = mkTime('1.3');
108    const result = time.addNanos(0.8);
109    expect(result.base).toEqual(2n);
110    expect(result.offset).toBeCloseTo(0.1);
111  });
112
113  it('should add seconds', () => {
114    const time = mkTime('1.3');
115    const result = time.addSeconds(0.008);
116    expect(result.base).toEqual(8000001n);
117    expect(result.offset).toBeCloseTo(0.3);
118  });
119
120  it('should perform gte comparisions', () => {
121    const time = mkTime('1.2');
122    expect(time.gte(mkTime('0.5'))).toBeTruthy();
123    expect(time.gte(mkTime('1.1'))).toBeTruthy();
124    expect(time.gte(mkTime('1.2'))).toBeTruthy();
125    expect(time.gte(mkTime('1.5'))).toBeFalsy();
126    expect(time.gte(mkTime('5.5'))).toBeFalsy();
127  });
128
129  it('should perform gt comparisions', () => {
130    const time = mkTime('1.2');
131    expect(time.gt(mkTime('0.5'))).toBeTruthy();
132    expect(time.gt(mkTime('1.1'))).toBeTruthy();
133    expect(time.gt(mkTime('1.2'))).toBeFalsy();
134    expect(time.gt(mkTime('1.5'))).toBeFalsy();
135    expect(time.gt(mkTime('5.5'))).toBeFalsy();
136  });
137
138  it('should perform lt comparisions', () => {
139    const time = mkTime('1.2');
140    expect(time.lt(mkTime('0.5'))).toBeFalsy();
141    expect(time.lt(mkTime('1.1'))).toBeFalsy();
142    expect(time.lt(mkTime('1.2'))).toBeFalsy();
143    expect(time.lt(mkTime('1.5'))).toBeTruthy();
144    expect(time.lt(mkTime('5.5'))).toBeTruthy();
145  });
146
147  it('should perform lte comparisions', () => {
148    const time = mkTime('1.2');
149    expect(time.lte(mkTime('0.5'))).toBeFalsy();
150    expect(time.lte(mkTime('1.1'))).toBeFalsy();
151    expect(time.lte(mkTime('1.2'))).toBeTruthy();
152    expect(time.lte(mkTime('1.5'))).toBeTruthy();
153    expect(time.lte(mkTime('5.5'))).toBeTruthy();
154  });
155
156  it('should detect equality', () => {
157    const time = new HPTime(1n, 0.2);
158    expect(time.eq(new HPTime(1n, 0.2))).toBeTruthy();
159    expect(time.eq(new HPTime(0n, 1.2))).toBeTruthy();
160    expect(time.eq(new HPTime(-100n, 101.2))).toBeTruthy();
161    expect(time.eq(new HPTime(1n, 0.3))).toBeFalsy();
162    expect(time.eq(new HPTime(2n, 0.2))).toBeFalsy();
163  });
164
165  it('should clamp a time to a range', () => {
166    const time1 = mkTime('1.2');
167    const time2 = mkTime('5.4');
168    const time3 = mkTime('2.8');
169    const lower = mkTime('2.3');
170    const upper = mkTime('4.5');
171    expect(time1.clamp(lower, upper)).toEqual(lower);
172    expect(time2.clamp(lower, upper)).toEqual(upper);
173    expect(time3.clamp(lower, upper)).toEqual(time3);
174  });
175
176  it('should convert to seconds', () => {
177    expect(new HPTime(1n, 0.2).seconds).toBeCloseTo(0.0000000012);
178    expect(new HPTime(1000000000n, 0.0).seconds).toBeCloseTo(1);
179  });
180
181  it('should convert to nanos', () => {
182    expect(new HPTime(1n, 0.2).nanos).toBeCloseTo(1.2);
183    expect(new HPTime(1000000000n, 0.0).nanos).toBeCloseTo(1e9);
184  });
185
186  it('should convert to timestamps', () => {
187    expect(new HPTime(1n, 0.2).toTime('round')).toBe(1n);
188    expect(new HPTime(1n, 0.5).toTime('round')).toBe(2n);
189    expect(new HPTime(1n, 0.2).toTime('floor')).toBe(1n);
190    expect(new HPTime(1n, 0.5).toTime('floor')).toBe(1n);
191    expect(new HPTime(1n, 0.2).toTime('ceil')).toBe(2n);
192    expect(new HPTime(1n, 0.5).toTime('ceil')).toBe(2n);
193  });
194
195  it('should divide', () => {
196    let result = mkTime('1').divide(2);
197    expect(result.base).toBe(0n);
198    expect(result.offset).toBeCloseTo(0.5);
199
200    result = mkTime('1.6').divide(2);
201    expect(result.base).toBe(0n);
202    expect(result.offset).toBeCloseTo(0.8);
203
204    result = mkTime('-0.5').divide(2);
205    expect(result.base).toBe(-1n);
206    expect(result.offset).toBeCloseTo(0.75);
207
208    result = mkTime('123.1').divide(123);
209    expect(result.base).toBe(1n);
210    expect(result.offset).toBeCloseTo(0.000813, 6);
211  });
212
213  it('should multiply', () => {
214    let result = mkTime('1').multiply(2);
215    expect(result.base).toBe(2n);
216    expect(result.offset).toBeCloseTo(0);
217
218    result = mkTime('1').multiply(2.5);
219    expect(result.base).toBe(2n);
220    expect(result.offset).toBeCloseTo(0.5);
221
222    result = mkTime('-0.5').multiply(2);
223    expect(result.base).toBe(-1n);
224    expect(result.offset).toBeCloseTo(0.0);
225
226    result = mkTime('123.1').multiply(25.5);
227    expect(result.base).toBe(3139n);
228    expect(result.offset).toBeCloseTo(0.05);
229  });
230
231  it('should convert to string', () => {
232    expect(mkTime('1.3').toString()).toBe('1.3');
233    expect(mkTime('12983423847.332533').toString()).toBe('12983423847.332533');
234    expect(new HPTime(234n).toString()).toBe('234');
235  });
236
237  it('should calculate absolute', () => {
238    let result = mkTime('-0.7').abs();
239    expect(result.base).toEqual(0n);
240    expect(result.offset).toBeCloseTo(0.7);
241
242    result = mkTime('-1.3').abs();
243    expect(result.base).toEqual(1n);
244    expect(result.offset).toBeCloseTo(0.3);
245
246    result = mkTime('-100').abs();
247    expect(result.base).toEqual(100n);
248    expect(result.offset).toBeCloseTo(0);
249
250    result = mkTime('34.5345').abs();
251    expect(result.base).toEqual(34n);
252    expect(result.offset).toBeCloseTo(0.5345);
253  });
254});
255
256describe('HighPrecisionTimeSpan', () => {
257  it('can be constructed from HP time', () => {
258    const span = new HPTimeInterval(mkTime('10'), mkTime('20'));
259    expect(span.start).toEqual(mkTime('10'));
260    expect(span.end).toEqual(mkTime('20'));
261  });
262
263  it('can be constructed from integer time', () => {
264    const span = new HPTimeInterval(Time.fromRaw(10n), Time.fromRaw(20n));
265    expect(span.start).toEqual(mkTime('10'));
266    expect(span.end).toEqual(mkTime('20'));
267  });
268
269  it('throws when start is later than end', () => {
270    expect(() => new HPTimeInterval(mkTime('0.1'), mkTime('0'))).toThrow();
271    expect(
272      () => new HPTimeInterval(mkTime('1124.0001'), mkTime('1124')),
273    ).toThrow();
274  });
275
276  it('can calc duration', () => {
277    let dur = mkSpan('10', '20').duration;
278    expect(dur.base).toBe(10n);
279    expect(dur.offset).toBeCloseTo(0);
280
281    dur = mkSpan('10.123', '20.456').duration;
282    expect(dur.base).toBe(10n);
283    expect(dur.offset).toBeCloseTo(0.333);
284  });
285
286  it('can calc midpoint', () => {
287    let mid = mkSpan('10', '20').midpoint;
288    expect(mid.base).toBe(15n);
289    expect(mid.offset).toBeCloseTo(0);
290
291    mid = mkSpan('10.25', '16.75').midpoint;
292    expect(mid.base).toBe(13n);
293    expect(mid.offset).toBeCloseTo(0.5);
294  });
295
296  it('can be compared', () => {
297    expect(mkSpan('0.1', '34.2').equals(mkSpan('0.1', '34.2'))).toBeTruthy();
298    expect(mkSpan('0.1', '34.5').equals(mkSpan('0.1', '34.2'))).toBeFalsy();
299    expect(mkSpan('0.9', '34.2').equals(mkSpan('0.1', '34.2'))).toBeFalsy();
300  });
301
302  it('checks if span contains another span', () => {
303    const x = mkSpan('10', '20');
304
305    expect(x.contains(mkTime('9'))).toBeFalsy();
306    expect(x.contains(mkTime('10'))).toBeTruthy();
307    expect(x.contains(mkTime('15'))).toBeTruthy();
308    expect(x.contains(mkTime('20'))).toBeFalsy();
309    expect(x.contains(mkTime('21'))).toBeFalsy();
310
311    expect(x.contains(mkSpan('12', '18'))).toBeTruthy();
312    expect(x.contains(mkSpan('5', '25'))).toBeFalsy();
313    expect(x.contains(mkSpan('5', '15'))).toBeFalsy();
314    expect(x.contains(mkSpan('15', '25'))).toBeFalsy();
315    expect(x.contains(mkSpan('0', '10'))).toBeFalsy();
316    expect(x.contains(mkSpan('20', '30'))).toBeFalsy();
317  });
318
319  it('checks if span intersects another span', () => {
320    const x = mkSpan('10', '20');
321
322    expect(x.intersectsInterval(mkSpan('0', '10'))).toBeFalsy();
323    expect(x.intersectsInterval(mkSpan('5', '15'))).toBeTruthy();
324    expect(x.intersectsInterval(mkSpan('12', '18'))).toBeTruthy();
325    expect(x.intersectsInterval(mkSpan('15', '25'))).toBeTruthy();
326    expect(x.intersectsInterval(mkSpan('20', '30'))).toBeFalsy();
327    expect(x.intersectsInterval(mkSpan('5', '25'))).toBeTruthy();
328  });
329
330  it('checks intersection', () => {
331    const x = mkSpan('10', '20');
332
333    expect(x.intersects(mkTime('0'), mkTime('10'))).toBeFalsy();
334    expect(x.intersects(mkTime('5'), mkTime('15'))).toBeTruthy();
335    expect(x.intersects(mkTime('12'), mkTime('18'))).toBeTruthy();
336    expect(x.intersects(mkTime('15'), mkTime('25'))).toBeTruthy();
337    expect(x.intersects(mkTime('20'), mkTime('30'))).toBeFalsy();
338    expect(x.intersects(mkTime('5'), mkTime('25'))).toBeTruthy();
339  });
340});
341