Commit 34c7f54d authored by hongchan@chromium.org's avatar hongchan@chromium.org

This patch includes a fix and refactoring on several layout tests for promise...

This patch includes a fix and refactoring on several layout tests for promise from AudioContext.suspend() and .resume().

|Audit| task runner handles the sequential execution of asynchrous test tasks.

BUG=460504

Review URL: https://codereview.chromium.org/945023002

git-svn-id: svn://svn.chromium.org/blink/trunk@190694 bbb929c8-8fbe-4397-9dbb-9b2b20218538
parent f482d402
......@@ -28,13 +28,13 @@ PASS context.createWaveShaper() threw exception InvalidStateError: Failed to exe
PASS osc.connect(gain) threw exception InvalidStateError: Failed to execute 'connect' on 'AudioNode': Cannot connect after the context has been closed..
PASS gain.disconnect() did not throw exception.
PASS Attempt to resume a closed context was correctly rejected
PASS Closing context again correctly rejected promise.
PASS offline = new OfflineAudioContext(1, 1000, 48000) did not throw exception.
PASS offline.state is "suspended"
PASS Closing offline context correctly rejected
PASS Closing context again correctly rejected promise.
PASS Closing offline context again correctly rejected
PASS offline.startRendering() did not throw exception.
PASS offline.state is "closed"
PASS event.target.state is "closed"
PASS successfullyParsed is true
TEST COMPLETE
......
<!doctype html>
<html>
<head>
<title>Test AudioContext.close()</title>
<script src="resources/compatibility.js"></script>
<script src="resources/audio-testing.js"></script>
<script src="../resources/js-test.js"></script>
</head>
<body>
<script>
description("Basic functionality test of closing an AudioContext");
var context;
var offline;
var osc;
var gain;
var promise1;
var promise2;
var offlinePromise;
var wave = new Float32Array(1);
<head>
<title>Test AudioContext.close()</title>
<script src="../resources/js-test.js"></script>
<script src="resources/compatibility.js"></script>
<script src="resources/audio-testing.js"></script>
</head>
<body>
<script>
description("Basic functionality test of closing an AudioContext");
window.jsTestIsAsync = true;
var context;
var offline;
var osc;
var gain;
var promise1;
var promise2;
var offlinePromise;
var wave = new Float32Array(1);
var audit = Audit.createTaskRunner();
// Task: test online context (1).
audit.defineTask('test-online-context-1', function (done) {
// Create a context and verify that the various states are correct and
// that close() exists.
shouldNotThrow("context = new AudioContext()");
shouldBeEqualToString("context.state", "running");
// Create gain and oscillator for testing later.
shouldNotThrow("osc = context.createOscillator()");
shouldNotThrow("gain = context.createGain()");
shouldNotThrow("gain.connect(context.destination)");
// Close the context. When the promise is resolved, continue the next
// test task.
context.close().then(
function () {
testPassed("context.close() was correctly resolved");
},
function () {
testFailed("context.close() was erroneously rejected");
}
).then(done);
});
// Task: test online context (2).
audit.defineTask('test-online-context-2', function (done) {
// Context is closed, so verify that we cannot create any more nodes,
// nor connect any.
shouldThrow("context.createAnalyser()");
shouldThrow("context.createBiquadFilter()");
// createBuffer is an exception because it's not really tied in any way
// to an audio context. And it's useful to be able to create a buffer
// inside the oncomplete event of an offline context to use for testing
// purposes.
shouldNotThrow("context.createBuffer(1, 1, 48000)");
shouldThrow("context.createBufferSource()");
shouldThrow("context.createChannelMerger()");
shouldThrow("context.createChannelSplitter()");
shouldThrow("context.createConvolver()");
shouldThrow("context.createDelay()");
shouldThrow("context.createDynamicsCompressor()");
shouldThrow("context.createGain()");
shouldThrow("context.createOscillator()");
shouldThrow("context.createPanner()");
shouldThrow("context.createPeriodicWave(wave, wave)");
shouldThrow("context.createScriptProcessor()");
shouldThrow("context.createStereoPanner()");
shouldThrow("context.createWaveShaper()");
shouldThrow("osc.connect(gain)");
shouldNotThrow("gain.disconnect()");
// Can't resume a context that is closed (released).
context.resume().then(
function () {
testFailed("Attempt to resume a closed context erroneously succeeded");
},
function () {
testPassed("Attempt to resume a closed context was correctly rejected");
}
).then(done);
});
// Task: test online context (3).
audit.defineTask('test-online-context-3', function (done) {
// Try closing the context again. The promise should be rejected.
context.close().then(
function () {
testFailed("Closing context again erroneously resolved successfully.");
},
function () {
testPassed("Closing context again correctly rejected promise.");
// Finally, run GC. The context should be gone, but this seems
// difficult to verify.
if (window.hasOwnProperty('GCController')) {
asyncGC(function () {
shouldBeNull("context.destination");
});
} else {
shouldBeNull("context.destination");
testFailed("asyncGC test not run");
}
}
).then(done);
});
// Task: test offline context (1).
audit.defineTask('test-offline-context-1', function (done) {
// For an offline context, just check that if we try to close the context,
// nothing happens except that the promise returned by close() is rejected.
shouldNotThrow("offline = new OfflineAudioContext(1, 1000, 48000)");
shouldBeEqualToString("offline.state", "suspended");
offline.close().then(
function () {
testFailed("Closing offline context erroneously resolved");
},
function () {
testPassed("Closing offline context correctly rejected");
}
).then(done);
});
function runTest() {
window.jsTestIsAsync = true;
runOnlineTest();
}
// Task: test offline context (2).
audit.defineTask('test-offline-context-2', function (done) {
function runOnlineTest() {
// Create a context and verify that the various states are correct and that close() exists.
shouldNotThrow("context = new AudioContext()");
shouldBeEqualToString("context.state", "running");
// Create gain and oscillator for testing later.
shouldNotThrow("osc = context.createOscillator()");
shouldNotThrow("gain = context.createGain()");
shouldNotThrow("gain.connect(context.destination)");
// Close the context. When the promise is resolved, continue online testing, and then run
// the offline tests.
context.close()
.then(function () {
testPassed("context.close() was correctly resolved");
continueOnlineTest();
},
function () {
testFailed("context.close() was erroneously rejected");
})
.then(runOfflineTest);
}
function continueOnlineTest() {
// Context is released, so verify that we cannot create any more nodes, nor connect any.
shouldThrow("context.createAnalyser()");
shouldThrow("context.createBiquadFilter()");
// createBuffer is an exception because it's not really tied in any way to an audio
// context. And it's useful to be able to create a buffer inside the oncomplete event of an
// offline context to use for testing purposes.
shouldNotThrow("context.createBuffer(1, 1, 48000)");
shouldThrow("context.createBufferSource()");
shouldThrow("context.createChannelMerger()");
shouldThrow("context.createChannelSplitter()");
shouldThrow("context.createConvolver()");
shouldThrow("context.createDelay()");
shouldThrow("context.createDynamicsCompressor()");
shouldThrow("context.createGain()");
shouldThrow("context.createOscillator()");
shouldThrow("context.createPanner()");
shouldThrow("context.createPeriodicWave(wave, wave)");
shouldThrow("context.createScriptProcessor()");
shouldThrow("context.createStereoPanner()");
shouldThrow("context.createWaveShaper()");
shouldThrow("osc.connect(gain)");
shouldNotThrow("gain.disconnect()");
// Can't resume a context that is closed (released).
context.resume()
.then(function () {
testFailed("Attempt to resume a closed context erroneously succeeded");
},
function () {
testPassed("Attempt to resume a closed context was correctly rejected");
})
.then(continueOnlineTest2);
}
function continueOnlineTest2() {
// Try closing the context again. The promise should be rejected.
context.close()
.then(function () {
testFailed("Closing context again erroneously resolved successfully.");
},
function () {
testPassed("Closing context again correctly rejected promise.");
// Finally, run GC. The context should be gone, but this seems difficult to verify.
if (window.hasOwnProperty('GCController')) {
asyncGC(function () {
shouldBeNull("context.destination");
});
} else {
shouldBeNull("context.destination");
testFailed("asyncGC test not run");
}
});
}
function runOfflineTest () {
// For an offline context, just check that if we try to close the context, nothing happens
// except that the promise returned by close() is rejected.
shouldNotThrow("offline = new OfflineAudioContext(1, 1000, 48000)");
shouldBeEqualToString("offline.state", "suspended");
offline.close()
.then(function () {
testFailed("Closing offline context erroneously resolved");
},
function () {
testPassed("Closing offline context correctly rejected");
})
.then(continueOfflineTest);
}
function continueOfflineTest () {
// Try closing again
offline.close()
.then(function () {
testFailed("Closing offline context again erroneously resolved");
}, function () {
testPassed("Closing offline context again correctly rejected");
})
.then(function () {
// Render the context, and check for a valid state
offline.oncomplete = function (event) {
shouldBeEqualToString("offline.state", "closed");
finishJSTest();
};
shouldNotThrow("offline.startRendering()");
});
}
runTest();
successfullyParsed = true;
</script>
</body>
// Try closing again
offline.close().then(
function () {
testFailed("Closing offline context again erroneously resolved");
},
function () {
testPassed("Closing offline context again correctly rejected");
}
).then(
function () {
// Render the context, and check for a valid state
offline.oncomplete = function (event) {
shouldBeEqualToString("event.target.state", "closed");
done();
};
shouldNotThrow("offline.startRendering()");
}
);
});
audit.defineTask('finish-test', function (done) {
done();
finishJSTest();
});
audit.runTasks(
'test-online-context-1',
'test-online-context-2',
'test-online-context-3',
'test-offline-context-1',
'test-offline-context-2',
'finish-test'
);
successfullyParsed = true;
</script>
</body>
</html>
......@@ -7,7 +7,7 @@ PASS context.state is "suspended"
PASS p1 = context.suspend() did not throw exception.
PASS [object Object] is an instance of function Promise() { [native code] }
PASS context.suspend() was correctly rejected for an offline context
PASS p1 = context.resume() did not throw exception.
PASS p2 = context.resume() did not throw exception.
PASS [object Object] is an instance of function Promise() { [native code] }
PASS context.state is "suspended"
PASS context.resume() was correctly rejected for an offline context
......
<!doctype html>
<html>
<head>
<title>Test audiocontext suspend/resume</title>
<script src="resources/compatibility.js"></script>
<script src="resources/audio-testing.js"></script>
<script src="../resources/js-test.js"></script>
</head>
<body>
<script>
description("Test suspend/resume for an (offline) AudioContext");
var context;
var osc;
var p1;
var p2;
var sampleRate = 44100;
var durationInSeconds = 1;
// Convenience function that returns a function that calls the |passFailMethod| with the given
// |message|. The |passFailMethod| should be either |testPassed| or |testFailed|.
function handlePromise(passFailMethod, message) {
return function () {
passFailMethod(message);
};
}
function checkResult (event) {
// We don't care about the actual result of the offline rendering.
<head>
<title>Test AudioContext.suspend() and AudioContext.resume()</title>
<script src="../resources/js-test.js"></script>
<script src="resources/compatibility.js"></script>
<script src="resources/audio-testing.js"></script>
</head>
<body>
<script>
description("Test suspend/resume for an (offline) AudioContext");
window.jsTestIsAsync = true;
var context;
var osc;
var p1;
var p2;
var sampleRate = 44100;
var durationInSeconds = 1;
var audit = Audit.createTaskRunner();
// Convenience function that returns a function that calls the |passFailFunc|
// with the given |message|. The |passFailFunc| should be either |testPassed|
// or |testFailed|.
function handlePromise(passFailFunc, message) {
return function () {
passFailFunc(message);
};
}
// Task: test suspend().
audit.defineTask('test-suspend', function (done) {
// Test suspend/resume. Ideally this test is best with a online
// AudioContext, but content shell doesn't really have a working online
// AudioContext. Hence, use an OfflineAudioContext. Not all possible
// scenarios can be easily checked with an offline context instead of an
// online context.
// Create an audio context with an oscillator.
context = new OfflineAudioContext(1, durationInSeconds * sampleRate, sampleRate);
osc = context.createOscillator();
osc.connect(context.destination);
// Verify the state.
shouldBeEqualToString("context.state", "suspended");
// Multiple calls to suspend() should not be a problem. But we can't test
// that on an offline context. Thus, check that suspend() on an
// OfflineAudioContext rejects the promise.
shouldNotThrow("p1 = context.suspend()");
shouldBeType(p1, Promise);
p1.then(
handlePromise(testFailed, "context.suspend() should have been rejected for an offline context"),
handlePromise(testPassed, "context.suspend() was correctly rejected for an offline context")
).then(done);
});
// Task: test resume().
audit.defineTask('test-resume', function (done) {
// Multiple calls to resume should not be a problem. But we can't test
// that on an offline context. Thus, check that resume() on an
// OfflineAudioContext rejects the promise.
shouldNotThrow("p2 = context.resume()");
shouldBeType(p2, Promise);
// Resume doesn't actually resume an offline context
shouldBeEqualToString("context.state", "suspended");
p2.then(
handlePromise(testFailed, "context.resume() should have been rejected for an offline context"),
handlePromise(testPassed, "context.resume() was correctly rejected for an offline context")
).then(done);
});
// Task: test the state after context closed.
audit.defineTask('test-after-close', function (done) {
// Render the offline context.
osc.start();
// Test suspend/resume in tested promise pattern. We don't care about the
// actual result of the offline rendering.
context.startRendering().then(function () {
shouldBeEqualToString("context.state", "closed");
// suspend() should be rejected on a closed context.
context.suspend()
.then(handlePromise(
testFailed,
"context.suspend() on a closed context not rejected"),
handlePromise(
testPassed,
"context.suspend() on a closed context rejected as expected"))
.then(function () {
// resume() should be rejected on closed context.
return context.resume();
})
.then(handlePromise(
testFailed,
"context.resume() on a closed context not rejected"),
handlePromise(
testPassed,
"context.resume() on a closed context rejected as expected"))
.then(finishJSTest);
}
function runOfflineContextTest() {
// Render the offline context.
osc.start();
context.oncomplete = checkResult;
context.startRendering();
}
function runResumeTest () {
// Multiple calls to resume should not be a problem. But we can't test that on an offline
// context. Thus, check that resume() on an OfflineAudioContext rejects the promise.
shouldNotThrow("p1 = context.resume()");
shouldBeType(p1, Promise);
// Resume doesn't actually resume an offline context
shouldBeEqualToString("context.state", "suspended");
p1.then(handlePromise(
testFailed,
"context.resume() should have been rejected for an offline context"),
handlePromise(
testPassed,
"context.resume() was correctly rejected for an offline context"))
.then(runOfflineContextTest);
}
function runTest() {
window.jsTestIsAsync = true;
// Test suspend/resume. Ideally this test is best with a online AudioContext, but content
// shell doesn't really have a working online AudioContext. Hence, use an
// OfflineAudioContext. Not all possible scenarios can be easily checked with an offline
// context instead of an online context.
// Create an audio context with an oscillator.
context = new OfflineAudioContext(1, durationInSeconds * sampleRate, sampleRate);
osc = context.createOscillator();
osc.connect(context.destination);
// Verify the state.
shouldBeEqualToString("context.state", "suspended");
// Multiple calls to suspend() should not be a problem. But we can't test that on an offline
// context. Thus, check that suspend() on an OfflineAudioContext rejects the promise.
shouldNotThrow("p1 = context.suspend()");
shouldBeType(p1, Promise);
p1.then(handlePromise(
testFailed,
"context.suspend() should have been rejected for an offline context"),
handlePromise(
testPassed,
"context.suspend() was correctly rejected for an offline context"))
.then(runResumeTest);
}
runTest();
successfullyParsed = true;
</script>
</body>
context.suspend().then(
handlePromise(testFailed, "context.suspend() on a closed context not rejected"),
handlePromise(testPassed, "context.suspend() on a closed context rejected as expected")
).then(function () {
// resume() should be rejected on closed context.
context.resume().then(
handlePromise(testFailed, "context.resume() on a closed context not rejected"),
handlePromise(testPassed, "context.resume() on a closed context rejected as expected")
).then(done);
});
});
});
audit.defineTask('finish-test', function (done) {
done();
finishJSTest();
});
audit.runTasks(
'test-suspend',
'test-resume',
'test-after-close',
'finish-test'
);
successfullyParsed = true;
</script>
</body>
</html>
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment