-
Notifications
You must be signed in to change notification settings - Fork 5
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
5 changed files
with
182 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,5 @@ | ||
export { Debouncer } from './debouncer'; | ||
export { Deferred } from './deferred'; | ||
export { sleep } from './sleep'; | ||
export { Stopwatch } from './stopwatch'; | ||
export { TimeUnit, convertTime, elapsedToString } from './time-utils'; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,109 @@ | ||
import { fakeAsync, tick } from '@angular/core/testing'; | ||
import { convertTime } from '@s-libs/js-core'; | ||
import { Stopwatch } from './stopwatch'; | ||
|
||
describe('Stopwatch', () => { | ||
beforeEach(() => { | ||
spyOn(performance, 'now').and.callFake(() => Date.now()); | ||
}); | ||
|
||
describe('constructor', () => { | ||
it('accepts a running flag', fakeAsync(() => { | ||
const started = new Stopwatch({ running: true }); | ||
const stopped = new Stopwatch({ running: false }); | ||
|
||
tick(1); | ||
|
||
expect(started.getElapsed()).toBe(1); | ||
expect(stopped.getElapsed()).toBe(0); | ||
})); | ||
|
||
it('accepts previouslyElapsed', fakeAsync(() => { | ||
expect(new Stopwatch({ previouslyElapsed: 23 }).getElapsed()).toBe(23); | ||
})); | ||
|
||
it('defaults to running w/ no previous time', fakeAsync(() => { | ||
const timer = new Stopwatch(); | ||
tick(1); | ||
expect(timer.getElapsed()).toBe(1); | ||
})); | ||
}); | ||
|
||
describe('.start()', () => { | ||
it('starts the timer', fakeAsync(() => { | ||
const timer = new Stopwatch({ running: false }); | ||
|
||
timer.start(); | ||
|
||
tick(1); | ||
expect(timer.getElapsed()).toBe(1); | ||
})); | ||
|
||
it('does nothing if already running', fakeAsync(() => { | ||
const timer = new Stopwatch(); | ||
tick(1); | ||
|
||
timer.start(); | ||
|
||
tick(1); | ||
expect(timer.getElapsed()).toBe(2); | ||
})); | ||
|
||
it('resumes the timer', fakeAsync(() => { | ||
const timer = new Stopwatch({ running: false, previouslyElapsed: 1 }); | ||
|
||
timer.start(); | ||
|
||
tick(1); | ||
expect(timer.getElapsed()).toBe(2); | ||
})); | ||
}); | ||
|
||
describe('.stop()', () => { | ||
it('stops the timer', fakeAsync(() => { | ||
const timer = new Stopwatch(); | ||
|
||
timer.stop(); | ||
|
||
tick(1); | ||
expect(timer.getElapsed()).toBe(0); | ||
})); | ||
|
||
it('preserves already elapsed time', fakeAsync(() => { | ||
const timer = new Stopwatch({ previouslyElapsed: 1 }); | ||
timer.stop(); | ||
expect(timer.getElapsed()).toBe(1); | ||
})); | ||
|
||
it('preserves currently elapsed time', fakeAsync(() => { | ||
const timer = new Stopwatch(); | ||
tick(1); | ||
|
||
timer.stop(); | ||
|
||
expect(timer.getElapsed()).toBe(1); | ||
})); | ||
}); | ||
|
||
describe('.getElapsed()', () => { | ||
it('includes previously and currently elapsed time', fakeAsync(() => { | ||
const timer = new Stopwatch({ previouslyElapsed: 1 }); | ||
tick(1); | ||
expect(timer.getElapsed()).toBe(2); | ||
})); | ||
}); | ||
|
||
describe('.toString()', () => { | ||
it('goes up to days', fakeAsync(() => { | ||
const timer = new Stopwatch(); | ||
tick(convertTime(365, 'days', 'ms')); | ||
expect(timer.toString()).toBe('365 d 0 h 0 m 0 s 0 ms'); | ||
})); | ||
|
||
it('does not show leading zeros', fakeAsync(() => { | ||
const timer = new Stopwatch(); | ||
tick(1); | ||
expect(timer.toString()).toBe('1 ms'); | ||
})); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
import { elapsedToString, isDefined } from '@s-libs/js-core'; | ||
|
||
/** | ||
* Keeps track of elapsed time. | ||
* | ||
* ```ts | ||
* const stopwatch = new Stopwatch(); | ||
* // do some stuff that should be included in time | ||
* stopwatch.stop(); | ||
* // do some more stuff that should not | ||
* stopwatch.start(); | ||
* // do more stuff that should be timed | ||
* console.log(`important stuff took ${stopwatch}`); | ||
* ``` | ||
*/ | ||
export class Stopwatch { | ||
#start?: number; | ||
#previouslyElapsed: number; | ||
|
||
/** | ||
* @param running whether the stopwatch is started immediately when constructed | ||
* @param previouslyElapsed milliseconds to be added to the total | ||
*/ | ||
constructor({ running = true, previouslyElapsed = 0 } = {}) { | ||
this.#previouslyElapsed = previouslyElapsed; | ||
if (running) { | ||
this.start(); | ||
} | ||
} | ||
|
||
/** | ||
* Starts adding elapsed time to the total. | ||
*/ | ||
start(): void { | ||
this.#start ??= performance.now(); | ||
} | ||
|
||
/** | ||
* Stops adding elapsed time to the total. | ||
*/ | ||
stop(): void { | ||
this.#previouslyElapsed += this.#getCurrentlyElapsed(); | ||
this.#start = undefined; | ||
} | ||
|
||
/** | ||
* @returns the total number of milliseconds added to the total so far. | ||
*/ | ||
getElapsed(): number { | ||
return this.#previouslyElapsed + this.#getCurrentlyElapsed(); | ||
} | ||
|
||
/** | ||
* @param units see {@link elapsedToString} | ||
*/ | ||
toString(units = ['d', 'h', 'm', 's', 'ms']): string { | ||
return elapsedToString(this.getElapsed(), units, { | ||
showLeadingZeros: false, | ||
}); | ||
} | ||
|
||
#getCurrentlyElapsed(): number { | ||
return isDefined(this.#start) ? performance.now() - this.#start : 0; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters