Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Live location sharing - monitor liveness of beacons yet to start (PSF-1081) #2437

Merged
merged 2 commits into from
Jun 7, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 40 additions & 4 deletions spec/unit/models/beacon.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ describe('Beacon', () => {

const advanceDateAndTime = (ms: number) => {
// bc liveness check uses Date.now we have to advance this mock
jest.spyOn(global.Date, 'now').mockReturnValue(now + ms);
jest.spyOn(global.Date, 'now').mockReturnValue(Date.now() + ms);
// then advance time for the interval by the same amount
jest.advanceTimersByTime(ms);
};
Expand Down Expand Up @@ -224,13 +224,49 @@ describe('Beacon', () => {
beacon.monitorLiveness();

// @ts-ignore
expect(beacon.livenessWatchInterval).toBeFalsy();
expect(beacon.livenessWatchTimeout).toBeFalsy();
advanceDateAndTime(HOUR_MS * 2 + 1);

// no emit
expect(emitSpy).not.toHaveBeenCalled();
});

it('checks liveness of beacon at expected start time', () => {
// go forward in time to make beacon with timestamp in future
jest.spyOn(global.Date, 'now').mockReturnValue(now + HOUR_MS);
const futureBeaconEvent = makeBeaconInfoEvent(
userId,
roomId,
{
timeout: HOUR_MS * 3,
isLive: true,
},
'$live123',
);
// go back to now
jest.spyOn(global.Date, 'now').mockReturnValue(now);

const beacon = new Beacon(futureBeaconEvent);
expect(beacon.isLive).toBeFalsy();
const emitSpy = jest.spyOn(beacon, 'emit');

beacon.monitorLiveness();

// advance to the start timestamp of the beacon
advanceDateAndTime(HOUR_MS + 1);

// beacon is in live period now
expect(emitSpy).toHaveBeenCalledTimes(1);
expect(emitSpy).toHaveBeenCalledWith(BeaconEvent.LivenessChange, true, beacon);

// check the expiry monitor is still setup ok
// advance to the expiry
advanceDateAndTime(HOUR_MS * 3 + 100);

expect(emitSpy).toHaveBeenCalledTimes(2);
expect(emitSpy).toHaveBeenCalledWith(BeaconEvent.LivenessChange, false, beacon);
});

it('checks liveness of beacon at expected expiry time', () => {
// live beacon was created an hour ago
// and has a 3hr duration
Expand All @@ -253,12 +289,12 @@ describe('Beacon', () => {

beacon.monitorLiveness();
// @ts-ignore
const oldMonitor = beacon.livenessWatchInterval;
const oldMonitor = beacon.livenessWatchTimeout;

beacon.monitorLiveness();

// @ts-ignore
expect(beacon.livenessWatchInterval).not.toEqual(oldMonitor);
expect(beacon.livenessWatchTimeout).not.toEqual(oldMonitor);
});

it('destroy kills liveness monitor and emits', () => {
Expand Down
19 changes: 13 additions & 6 deletions src/models/beacon.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ export class Beacon extends TypedEventEmitter<Exclude<BeaconEvent, BeaconEvent.N
public readonly roomId: string;
private _beaconInfo: BeaconInfoState;
private _isLive: boolean;
private livenessWatchInterval: ReturnType<typeof setInterval>;
private livenessWatchTimeout: ReturnType<typeof setTimeout>;
private _latestLocationState: BeaconLocationState | undefined;

constructor(
Expand Down Expand Up @@ -109,8 +109,8 @@ export class Beacon extends TypedEventEmitter<Exclude<BeaconEvent, BeaconEvent.N
}

public destroy(): void {
if (this.livenessWatchInterval) {
clearInterval(this.livenessWatchInterval);
if (this.livenessWatchTimeout) {
clearTimeout(this.livenessWatchTimeout);
}

this._isLive = false;
Expand All @@ -122,19 +122,26 @@ export class Beacon extends TypedEventEmitter<Exclude<BeaconEvent, BeaconEvent.N
* Emits BeaconEvent.LivenessChange when beacon expires
*/
public monitorLiveness(): void {
if (this.livenessWatchInterval) {
clearInterval(this.livenessWatchInterval);
if (this.livenessWatchTimeout) {
clearTimeout(this.livenessWatchTimeout);
}

this.checkLiveness();
if (this.isLive) {
const expiryInMs = (this._beaconInfo?.timestamp + this._beaconInfo?.timeout) - Date.now();
if (expiryInMs > 1) {
this.livenessWatchInterval = setInterval(
this.livenessWatchTimeout = setTimeout(
() => { this.monitorLiveness(); },
expiryInMs,
);
}
} else if (this._beaconInfo?.timestamp > Date.now()) {
// beacon start timestamp is in the future
// check liveness again then
this.livenessWatchTimeout = setTimeout(
() => { this.monitorLiveness(); },
this.beaconInfo?.timestamp - Date.now(),
);
}
}

Expand Down