Skip to content

Commit 87c7227

Browse files
author
Greg Darke
committed
Add tests for the wakelock feature.
Add tests to for both the `rfb` side (calling into the new wakelock code), and the new wakelock class (which tracks the desired state and how to get there).
1 parent 6979e68 commit 87c7227

File tree

2 files changed

+295
-0
lines changed

2 files changed

+295
-0
lines changed

tests/test.rfb.js

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import KeyTable from '../core/input/keysym.js';
99
import legacyCrypto from '../core/crypto/crypto.js';
1010

1111
import FakeWebSocket from './fake.websocket.js';
12+
import WakeLockManager from '../core/util/wakelock.js';
1213

1314
function push8(arr, num) {
1415
"use strict";
@@ -5148,6 +5149,102 @@ describe('Remote Frame Buffer protocol client', function () {
51485149
expect(RFB.messages.clientEncodings.getCall(0).args[1]).to.include(encodings.pseudoEncodingCompressLevel0 + newCompression);
51495150
});
51505151
});
5152+
5153+
describe('wakelock setting', function () {
5154+
let client;
5155+
5156+
beforeEach(function () {
5157+
sinon.spy(WakeLockManager.prototype, "acquire");
5158+
sinon.spy(WakeLockManager.prototype, "release");
5159+
5160+
client = makeRFB();
5161+
});
5162+
5163+
afterEach(function () {
5164+
WakeLockManager.prototype.acquire.restore();
5165+
WakeLockManager.prototype.release.restore();
5166+
});
5167+
5168+
it('should acquire wakelock when connected', function() {
5169+
expect(WakeLockManager.prototype.acquire).to.not.have.been.called;
5170+
5171+
client.requestLocalWakelock = true;
5172+
expect(WakeLockManager.prototype.acquire).to.have.been.calledOnce;
5173+
});
5174+
5175+
it('should acquire wakelock after connection', function() {
5176+
expect(WakeLockManager.prototype.acquire).to.not.have.been.called;
5177+
client._rfbConnectionState = 'connecting';
5178+
5179+
client.requestLocalWakelock = true;
5180+
expect(WakeLockManager.prototype.acquire).to.not.have.been.called;
5181+
5182+
client._updateConnectionState('connected');
5183+
expect(WakeLockManager.prototype.acquire).to.have.been.calledOnce;
5184+
});
5185+
5186+
it('should release wakelock when disabled', function() {
5187+
client.requestLocalWakelock = true;
5188+
expect(WakeLockManager.prototype.acquire).to.have.been.calledOnce;
5189+
expect(WakeLockManager.prototype.release).to.not.have.been.called;
5190+
5191+
client.requestLocalWakelock = false;
5192+
expect(WakeLockManager.prototype.release).to.have.been.calledOnce;
5193+
});
5194+
5195+
it('should release wakelock when disconnected', function() {
5196+
client.requestLocalWakelock = true;
5197+
expect(WakeLockManager.prototype.acquire).to.have.been.calledOnce;
5198+
expect(WakeLockManager.prototype.release).to.not.have.been.called;
5199+
5200+
client.disconnect();
5201+
expect(WakeLockManager.prototype.release).to.have.been.calledOnce;
5202+
});
5203+
5204+
it('should behave sensibly with non-boolean values', function() {
5205+
// Client starts with requestLocakWakelock = false, setting it to
5206+
// the same value should have no effect.
5207+
client.requestLocalWakelock = false;
5208+
expect(client.requestLocalWakelock).to.be.false;
5209+
expect(WakeLockManager.prototype.acquire).to.not.have.been.called;
5210+
expect(WakeLockManager.prototype.release).to.not.have.been.called;
5211+
5212+
// Setting it to something else falsely should have no effect.
5213+
client.requestLocalWakelock = null;
5214+
expect(client.requestLocalWakelock).to.be.false;
5215+
expect(WakeLockManager.prototype.acquire).to.not.have.been.called;
5216+
expect(WakeLockManager.prototype.release).to.not.have.been.called;
5217+
5218+
// Setting it to something else falsely should have no effect.
5219+
client.requestLocalWakelock = undefined;
5220+
expect(client.requestLocalWakelock).to.be.false;
5221+
expect(WakeLockManager.prototype.acquire).to.not.have.been.called;
5222+
expect(WakeLockManager.prototype.release).to.not.have.been.called;
5223+
5224+
// Switching to something true should trigger a single call to
5225+
// acquire.
5226+
client.requestLocalWakelock = true;
5227+
expect(client.requestLocalWakelock).to.be.true;
5228+
expect(WakeLockManager.prototype.acquire).to.have.been.calledOnce;
5229+
expect(WakeLockManager.prototype.release).to.not.have.been.called;
5230+
5231+
WakeLockManager.prototype.acquire.resetHistory();
5232+
5233+
// Switching to something else trueish should have no effect.
5234+
client.requestLocalWakelock = "some-value";
5235+
expect(client.requestLocalWakelock).to.be.true;
5236+
expect(WakeLockManager.prototype.acquire).to.not.have.been.called;
5237+
expect(WakeLockManager.prototype.release).to.not.have.been.called;
5238+
5239+
// Validate that switching from a trueish value to a falseish value
5240+
// works.
5241+
client.requestLocalWakelock = null;
5242+
expect(client.requestLocalWakelock).to.be.false;
5243+
expect(WakeLockManager.prototype.acquire).to.not.have.been.called;
5244+
expect(WakeLockManager.prototype.release).to.have.been.calledOnce;
5245+
});
5246+
5247+
});
51515248
});
51525249

