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)).onIsLanguageAvailable( 74 "eng", "USA", "variant"); 75 LittleMock.verify(delegate, LittleMock.times(1)).onLoadLanguage( 76 "eng", "USA", "variant"); 77 } 78 79 public void testSetLanguage_availableLanguage() throws Exception { 80 IDelegate delegate = LittleMock.mock(IDelegate.class); 81 MockableTextToSpeechService.setMocker(delegate); 82 83 // --------------------------------------------------------- 84 // Test 2 : Tests that when the language is successfully set 85 // like above (returns LANG_COUNTRY_AVAILABLE). That the 86 // request language changes from that point on. 87 LittleMock.doReturn(TextToSpeech.LANG_COUNTRY_AVAILABLE).when(delegate).onIsLanguageAvailable( 88 "eng", "USA", "variant"); 89 LittleMock.doReturn(TextToSpeech.LANG_COUNTRY_AVAILABLE).when(delegate).onLoadLanguage( 90 "eng", "USA", "variant"); 91 mTts.setLanguage(new Locale("eng", "USA", "variant")); 92 blockingCallSpeak("foo bar", delegate); 93 ArgumentCaptor<SynthesisRequest> req = LittleMock.createCaptor(); 94 LittleMock.verify(delegate, LittleMock.times(1)).onSynthesizeText(req.capture(), 95 LittleMock.<SynthesisCallback>anyObject()); 96 97 assertEquals("eng", req.getValue().getLanguage()); 98 assertEquals("USA", req.getValue().getCountry()); 99 assertEquals("", req.getValue().getVariant()); 100 } 101 102 public void testSetLanguage_unavailableLanguage() throws Exception { 103 IDelegate delegate = LittleMock.mock(IDelegate.class); 104 MockableTextToSpeechService.setMocker(delegate); 105 106 // --------------------------------------------------------- 107 // TEST 3 : Tests that the language that is set does not change when the 108 // engine reports it could not load the specified language. 109 LittleMock.doReturn(TextToSpeech.LANG_NOT_SUPPORTED).when( 110 delegate).onIsLanguageAvailable("fra", "FRA", ""); 111 LittleMock.doReturn(TextToSpeech.LANG_NOT_SUPPORTED).when( 112 delegate).onLoadLanguage("fra", "FRA", ""); 113 mTts.setLanguage(Locale.FRANCE); 114 blockingCallSpeak("le fou barre", delegate); 115 ArgumentCaptor<SynthesisRequest> req2 = LittleMock.createCaptor(); 116 LittleMock.verify(delegate, LittleMock.times(1)).onSynthesizeText(req2.capture(), 117 LittleMock.<SynthesisCallback>anyObject()); 118 119 // The params are basically unchanged. 120 assertEquals("eng", req2.getValue().getLanguage()); 121 assertEquals("USA", req2.getValue().getCountry()); 122 assertEquals("", req2.getValue().getVariant()); 123 } 124 125 public void testIsLanguageAvailable() { 126 IDelegate delegate = LittleMock.mock(IDelegate.class); 127 MockableTextToSpeechService.setMocker(delegate); 128 129 // Test1: Simple end to end test. 130 LittleMock.doReturn(TextToSpeech.LANG_COUNTRY_AVAILABLE).when( 131 delegate).onIsLanguageAvailable("eng", "USA", ""); 132 133 assertEquals(TextToSpeech.LANG_COUNTRY_AVAILABLE, mTts.isLanguageAvailable(Locale.US)); 134 LittleMock.verify(delegate, LittleMock.times(1)).onIsLanguageAvailable( 135 "eng", "USA", ""); 136 } 137 138 139 private void blockingCallSpeak(String speech, IDelegate mock) throws 140 InterruptedException { 141 final CountDownLatch latch = new CountDownLatch(1); 142 doCountDown(latch).when(mock).onSynthesizeText(LittleMock.<SynthesisRequest>anyObject(), 143 LittleMock.<SynthesisCallback>anyObject()); 144 mTts.speak(speech, TextToSpeech.QUEUE_ADD, null); 145 146 awaitCountDown(latch, 5, TimeUnit.SECONDS); 147 } 148 149 private void blockingInitAndVerify(final String engine, int errorCode) throws 150 InterruptedException { 151 TextToSpeech.OnInitListener listener = LittleMock.mock( 152 TextToSpeech.OnInitListener.class); 153 154 final CountDownLatch latch = new CountDownLatch(1); 155 doCountDown(latch).when(listener).onInit(errorCode); 156 157 mTts = new TextToSpeech(getInstrumentation().getTargetContext(), 158 listener, engine, MOCK_PACKAGE, false /* use fallback package */); 159 160 awaitCountDown(latch, 5, TimeUnit.SECONDS); 161 } 162 163 public interface CountDownBehaviour extends Behaviour { 164 /** Used to mock methods that return a result. */ 165 Behaviour andReturn(Object result); 166 } 167 168 public static CountDownBehaviour doCountDown(final CountDownLatch latch) { 169 return new CountDownBehaviour() { 170 @Override 171 public <T> T when(T mock) { 172 return LittleMock.doAnswer(new Callable<Void>() { 173 @Override 174 public Void call() throws Exception { 175 latch.countDown(); 176 return null; 177 } 178 }).when(mock); 179 } 180 181 @Override 182 public Behaviour andReturn(final Object result) { 183 return new Behaviour() { 184 @Override 185 public <T> T when(T mock) { 186 return LittleMock.doAnswer(new Callable<Object>() { 187 @Override 188 public Object call() throws Exception { 189 latch.countDown(); 190 return result; 191 } 192 }).when(mock); 193 } 194 }; 195 } 196 }; 197 } 198 199 public static void awaitCountDown(CountDownLatch latch, long timeout, TimeUnit unit) 200 throws InterruptedException { 201 Assert.assertTrue("Waited too long for method call", latch.await(timeout, unit)); 202 } 203 } 204