| <!doctype html> |
| <title>Test AudioContext state updates with suspend() shortly after |
| construction</title> |
| <script src=/resources/testharness.js></script> |
| <script src=/resources/testharnessreport.js></script> |
| <script> |
| // A separate async_test is used for tracking state change counts so that it |
| // can report excess changes after the promise_test for the iteration has |
| // completed. |
| const changeCountingTest = async_test('State change counting'); |
| |
| const doTest = async (testCount) => { |
| const ctx = new AudioContext(); |
| // Explicitly resume to get a promise to indicate whether the context |
| // successfully started running. |
| const resume = ctx.resume(); |
| const suspend = ctx.suspend(); |
| let stateChangesDone = new Promise((resolve) => { |
| ctx.onstatechange = () => { |
| ++ctx.stateChangeCount; |
| changeCountingTest.step(() => { |
| assert_less_than_equal(ctx.stateChangeCount, |
| ctx.expectedStateChangeCount, |
| `ctx ${testCount} state change count.`); |
| assert_equals(ctx.state, ctx.expectedState, `ctx ${testCount} state`); |
| }); |
| if (ctx.stateChangeCount == ctx.totalStateChangeCount) { |
| resolve(); |
| } |
| }; |
| }); |
| ctx.stateChangeCount = 0; |
| ctx.expectedStateChangeCount = 1; |
| ctx.expectedState = 'running'; |
| ctx.totalStateChangeCount = 2; |
| let resumeState = 'pending'; |
| resume.then(() => { |
| resumeState = 'fulfilled'; |
| assert_equals(ctx.state, 'running', 'state on resume fulfilled.'); |
| }).catch(() => { |
| // The resume() promise may be rejected if "Attempt to acquire system |
| // resources" fails. The spec does not discuss the possibility of a |
| // subsequent suspend causing such a failure, but accept this as a |
| // reasonable behavior. |
| resumeState = 'rejected'; |
| assert_equals(ctx.state, 'suspended', 'state on resume rejected.'); |
| assert_equals(ctx.stateChangeCount, 0); |
| ctx.expectedStateChangeCount = 0; |
| stateChangesDone = Promise.resolve(); |
| }); |
| suspend.then(() => { |
| assert_not_equals(resumeState, 'pending', |
| 'resume promise should settle before suspend promise.') |
| if (resumeState == 'fulfilled') { |
| ++ctx.expectedStateChangeCount; |
| } |
| ctx.expectedState = 'suspended'; |
| assert_equals(ctx.state, 'suspended', 'state on suspend fulfilled.'); |
| }); |
| await resume; |
| await suspend; |
| await stateChangesDone; |
| }; |
| |
| // Repeat the test because Gecko uses different code when there is more than |
| // one AudioContext. The third run provides time to check that no further |
| // state changes from the second run are pending. |
| for (const testCount of [1, 2, 3]) { |
| promise_test(() => { return doTest(testCount); }, `Iteration ${testCount}`); |
| } |
| promise_test(async () => changeCountingTest.done(), 'Stop waiting'); |
| </script> |