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