chromium/chrome/test/data/extensions/api_test/tts_engine/on_speak_with_audio_stream/test.js

// Copyright 2021 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

chrome.test.runTests([
  function testSendAudioData() {
    // Sends a series of audio buffers, and verifies we get events for them.
    const expectedEvents = [
      {type: 'start', charIndex: 0}, {'type': 'word', 'charIndex': 10},
      {type: 'word', charIndex: 20}, {type: 'end', charIndex: 39}
    ];

    chrome.ttsEngine.onStop.addListener(() => {});

    let errors = 0;
    const listener = (text, options, audioStreamOptions, sendAudioCallback) => {
      // Check the additional parameters.
      chrome.test.assertTrue(audioStreamOptions.sampleRate >= 1000);
      chrome.test.assertTrue(audioStreamOptions.bufferSize >= 100);

      const buffer = new Float32Array(audioStreamOptions.bufferSize);

      // Start event.
      sendAudioCallback({audioBuffer: buffer});

      // A word event.
      sendAudioCallback({audioBuffer: buffer, charIndex: 10});

      // A word event.
      sendAudioCallback({audioBuffer: buffer, charIndex: 20});

      // Invalid audio buffer length.
      try {
        sendAudioCallback({audioBuffer: [0, 0]});
      } catch (e) {
        errors++;
      }

      // End event.
      sendAudioCallback(
          {audioBuffer: buffer, charIndex: 30, isLastBuffer: true});
    };
    chrome.ttsEngine.onSpeakWithAudioStream.addListener(listener);

    function onEvent(event) {
      const expected = expectedEvents.shift();
      chrome.test.assertEq(JSON.stringify(expected), JSON.stringify(event));
      if (event.type == 'end') {
        chrome.test.assertEq(0, expectedEvents.length);
        chrome.test.assertEq(1, errors);
        chrome.ttsEngine.onSpeakWithAudioStream.removeListener(listener);
        chrome.test.succeed();
      }
    }

    chrome.tts.speak(
        'this is a test of using audio playback.',
        {voiceName: 'Zach', onEvent: onEvent});
  },

  async function testErrorEvents() {
    // Errors, when sent, invalidate the utterance (id) so all other callbacks
    // sent by the engine are not sent.
    let toSend;
    let bufferSize;
    const listener =
        (text, options, audioStreamOptions, sendAudioCallback,
         sendErrorCallback) => {
          bufferSize = audioStreamOptions.bufferSize;
          if (toSend.errorMessage) {
            sendErrorCallback(toSend.errorMessage);
          } else {
            sendAudioCallback(toSend);
          }
        };
    chrome.ttsEngine.onSpeakWithAudioStream.addListener(listener);

    // Note that 'start' gets sent by the controller regardless of what is sent
    // here, so 'start' can be sent in any order relative to 'error'.

    // Sent an error.
    toSend = {errorMessage: 'Error encountered'};
    await new Promise(r => {chrome.tts.speak('asdf', {
                        voiceName: 'Zach',
                        onEvent: (event) => {
                          if (event.errorMessage === 'Error encountered') {
                            chrome.test.assertEq('error', event.type);
                            r();
                          }
                        }
                      })});

    // Sanity check another utterance with no errors gets cycled back correctly.
    // The check asserts the event type directly without waitiing to catch if
    // 'error' gets sent and causes this to flake.
    toSend = {type: 'start', audioBuffer: new Float32Array(bufferSize)};
    await new Promise(r => {chrome.tts.speak('asdf', {
                        voiceName: 'Zach',
                        onEvent: (event) => {
                          chrome.test.assertEq('start', event.type);
                          r();
                        }
                      })});

    chrome.test.succeed();
  }
]);