1package com.android.car.messenger.tts; 2 3import android.speech.tts.TextToSpeech; 4 5import com.android.car.messenger.TestConfig; 6 7import org.junit.Assert; 8import org.junit.Before; 9import org.junit.Test; 10import org.junit.runner.RunWith; 11import org.robolectric.Robolectric; 12import org.robolectric.RobolectricTestRunner; 13import org.robolectric.RuntimeEnvironment; 14import org.robolectric.annotation.Config; 15import org.robolectric.util.Scheduler; 16 17import java.util.Arrays; 18import java.util.Collections; 19 20@RunWith(RobolectricTestRunner.class) 21@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION) 22public class TTSHelperTest { 23 private static final long SHUTDOWN_DELAY_MILLIS = 10; 24 25 private FakeTTSEngine mFakeTTS; 26 private TTSHelper mTTSHelper; 27 28 @Before 29 public void setup() { 30 mFakeTTS = new FakeTTSEngine(); 31 mTTSHelper = new TTSHelper(RuntimeEnvironment.application, mFakeTTS, SHUTDOWN_DELAY_MILLIS); 32 } 33 34 @Test 35 public void testSpeakBeforeInit() { 36 // Should get queued since engine not initialized. 37 final RecordingTTSListener recorder1 = new RecordingTTSListener(); 38 mTTSHelper.requestPlay(Collections.singletonList("hello"), recorder1); 39 // Will squash previous request. 40 final RecordingTTSListener recorder2 = new RecordingTTSListener(); 41 mTTSHelper.requestPlay(Collections.singletonList("world"), recorder2); 42 Assert.assertFalse(recorder1.wasStarted()); 43 Assert.assertTrue(recorder1.wasStoppedWithSuccess()); 44 45 // Finish initialization. 46 mFakeTTS.mOnInitListener.onInit(TextToSpeech.SUCCESS); 47 48 // The queued request should have been passed for playout. 49 FakeTTSEngine.Request lastRequest = mFakeTTS.mRequests.getLast(); 50 Assert.assertEquals("world", lastRequest.mText); 51 Assert.assertEquals(TextToSpeech.QUEUE_ADD, lastRequest.mQueueMode); 52 53 mFakeTTS.startRequest(lastRequest.mUtteranceId); 54 Assert.assertTrue(recorder2.wasStarted()); 55 mFakeTTS.finishRequest(lastRequest.mUtteranceId); 56 Assert.assertTrue(recorder2.wasStoppedWithSuccess()); 57 } 58 59 @Test 60 public void testSpeakInterruptedByNext() { 61 // First request made and starts playing. 62 final RecordingTTSListener recorder1 = new RecordingTTSListener(); 63 mTTSHelper.requestPlay(Collections.singletonList("hello"), recorder1); 64 // Finish initialization. 65 mFakeTTS.mOnInitListener.onInit(TextToSpeech.SUCCESS); 66 final FakeTTSEngine.Request request1 = mFakeTTS.mRequests.getLast(); 67 mFakeTTS.startRequest(request1.mUtteranceId); 68 Assert.assertTrue(recorder1.wasStarted()); 69 70 // Second request made. It will flush first one. 71 final RecordingTTSListener recorder2 = new RecordingTTSListener(); 72 mTTSHelper.requestPlay(Collections.singletonList("world"), recorder2); 73 final FakeTTSEngine.Request request2 = mFakeTTS.mRequests.getLast(); 74 mFakeTTS.interruptRequest(request1.mUtteranceId, true /* interrupted */); 75 Assert.assertTrue(recorder1.wasStoppedWithSuccess()); 76 mFakeTTS.startRequest(request2.mUtteranceId); 77 Assert.assertTrue(recorder2.wasStarted()); 78 mFakeTTS.finishRequest(request2.mUtteranceId); 79 Assert.assertTrue(recorder2.wasStoppedWithSuccess()); 80 } 81 82 @Test 83 public void testKeepAliveAndAutoShutdown() { 84 // Request made and starts playing. 85 final RecordingTTSListener recorder1 = new RecordingTTSListener(); 86 mTTSHelper.requestPlay(Collections.singletonList("hello"), recorder1); 87 // Finish initialization. 88 mFakeTTS.mOnInitListener.onInit(TextToSpeech.SUCCESS); 89 final FakeTTSEngine.Request request1 = mFakeTTS.mRequests.getLast(); 90 mFakeTTS.startRequest(request1.mUtteranceId); 91 Assert.assertTrue(recorder1.wasStarted()); 92 mFakeTTS.finishRequest(request1.mUtteranceId); 93 Assert.assertTrue(recorder1.wasStoppedWithSuccess()); 94 95 Scheduler scheduler = Robolectric.getForegroundThreadScheduler(); 96 scheduler.advanceBy(SHUTDOWN_DELAY_MILLIS / 2); 97 Assert.assertTrue(mFakeTTS.isInitialized()); 98 99 // Queue another request, forces keep-alive. 100 final RecordingTTSListener recorder2 = new RecordingTTSListener(); 101 mTTSHelper.requestPlay(Collections.singletonList("world"), recorder2); 102 final FakeTTSEngine.Request request2 = mFakeTTS.mRequests.getLast(); 103 mFakeTTS.failRequest(request2.mUtteranceId, -2 /* errorCode */); 104 Assert.assertTrue(recorder2.wasStoppedWithError()); 105 Assert.assertTrue(mFakeTTS.isInitialized()); 106 107 scheduler.advanceBy(SHUTDOWN_DELAY_MILLIS); 108 Assert.assertFalse(mFakeTTS.isInitialized()); 109 } 110 111 @Test 112 public void testBatchPlayout() { 113 // Request made and starts playing. 114 final RecordingTTSListener recorder1 = new RecordingTTSListener(); 115 mTTSHelper.requestPlay(Arrays.asList("hello", "world"), recorder1); 116 // Finish initialization. 117 mFakeTTS.mOnInitListener.onInit(TextToSpeech.SUCCESS); 118 // Two requests should be queued from batch. 119 Assert.assertEquals(2, mFakeTTS.mRequests.size()); 120 final FakeTTSEngine.Request request1 = mFakeTTS.mRequests.getFirst(); 121 final FakeTTSEngine.Request request2 = mFakeTTS.mRequests.getLast(); 122 mFakeTTS.startRequest(request1.mUtteranceId); 123 Assert.assertTrue(recorder1.wasStarted()); 124 mFakeTTS.finishRequest(request1.mUtteranceId); 125 Assert.assertFalse(recorder1.wasStoppedWithSuccess()); 126 mFakeTTS.startRequest(request2.mUtteranceId); 127 Assert.assertTrue(recorder1.wasStarted()); 128 mFakeTTS.finishRequest(request2.mUtteranceId); 129 Assert.assertTrue(recorder1.wasStoppedWithSuccess()); 130 } 131 132 private static class RecordingTTSListener implements TTSHelper.Listener { 133 private boolean mStarted = false; 134 private boolean mStopped = false; 135 private boolean mError = false; 136 137 boolean wasStarted() { 138 return mStarted; 139 } 140 141 boolean wasStoppedWithSuccess() { 142 return mStopped && !mError; 143 } 144 145 boolean wasStoppedWithError() { 146 return mStopped && mError; 147 } 148 149 @Override 150 public void onTTSStarted() { 151 mStarted = true; 152 } 153 154 @Override 155 public void onTTSStopped(boolean error) { 156 mStopped = true; 157 mError = error; 158 } 159 } 160}