1 /* 2 * Copyright (C) 2011 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.speech.tts; 18 19 import android.speech.tts.SynthesisCallback; 20 import android.speech.tts.SynthesisRequest; 21 import android.speech.tts.TextToSpeech; 22 import android.test.InstrumentationTestCase; 23 24 import com.android.speech.tts.MockableTextToSpeechService.IDelegate; 25 import org.mockito.ArgumentCaptor; 26 import org.mockito.Mockito; 27 import org.mockito.internal.stubbing.StubberImpl; 28 import org.mockito.invocation.InvocationOnMock; 29 import org.mockito.stubbing.Answer; 30 import org.mockito.stubbing.Stubber; 31 import org.mockito.quality.Strictness; 32 import junit.framework.Assert; 33 34 import java.util.Locale; 35 import java.util.concurrent.Callable; 36 import java.util.concurrent.CountDownLatch; 37 import java.util.concurrent.TimeUnit; 38 39 public class TextToSpeechTests extends InstrumentationTestCase { 40 private static final String MOCK_ENGINE = "com.android.speech.tts"; 41 private static final String MOCK_PACKAGE = "com.android.speech.tts.__testpackage__"; 42 43 private TextToSpeech mTts; 44 45 @Override setUp()46 public void setUp() throws Exception { 47 IDelegate passThrough = Mockito.mock(IDelegate.class); 48 MockableTextToSpeechService.setMocker(passThrough); 49 50 // For the default voice selection 51 Mockito.doReturn(TextToSpeech.LANG_COUNTRY_AVAILABLE).when(passThrough) 52 .onIsLanguageAvailable( 53 Mockito.anyString(), Mockito.anyString(), Mockito.anyString()); 54 Mockito.doReturn(TextToSpeech.LANG_COUNTRY_AVAILABLE).when(passThrough) 55 .onLoadLanguage( 56 Mockito.anyString(), Mockito.anyString(), Mockito.anyString()); 57 58 blockingInitAndVerify(MOCK_ENGINE, TextToSpeech.SUCCESS); 59 assertEquals(MOCK_ENGINE, mTts.getCurrentEngine()); 60 } 61 62 @Override tearDown()63 public void tearDown() { 64 if (mTts != null) { 65 mTts.shutdown(); 66 } 67 } 68 testEngineInitialized()69 public void testEngineInitialized() throws Exception { 70 // Fail on an engine that doesn't exist. 71 blockingInitAndVerify("__DOES_NOT_EXIST__", TextToSpeech.ERROR); 72 73 // Also, the "current engine" must be null 74 assertNull(mTts.getCurrentEngine()); 75 } 76 testSetLanguage_delegation()77 public void testSetLanguage_delegation() { 78 IDelegate delegate = Mockito.mock(IDelegate.class); 79 MockableTextToSpeechService.setMocker(delegate); 80 81 Mockito.doReturn(TextToSpeech.LANG_COUNTRY_VAR_AVAILABLE).when(delegate).onIsLanguageAvailable( 82 "eng", "USA", "variant"); 83 Mockito.doReturn(TextToSpeech.LANG_COUNTRY_VAR_AVAILABLE).when(delegate).onLoadLanguage( 84 "eng", "USA", "variant"); 85 86 // Test 1 :Tests that calls to onLoadLanguage( ) are delegated through to the 87 // service without any caching or intermediate steps. 88 assertEquals(TextToSpeech.LANG_COUNTRY_VAR_AVAILABLE, mTts.setLanguage(new Locale("eng", "USA", "variant"))); 89 Mockito.verify(delegate, Mockito.atLeast(0)).onIsLanguageAvailable( 90 "eng", "USA", "variant"); 91 Mockito.verify(delegate, Mockito.atLeast(0)).onLoadLanguage( 92 "eng", "USA", "variant"); 93 } 94 testSetLanguage_availableLanguage()95 public void testSetLanguage_availableLanguage() throws Exception { 96 IDelegate delegate = Mockito.mock(IDelegate.class); 97 MockableTextToSpeechService.setMocker(delegate); 98 99 // --------------------------------------------------------- 100 // Test 2 : Tests that when the language is successfully set 101 // like above (returns LANG_COUNTRY_AVAILABLE). That the 102 // request language changes from that point on. 103 Mockito.doReturn(TextToSpeech.LANG_COUNTRY_AVAILABLE).when(delegate).onIsLanguageAvailable( 104 "eng", "USA", "variant"); 105 Mockito.doReturn(TextToSpeech.LANG_COUNTRY_AVAILABLE).when(delegate).onIsLanguageAvailable( 106 "eng", "USA", ""); 107 Mockito.doReturn(TextToSpeech.LANG_COUNTRY_AVAILABLE).when(delegate).onLoadLanguage( 108 "eng", "USA", ""); 109 mTts.setLanguage(new Locale("eng", "USA", "variant")); 110 blockingCallSpeak("foo bar", delegate); 111 ArgumentCaptor<SynthesisRequest> req = ArgumentCaptor.forClass(SynthesisRequest.class); 112 Mockito.verify(delegate, Mockito.times(1)).onSynthesizeText(req.capture(), 113 Mockito.<SynthesisCallback>anyObject()); 114 115 assertEquals("eng", req.getValue().getLanguage()); 116 assertEquals("USA", req.getValue().getCountry()); 117 assertEquals("", req.getValue().getVariant()); 118 assertEquals("en-US", req.getValue().getVoiceName()); 119 } 120 testSetLanguage_unavailableLanguage()121 public void testSetLanguage_unavailableLanguage() throws Exception { 122 IDelegate delegate = Mockito.mock(IDelegate.class); 123 MockableTextToSpeechService.setMocker(delegate); 124 125 // --------------------------------------------------------- 126 // TEST 3 : Tests that the language that is set does not change when the 127 // engine reports it could not load the specified language. 128 Mockito.doReturn(TextToSpeech.LANG_NOT_SUPPORTED).when( 129 delegate).onIsLanguageAvailable("fra", "FRA", ""); 130 Mockito.doReturn(TextToSpeech.LANG_NOT_SUPPORTED).when( 131 delegate).onLoadLanguage("fra", "FRA", ""); 132 mTts.setLanguage(Locale.FRANCE); 133 blockingCallSpeak("le fou barre", delegate); 134 ArgumentCaptor<SynthesisRequest> req2 = ArgumentCaptor.forClass(SynthesisRequest.class); 135 Mockito.verify(delegate, Mockito.times(1)).onSynthesizeText(req2.capture(), 136 Mockito.<SynthesisCallback>anyObject()); 137 138 // The params are basically unchanged. 139 assertEquals("eng", req2.getValue().getLanguage()); 140 assertEquals("USA", req2.getValue().getCountry()); 141 assertEquals("", req2.getValue().getVariant()); 142 assertEquals("en-US", req2.getValue().getVoiceName()); 143 } 144 testIsLanguageAvailable()145 public void testIsLanguageAvailable() { 146 IDelegate delegate = Mockito.mock(IDelegate.class); 147 MockableTextToSpeechService.setMocker(delegate); 148 149 // Test1: Simple end to end test. 150 Mockito.doReturn(TextToSpeech.LANG_COUNTRY_AVAILABLE).when( 151 delegate).onIsLanguageAvailable("eng", "USA", ""); 152 153 assertEquals(TextToSpeech.LANG_COUNTRY_AVAILABLE, mTts.isLanguageAvailable(Locale.US)); 154 Mockito.verify(delegate, Mockito.times(1)).onIsLanguageAvailable( 155 "eng", "USA", ""); 156 } 157 testDefaultLanguage_setsVoiceName()158 public void testDefaultLanguage_setsVoiceName() throws Exception { 159 IDelegate delegate = Mockito.mock(IDelegate.class); 160 MockableTextToSpeechService.setMocker(delegate); 161 Locale defaultLocale = Locale.getDefault(); 162 163 // --------------------------------------------------------- 164 // Test that default language also sets the default voice 165 // name 166 Mockito.doReturn(TextToSpeech.LANG_COUNTRY_AVAILABLE). 167 when(delegate).onIsLanguageAvailable( 168 defaultLocale.getISO3Language(), 169 defaultLocale.getISO3Country().toUpperCase(), 170 defaultLocale.getVariant()); 171 Mockito.doReturn(TextToSpeech.LANG_COUNTRY_AVAILABLE). 172 when(delegate).onLoadLanguage( 173 defaultLocale.getISO3Language(), 174 defaultLocale.getISO3Country(), 175 defaultLocale.getVariant()); 176 177 blockingCallSpeak("foo bar", delegate); 178 ArgumentCaptor<SynthesisRequest> req = ArgumentCaptor.forClass(SynthesisRequest.class); 179 Mockito.verify(delegate, Mockito.times(1)).onSynthesizeText(req.capture(), 180 Mockito.<SynthesisCallback>anyObject()); 181 182 assertEquals(defaultLocale.getISO3Language(), req.getValue().getLanguage()); 183 assertEquals(defaultLocale.getISO3Country(), req.getValue().getCountry()); 184 assertEquals("", req.getValue().getVariant()); 185 assertEquals(defaultLocale.toLanguageTag(), req.getValue().getVoiceName()); 186 } 187 188 blockingCallSpeak(String speech, IDelegate mock)189 private void blockingCallSpeak(String speech, IDelegate mock) throws 190 InterruptedException { 191 final CountDownLatch latch = new CountDownLatch(1); 192 doCountDown(latch).when(mock).onSynthesizeText(Mockito.<SynthesisRequest>anyObject(), 193 Mockito.<SynthesisCallback>anyObject()); 194 mTts.speak(speech, TextToSpeech.QUEUE_ADD, null); 195 196 awaitCountDown(latch, 5, TimeUnit.SECONDS); 197 } 198 blockingInitAndVerify(final String engine, int errorCode)199 private void blockingInitAndVerify(final String engine, int errorCode) throws 200 InterruptedException { 201 TextToSpeech.OnInitListener listener = Mockito.mock( 202 TextToSpeech.OnInitListener.class); 203 204 final CountDownLatch latch = new CountDownLatch(1); 205 doCountDown(latch).when(listener).onInit(errorCode); 206 207 mTts = new TextToSpeech(getInstrumentation().getTargetContext(), 208 listener, engine, MOCK_PACKAGE, false /* use fallback package */); 209 210 awaitCountDown(latch, 5, TimeUnit.SECONDS); 211 } 212 213 public static abstract class CountDownBehaviour extends StubberImpl { CountDownBehaviour(Strictness strictness)214 public CountDownBehaviour(Strictness strictness) { 215 super(strictness); 216 } 217 218 /** Used to mock methods that return a result. */ andReturn(Object result)219 public abstract Stubber andReturn(Object result); 220 } 221 doCountDown(final CountDownLatch latch)222 public static CountDownBehaviour doCountDown(final CountDownLatch latch) { 223 return new CountDownBehaviour(Strictness.WARN) { 224 @Override 225 public <T> T when(T mock) { 226 return Mockito.doAnswer(new Answer<Void>() { 227 @Override 228 public Void answer(InvocationOnMock invocation) throws Exception { 229 latch.countDown(); 230 return null; 231 } 232 }).when(mock); 233 } 234 235 @Override 236 public Stubber andReturn(final Object result) { 237 return new StubberImpl(Strictness.WARN) { 238 @Override 239 public <T> T when(T mock) { 240 return Mockito.doAnswer(new Answer<Object>() { 241 @Override 242 public Object answer(InvocationOnMock invocation) throws Exception { 243 latch.countDown(); 244 return result; 245 } 246 }).when(mock); 247 } 248 }; 249 } 250 }; 251 } 252 253 public static void awaitCountDown(CountDownLatch latch, long timeout, TimeUnit unit) 254 throws InterruptedException { 255 Assert.assertTrue("Waited too long for method call", latch.await(timeout, unit)); 256 } 257 } 258