| <!DOCTYPE html> |
| <html> |
| <head> |
| <title> |
| Test decodeAudioData promises |
| </title> |
| <script src="../../resources/testharness.js"></script> |
| <script src="../../resources/testharnessreport.js"></script> |
| <script src="../resources/audit.js"></script> |
| </head> |
| <body> |
| <script id="layout-test-code"> |
| // The functionality of decodeAudioData() is orthogonal to the type and |
| // the state of AudioContext. So we use the online context here and any |
| // resampling of the file is okay for this test. |
| let context = new AudioContext(); |
| |
| // Test file URLs. |
| let validAudioFileUrl = '../resources/media/24bit-44khz.wav'; |
| let invalidAudioFileUrl = '../resources/media/invalid-audio-file.txt'; |
| |
| // Global storage for array buffers from XHR. |
| let validArrayBuffer; |
| let invalidArrayBuffer; |
| |
| // Decoded data from validAudioFile. |
| let referenceDecodedAudioBuffer; |
| |
| let audit = Audit.createTaskRunner(); |
| |
| // Preload ArrayBuffer and the reference AudioBuffer from URLs. |
| audit.define('preload-arraybuffer', (task, should) => { |
| Promise |
| .all([ |
| should( |
| Audit.loadFileFromUrl(validAudioFileUrl), |
| 'Loading valid audio file') |
| .beResolved(), |
| should( |
| Audit.loadFileFromUrl(invalidAudioFileUrl), |
| 'loading invalid audio file') |
| .beResolved() |
| ]) |
| .then((arrayBuffers) => { |
| validArrayBuffer = arrayBuffers[0]; |
| invalidArrayBuffer = arrayBuffers[1]; |
| }) |
| .then(() => task.done()); |
| }); |
| |
| // Decode a valid encoded file and verify that the promise succeeds |
| // correctly. |
| audit.define('decode-valid-file', (task, should) => { |
| // Note that the order of completion for each promise is undefined and |
| // we do not care about it in this test. |
| Promise |
| .all([ |
| // Do not use the original arrayBuffers for decoding; decode a |
| // copy because decodeAudioData will detach the buffers. |
| should( |
| context.decodeAudioData(validArrayBuffer.slice(0)), |
| 'Decoding a valid audio file') |
| .beResolved() |
| .then(buffer => referenceDecodedAudioBuffer = buffer), |
| should( |
| context.decodeAudioData(invalidArrayBuffer.slice(0)), |
| 'Decoding an invalid audio file') |
| .beRejectedWith('EncodingError'), |
| should(context.decodeAudioData(null), 'Decoding null AudioBuffer') |
| .beRejected() |
| ]) |
| .then(() => task.done()); |
| }); |
| |
| // Decode a valid file and verify that the promise is fulfilled and the |
| // successCallback is invoked and both have identical decoded audio |
| // buffers. |
| audit.define('promise-and-success-callback', (task, should) => { |
| let bufferByCallback; |
| let bufferByPromise; |
| |
| // Use one callback for success and error. |callbackArg| is a parameter |
| // for callback functions; it is a decoded audio buffer for success case |
| // and an error object for failure case. |
| let successOrErrorCallback = (callbackArg) => { |
| should( |
| callbackArg instanceof AudioBuffer, |
| 'Decoding valid file by callback function') |
| .message( |
| 'successCallback invoked correctly', |
| 'errorCallback incorrectly invoked with ' + callbackArg); |
| bufferByCallback = callbackArg; |
| }; |
| |
| // Step 1: Decode a file with callback functions. |
| let step1 = context.decodeAudioData( |
| validArrayBuffer.slice(), successOrErrorCallback, |
| successOrErrorCallback); |
| |
| // Step 2: Then decode a file with promise pattern. |
| let step2 = should(step1, 'Decoding a file via promise') |
| .beResolved() |
| .then((audioBuffer) => { |
| bufferByPromise = audioBuffer; |
| }); |
| |
| // Step 3: compare two buffers from Step 1 and Step 2. |
| step2.then(() => { |
| should( |
| bufferByCallback === bufferByPromise, |
| 'Two buffers decoded by callback function and promise') |
| .message('are identical', 'are different'); |
| task.done(); |
| }); |
| }); |
| |
| // Decode an invalid file and verify that the promise is rejected and the |
| // errorCallback is invoked. |
| audit.define('promise-and-error-callback', (task, should) => { |
| let successOrErrorCallback = (callbackArg) => { |
| should( |
| callbackArg instanceof Error, |
| 'Decoding invalid file with promise and callback:') |
| .message( |
| 'errorCallback invoked correctly with ' + callbackArg, |
| 'successCallback should not have invoked'); |
| }; |
| |
| let decodeAudioDataPromise = context.decodeAudioData( |
| invalidArrayBuffer.slice(), successOrErrorCallback, |
| successOrErrorCallback); |
| |
| should(decodeAudioDataPromise, 'decodeAudioData promise') |
| .beRejected('EncodingError') |
| .then(() => task.done()); |
| }); |
| |
| // decodeAudioData() should be functional even after the associated |
| // context is closed. |
| audit.define('decoding-on-closed-context', (task, should) => { |
| // Use one handler for resolve and reject. |promiseArg| is a parameter |
| // for handlers; it is a decoded audio buffer for success case and an |
| // error object for failure case. |
| let resolveOrReject = (promiseArg) => { |
| let didDecode = promiseArg instanceof AudioBuffer; |
| |
| if (didDecode) { |
| // Compare two decoded AudioBuffers. |
| let actual = promiseArg; |
| let expected = referenceDecodedAudioBuffer; |
| should(actual.length, 'Decoded buffer length (frames)') |
| .beEqualTo(expected.length); |
| should(actual.duration, 'Decoded buffer duration (sec)') |
| .beEqualTo(expected.duration); |
| should(actual.sampleRate, 'Decoded buffer sample rate (Hz)') |
| .beEqualTo(expected.sampleRate); |
| should( |
| actual.numberOfChannels, 'Number of channels in decoded buffer') |
| .beEqualTo(expected.numberOfChannels); |
| for (let c = 0; c < expected.numberOfChannels; ++c) { |
| let actualChannelData = actual.getChannelData(c); |
| let expectedChannelData = expected.getChannelData(c); |
| should(actualChannelData, 'Decoded buffer channel #' + c) |
| .beEqualToArray( |
| expectedChannelData, 'the expected channel #' + c); |
| } |
| should(task.state, 'The buffer') |
| .message( |
| 'correctly decoded after the context has been closed', |
| 'decoding succeeded but the data is incorrect'); |
| } |
| |
| should( |
| didDecode, 'Decoding ArrayBuffer after context has been closed') |
| .message('completed successfully', 'failed : ' + promiseArg); |
| }; |
| |
| let onlineContext = new AudioContext(); |
| onlineContext.close() |
| .then(() => { |
| return context.decodeAudioData(validArrayBuffer); |
| }) |
| .then(resolveOrReject, resolveOrReject) |
| .then(() => { |
| task.done(); |
| }); |
| }); |
| |
| audit.run(); |
| </script> |
| </body> |
| </html> |