51535250
describe('RFB messages', function () {

tests/test.wakelock.js

Lines changed: 198 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,198 @@
1+
/* jshint expr: true */
2+
3+
import WakeLockManager from '../core/util/wakelock.js';
4+
5+
class FakeWakeLockSentinal extends EventTarget {
6+
constructor() {
7+
super();
8+
this.released = false;
9+
}
10+
11+
async release() {
12+
if (this.released) {
13+
return;
14+
}
15+
this.released = true;
16+
this.dispatchEvent(new Event("release"));
17+
}
18+
}
19+
20+
function waitForStateTransition(wakelockManager, newState) {
21+
const {promise, resolve} = Promise.withResolvers();
22+
23+
const eventListener = (event) => {
24+
console.warn(`Got state change: ${event.oldState} -> ${event.newState}`);
25+
if (event.newState !== newState) {
26+
return;
27+
}
28+
wakelockManager.removeEventListener("testOnlyStateChange", eventListener);
29+
resolve();
30+
};
31+
wakelockManager.addEventListener("testOnlyStateChange", eventListener);
32+
33+
return promise;
34+
}
35+
36+
describe('WakeLockManager', function () {
37+
"use strict";
38+
39+
let wakelockRequest;
40+
beforeEach(function() {
41+
wakelockRequest = sinon.stub(navigator.wakeLock, 'request');
42+
})
43+
afterEach(function() {
44+
wakelockRequest.restore();
45+
});
46+
47+
it('can acquire and release lock', async function() {
48+
let wakeLockSentinal = new FakeWakeLockSentinal();
49+
wakelockRequest.onFirstCall().resolves(wakeLockSentinal);
50+
51+
let wlm = new WakeLockManager();
52+
expect(wakelockRequest).to.not.have.been.called;
53+
54+
let done = waitForStateTransition(wlm, 'acquired');
55+
wlm.acquire();
56+
await done;
57+
expect(wakelockRequest).to.have.been.calledOnce;
58+
expect(wakeLockSentinal.released).to.be.false;
59+
60+
done = waitForStateTransition(wlm, 'released');
61+
wlm.release();
62+
await done;
63+
expect(wakelockRequest).to.have.been.calledOnce;
64+
expect(wakeLockSentinal.released).to.be.true;
65+
});
66+
67+
it('can release without holding wakelock', async function() {
68+
let wlm = new WakeLockManager();
69+
wlm.release();
70+
expect(wakelockRequest).to.not.have.been.called;
71+
});
72+
73+
it('can release while waiting for wakelock', async function() {
74+
let wakeLockSentinal = new FakeWakeLockSentinal();
75+
let {promise, resolve} = Promise.withResolvers();
76+
77+
wakelockRequest.onFirstCall().returns(promise);
78+
79+
let wlm = new WakeLockManager();
80+
expect(wakelockRequest).to.not.have.been.called;
81+
82+
let seenAcquiring = waitForStateTransition(wlm, 'acquiring');
83+
let seenReleasing = waitForStateTransition(wlm, 'releasing');
84+
let seenReleased = waitForStateTransition(wlm, 'released');
85+
86+
wlm.acquire();
87+
await seenAcquiring;
88+
expect(wakelockRequest).to.have.been.calledOnce;
89+
90+
// We can call acquire multiple times, while waiting for the promise
91+
// to resolve.
92+
wlm.acquire();
93+
// It should not request a second wakelock.
94+
expect(wakelockRequest).to.have.been.calledOnce;
95+
96+
wlm.release();
97+
await seenReleasing;
98+
99+
expect(wakeLockSentinal.released).to.be.false;
100+
101+
// Now return the wake lock, we should immediately release it.
102+
resolve(wakeLockSentinal);
103+
await seenReleased;
104+
expect(wakeLockSentinal.released).to.be.true;
105+
});
106+
107+
it('handles visibility loss', async function() {
108+
let documentHidden = sinon.stub(document, 'hidden');
109+
let documentVisibility = sinon.stub(document, 'visibilityState');
110+
afterEach(function() {
111+
documentHidden.restore();
112+
documentVisibility.restore();
113+
});
114+
documentHidden.value(false);
115+
documentVisibility.value('visible');
116+
117+
let wakeLockSentinal1 = new FakeWakeLockSentinal();
118+
let wakeLockSentinal2 = new FakeWakeLockSentinal();
119+
wakelockRequest.onFirstCall().resolves(wakeLockSentinal1);
120+
wakelockRequest.onSecondCall().resolves(wakeLockSentinal2);
121+
122+
let wlm = new WakeLockManager();
123+
let seenAcquired = waitForStateTransition(wlm, 'acquired');
124+
let seenAwaitingVisible = waitForStateTransition(wlm, 'awaiting_visible');
125+
126+
wlm.acquire();
127+
await seenAcquired;
128+
expect(wakelockRequest).to.have.been.calledOnce;
129+
130+
// Fake a visibility change.
131+
documentHidden.value(true);
132+
documentVisibility.value('hidden');
133+
wakeLockSentinal1.release()
134+
135+
await seenAwaitingVisible;
136+
seenAcquired = waitForStateTransition(wlm, 'acquired');
137+
138+
// Fake a visibility change back
139+
documentHidden.value(false);
140+
documentVisibility.value('visible');
141+
document.dispatchEvent(new Event('visibilitychange'));
142+
await seenAcquired;
143+
144+
expect(wakelockRequest).to.have.been.calledTwice;
145+
expect(wakeLockSentinal2.released).to.be.false;
146+
});
147+
148+
it('can start hidden', async function() {
149+
let documentHidden = sinon.stub(document, 'hidden');
150+
let documentVisibility = sinon.stub(document, 'visibilityState');
151+
afterEach(function() {
152+
documentHidden.restore();
153+
documentVisibility.restore();
154+
});
155+
documentHidden.value(true);
156+
documentVisibility.value('hidden');
157+
158+
let wakeLockSentinal = new FakeWakeLockSentinal();
159+
wakelockRequest.onFirstCall().resolves(wakeLockSentinal);
160+
161+
let wlm = new WakeLockManager();
162+
let seenAwaitingVisible = waitForStateTransition(wlm, 'awaiting_visible');
163+
let seenAcquired = waitForStateTransition(wlm, 'acquired');
164+
165+
wlm.acquire();
166+
await seenAwaitingVisible;
167+
expect(wakelockRequest).to.not.have.been.called;
168+
169+
// Fake a visibility change.
170+
documentHidden.value(false);
171+
documentVisibility.value('visible');
172+
document.dispatchEvent(new Event('visibilitychange'));
173+
await seenAcquired;
174+
175+
expect(wakelockRequest).to.have.been.calledOnce;
176+
expect(wakeLockSentinal.released).to.be.false;
177+
});
178+
179+
it('handles acquire errors', async function() {
180+
wakelockRequest.onFirstCall().rejects('WakeLockError');
181+
let wakeLockSentinal = new FakeWakeLockSentinal();
182+
wakelockRequest.onSecondCall().resolves(wakeLockSentinal);
183+
184+
let wlm = new WakeLockManager();
185+
186+
let seenError = waitForStateTransition(wlm, 'error');
187+
wlm.acquire();
188+
await seenError;
189+
expect(wakelockRequest).to.have.been.calledOnce;
190+
191+
// Even though we saw an error previously, it will retry when
192+
// requested.
193+
let seenAcquired = waitForStateTransition(wlm, 'acquired');
194+
wlm.acquire();
195+
await seenAcquired;
196+
expect(wakelockRequest).to.have.been.calledTwice;
197+
});
198+
});

0 commit comments

Comments
 (0)