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