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