This repository has been archived by the owner on Oct 25, 2023. It is now read-only.
-
-
Notifications
You must be signed in to change notification settings - Fork 30
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
270 additions
and
4 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,6 @@ | ||
{ | ||
"extends": "appium" | ||
"extends": "appium", | ||
"globals": { | ||
"BigInt": true | ||
} | ||
} |
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 |
---|---|---|
@@ -0,0 +1,119 @@ | ||
import _ from 'lodash'; | ||
|
||
|
||
const NS_PER_S = 1e9; | ||
const NS_PER_MS = 1e6; | ||
|
||
|
||
/** | ||
* Class representing a duration, encapsulating the number and units. | ||
*/ | ||
class Duration { | ||
constructor (nanos) { | ||
this._nanos = nanos; | ||
} | ||
|
||
get nanos () { | ||
return this._nanos; | ||
} | ||
|
||
/** | ||
* Get the duration as nanoseconds | ||
* | ||
* @returns {number} The duration as nanoseconds | ||
*/ | ||
get asNanoSeconds () { | ||
return this.nanos; | ||
} | ||
|
||
/** | ||
* Get the duration converted into milliseconds | ||
* | ||
* @returns {number} The duration as milliseconds | ||
*/ | ||
get asMilliSeconds () { | ||
return this.nanos / NS_PER_MS; | ||
} | ||
|
||
/** | ||
* Get the duration converted into seconds | ||
* | ||
* @returns {number} The duration fas seconds | ||
*/ | ||
get asSeconds () { | ||
return this.nanos / NS_PER_S; | ||
} | ||
|
||
toString () { | ||
// default to milliseconds, rounded | ||
return this.asMilliSeconds.toFixed(0); | ||
} | ||
} | ||
|
||
class Timer { | ||
/** | ||
* Creates a timer | ||
*/ | ||
constructor () { | ||
this._startTime = null; | ||
} | ||
|
||
get startTime () { | ||
return this._startTime; | ||
} | ||
|
||
/** | ||
* Start the timer | ||
* | ||
* @return {Timer} The current instance, for chaining | ||
*/ | ||
start () { | ||
if (!_.isNull(this.startTime)) { | ||
throw new Error('Timer has already been started.'); | ||
} | ||
// once Node 10 is no longer supported, this check can be removed | ||
this._startTime = _.isFunction(process.hrtime.bigint) | ||
? process.hrtime.bigint() | ||
: process.hrtime(); | ||
return this; | ||
} | ||
|
||
/** | ||
* Get the duration since the timer was started | ||
* | ||
* @return {Duration} the duration, in the specified units | ||
*/ | ||
getDuration () { | ||
if (_.isNull(this.startTime)) { | ||
throw new Error(`Unable to get duration. Timer was not started`); | ||
} | ||
|
||
let nanoDuration; | ||
if (_.isArray(this.startTime)) { | ||
// startTime was created using process.hrtime() | ||
const [seconds, nanos] = process.hrtime(this.startTime); | ||
nanoDuration = seconds * NS_PER_S + nanos; | ||
} else if (typeof this.startTime === 'bigint' && _.isFunction(process.hrtime.bigint)) { | ||
// startTime was created using process.hrtime.bigint() | ||
const endTime = process.hrtime.bigint(); | ||
// get the difference, and convert to number | ||
nanoDuration = Number(endTime - this.startTime); | ||
} else { | ||
throw new Error(`Unable to get duration. Start time '${this.startTime}' cannot be parsed`); | ||
} | ||
|
||
return new Duration(nanoDuration); | ||
} | ||
|
||
toString () { | ||
try { | ||
return this.getDuration().toString(); | ||
} catch (err) { | ||
return `<err: ${err.message}>`; | ||
} | ||
} | ||
} | ||
|
||
|
||
export { Timer, Duration }; | ||
export default Timer; |
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 |
---|---|---|
@@ -0,0 +1,143 @@ | ||
import _ from 'lodash'; | ||
import chai from 'chai'; | ||
import sinon from 'sinon'; | ||
import { timing } from '..'; | ||
|
||
|
||
chai.should(); | ||
const expect = chai.expect; | ||
|
||
describe('timing', function () { | ||
let processMock; | ||
afterEach(function () { | ||
processMock.verify(); | ||
}); | ||
|
||
describe('no bigint', function () { | ||
const bigintFn = process.hrtime.bigint; | ||
before(function () { | ||
// if the system has BigInt support, remove it | ||
if (_.isFunction(bigintFn)) { | ||
delete process.hrtime.bigint; | ||
} | ||
}); | ||
beforeEach(function () { | ||
processMock = sinon.mock(process); | ||
}); | ||
after(function () { | ||
if (_.isFunction(bigintFn)) { | ||
process.hrtime.bigint = bigintFn; | ||
} | ||
}); | ||
it('should get a start time as array', function () { | ||
const timer = new timing.Timer().start(); | ||
_.isArray(timer.startTime).should.be.true; | ||
}); | ||
it('should get a duration', function () { | ||
const timer = new timing.Timer().start(); | ||
const duration = timer.getDuration(); | ||
_.isNumber(duration.nanos).should.be.true; | ||
}); | ||
it('should get correct seconds', function () { | ||
processMock.expects('hrtime').twice() | ||
.onFirstCall().returns([12, 12345]) | ||
.onSecondCall().returns([13, 54321]); | ||
|
||
const timer = new timing.Timer().start(); | ||
const duration = timer.getDuration(); | ||
duration.asSeconds.should.eql(13.000054321); | ||
}); | ||
it('should get correct milliseconds', function () { | ||
processMock.expects('hrtime').twice() | ||
.onFirstCall().returns([12, 12345]) | ||
.onSecondCall().returns([13, 54321]); | ||
|
||
const timer = new timing.Timer().start(); | ||
const duration = timer.getDuration(); | ||
duration.asMilliSeconds.should.eql(13000.054321); | ||
}); | ||
it('should get correct nanoseconds', function () { | ||
processMock.expects('hrtime').twice() | ||
.onFirstCall().returns([12, 12345]) | ||
.onSecondCall().returns([13, 54321]); | ||
|
||
const timer = new timing.Timer().start(); | ||
const duration = timer.getDuration(); | ||
duration.asNanoSeconds.should.eql(13000054321); | ||
}); | ||
it('should error if the timer was not started', function () { | ||
const timer = new timing.Timer(); | ||
expect(() => timer.getDuration()) | ||
.to.throw('Unable to get duration'); | ||
}); | ||
it('should error if start time is a number', function () { | ||
const timer = new timing.Timer(); | ||
timer._startTime = 12345; | ||
expect(() => timer.getDuration()) | ||
.to.throw('Unable to get duration'); | ||
}); | ||
}); | ||
describe('bigint', function () { | ||
beforeEach(function () { | ||
// the non-mocked test cannot run if BigInt does not exist, | ||
// and it cannot be mocked. Luckily support was added in Node 10.4.0, | ||
// so it should not be a case where we are testing without this, | ||
// though it still can be a test that Appium is _used_ without it. | ||
if (!_.isFunction(process.hrtime.bigint)) { | ||
return this.skip(); | ||
} | ||
processMock = sinon.mock(process.hrtime); | ||
}); | ||
|
||
function setupMocks (once = false) { | ||
if (once) { | ||
processMock.expects('bigint').once() | ||
.onFirstCall().returns(BigInt(1172941153404030)); | ||
} else { | ||
processMock.expects('bigint').twice() | ||
.onFirstCall().returns(BigInt(1172941153404030)) | ||
.onSecondCall().returns(BigInt(1172951164887132)); | ||
} | ||
} | ||
|
||
it('should get a duration', function () { | ||
setupMocks(); | ||
|
||
const timer = new timing.Timer().start(); | ||
const duration = timer.getDuration(); | ||
_.isNumber(duration.nanos).should.be.true; | ||
}); | ||
it('should get correct seconds', function () { | ||
setupMocks(); | ||
|
||
const timer = new timing.Timer().start(); | ||
const duration = timer.getDuration(); | ||
duration.asSeconds.should.be.eql(10.011483102); | ||
}); | ||
it('should get correct milliseconds', function () { | ||
setupMocks(); | ||
|
||
const timer = new timing.Timer().start(); | ||
const duration = timer.getDuration(); | ||
duration.asMilliSeconds.should.be.eql(10011.483102); | ||
}); | ||
it('should get correct nanoseconds', function () { | ||
setupMocks(); | ||
|
||
const timer = new timing.Timer().start(); | ||
const duration = timer.getDuration(); | ||
duration.asNanoSeconds.should.be.eql(10011483102); | ||
}); | ||
it('should error if the timer was not started', function () { | ||
const timer = new timing.Timer(); | ||
expect(() => timer.getDuration()) | ||
.to.throw('Unable to get duration'); | ||
}); | ||
it('should error if passing in a non-bigint', function () { | ||
const timer = new timing.Timer(); | ||
timer._startTime = 12345; | ||
expect(() => timer.getDuration()) | ||
.to.throw('Unable to get duration'); | ||
}); | ||
}); | ||
}); |