forked from electron/electron
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathapi-power-monitor-spec.ts
193 lines (173 loc) · 7.44 KB
/
api-power-monitor-spec.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
// For these tests we use a fake DBus daemon to verify powerMonitor module
// interaction with the system bus. This requires python-dbusmock installed and
// running (with the DBUS_SYSTEM_BUS_ADDRESS environment variable set).
// script/spec-runner.js will take care of spawning the fake DBus daemon and setting
// DBUS_SYSTEM_BUS_ADDRESS when python-dbusmock is installed.
//
// See https://pypi.python.org/pypi/python-dbusmock for more information about
// python-dbusmock.
import { expect } from 'chai';
import * as dbus from 'dbus-native';
import { ifdescribe, startRemoteControlApp } from './lib/spec-helpers';
import { promisify } from 'node:util';
import { setTimeout } from 'node:timers/promises';
describe('powerMonitor', () => {
let logindMock: any, dbusMockPowerMonitor: any, getCalls: any, emitSignal: any, reset: any;
ifdescribe(process.platform === 'linux' && process.env.DBUS_SYSTEM_BUS_ADDRESS != null)('when powerMonitor module is loaded with dbus mock', () => {
before(async () => {
const systemBus = dbus.systemBus();
const loginService = systemBus.getService('org.freedesktop.login1');
const getInterface = promisify(loginService.getInterface.bind(loginService));
logindMock = await getInterface('/org/freedesktop/login1', 'org.freedesktop.DBus.Mock');
getCalls = promisify(logindMock.GetCalls.bind(logindMock));
emitSignal = promisify(logindMock.EmitSignal.bind(logindMock));
reset = promisify(logindMock.Reset.bind(logindMock));
});
after(async () => {
await reset();
});
function onceMethodCalled (done: () => void) {
function cb () {
logindMock.removeListener('MethodCalled', cb);
}
done();
return cb;
}
before(done => {
logindMock.on('MethodCalled', onceMethodCalled(done));
// lazy load powerMonitor after we listen to MethodCalled mock signal
dbusMockPowerMonitor = require('electron').powerMonitor;
});
it('should call Inhibit to delay suspend once a listener is added', async () => {
// No calls to dbus until a listener is added
{
const calls = await getCalls();
expect(calls).to.be.an('array').that.has.lengthOf(0);
}
// Add a dummy listener to engage the monitors
dbusMockPowerMonitor.on('dummy-event', () => {});
try {
let retriesRemaining = 3;
// There doesn't seem to be a way to get a notification when a call
// happens, so poll `getCalls` a few times to reduce flake.
let calls: any[] = [];
while (retriesRemaining-- > 0) {
calls = await getCalls();
if (calls.length > 0) break;
await setTimeout(1000);
}
expect(calls).to.be.an('array').that.has.lengthOf(1);
expect(calls[0].slice(1)).to.deep.equal([
'Inhibit', [
[[{ type: 's', child: [] }], ['sleep']],
[[{ type: 's', child: [] }], ['electron']],
[[{ type: 's', child: [] }], ['Application cleanup before suspend']],
[[{ type: 's', child: [] }], ['delay']]
]
]);
} finally {
dbusMockPowerMonitor.removeAllListeners('dummy-event');
}
});
describe('when PrepareForSleep(true) signal is sent by logind', () => {
it('should emit "suspend" event', (done) => {
dbusMockPowerMonitor.once('suspend', () => done());
emitSignal('org.freedesktop.login1.Manager', 'PrepareForSleep',
'b', [['b', true]]);
});
describe('when PrepareForSleep(false) signal is sent by logind', () => {
it('should emit "resume" event', done => {
dbusMockPowerMonitor.once('resume', () => done());
emitSignal('org.freedesktop.login1.Manager', 'PrepareForSleep',
'b', [['b', false]]);
});
it('should have called Inhibit again', async () => {
const calls = await getCalls();
expect(calls).to.be.an('array').that.has.lengthOf(2);
expect(calls[1].slice(1)).to.deep.equal([
'Inhibit', [
[[{ type: 's', child: [] }], ['sleep']],
[[{ type: 's', child: [] }], ['electron']],
[[{ type: 's', child: [] }], ['Application cleanup before suspend']],
[[{ type: 's', child: [] }], ['delay']]
]
]);
});
});
});
describe('when a listener is added to shutdown event', () => {
before(async () => {
const calls = await getCalls();
expect(calls).to.be.an('array').that.has.lengthOf(2);
dbusMockPowerMonitor.once('shutdown', () => { });
});
it('should call Inhibit to delay shutdown', async () => {
const calls = await getCalls();
expect(calls).to.be.an('array').that.has.lengthOf(3);
expect(calls[2].slice(1)).to.deep.equal([
'Inhibit', [
[[{ type: 's', child: [] }], ['shutdown']],
[[{ type: 's', child: [] }], ['electron']],
[[{ type: 's', child: [] }], ['Ensure a clean shutdown']],
[[{ type: 's', child: [] }], ['delay']]
]
]);
});
describe('when PrepareForShutdown(true) signal is sent by logind', () => {
it('should emit "shutdown" event', done => {
dbusMockPowerMonitor.once('shutdown', () => { done(); });
emitSignal('org.freedesktop.login1.Manager', 'PrepareForShutdown',
'b', [['b', true]]);
});
});
});
});
it('is usable before app ready', async () => {
const remoteApp = await startRemoteControlApp(['--boot-eval=globalThis.initialValue=require("electron").powerMonitor.getSystemIdleTime()']);
expect(await remoteApp.remoteEval('globalThis.initialValue')).to.be.a('number');
});
describe('when powerMonitor module is loaded', () => {
let powerMonitor: typeof Electron.powerMonitor;
before(() => {
powerMonitor = require('electron').powerMonitor;
});
describe('powerMonitor.getSystemIdleState', () => {
it('gets current system idle state', () => {
// this function is not mocked out, so we can test the result's
// form and type but not its value.
const idleState = powerMonitor.getSystemIdleState(1);
expect(idleState).to.be.a('string');
const validIdleStates = ['active', 'idle', 'locked', 'unknown'];
expect(validIdleStates).to.include(idleState);
});
it('does not accept non positive integer threshold', () => {
expect(() => {
powerMonitor.getSystemIdleState(-1);
}).to.throw(/must be greater than 0/);
expect(() => {
powerMonitor.getSystemIdleState(NaN);
}).to.throw(/conversion failure/);
expect(() => {
powerMonitor.getSystemIdleState('a' as any);
}).to.throw(/conversion failure/);
});
});
describe('powerMonitor.getSystemIdleTime', () => {
it('returns current system idle time', () => {
const idleTime = powerMonitor.getSystemIdleTime();
expect(idleTime).to.be.at.least(0);
});
});
describe('powerMonitor.getCurrentThermalState', () => {
it('returns a valid state', () => {
expect(powerMonitor.getCurrentThermalState()).to.be.oneOf(['unknown', 'nominal', 'fair', 'serious', 'critical']);
});
});
describe('powerMonitor.onBatteryPower', () => {
it('returns a boolean', () => {
expect(powerMonitor.onBatteryPower).to.be.a('boolean');
expect(powerMonitor.isOnBatteryPower()).to.be.a('boolean');
});
});
});
});