api-power-monitor-spec.ts 7.1 KB

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