| /* |
| * Copyright (C) 2011 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package com.android.speech.tts; |
| |
| import android.speech.tts.SynthesisCallback; |
| import android.speech.tts.SynthesisRequest; |
| import android.speech.tts.TextToSpeech; |
| import android.test.InstrumentationTestCase; |
| |
| import com.android.speech.tts.MockableTextToSpeechService.IDelegate; |
| import org.mockito.ArgumentCaptor; |
| import org.mockito.Mockito; |
| import org.mockito.internal.stubbing.StubberImpl; |
| import org.mockito.invocation.InvocationOnMock; |
| import org.mockito.stubbing.Answer; |
| import org.mockito.stubbing.Stubber; |
| import org.mockito.quality.Strictness; |
| import junit.framework.Assert; |
| |
| import java.util.Locale; |
| import java.util.concurrent.Callable; |
| import java.util.concurrent.CountDownLatch; |
| import java.util.concurrent.TimeUnit; |
| |
| public class TextToSpeechTests extends InstrumentationTestCase { |
| private static final String MOCK_ENGINE = "com.android.speech.tts"; |
| private static final String MOCK_PACKAGE = "com.android.speech.tts.__testpackage__"; |
| |
| private TextToSpeech mTts; |
| |
| @Override |
| public void setUp() throws Exception { |
| IDelegate passThrough = Mockito.mock(IDelegate.class); |
| MockableTextToSpeechService.setMocker(passThrough); |
| |
| // For the default voice selection |
| Mockito.doReturn(TextToSpeech.LANG_COUNTRY_AVAILABLE).when(passThrough) |
| .onIsLanguageAvailable( |
| Mockito.anyString(), Mockito.anyString(), Mockito.anyString()); |
| Mockito.doReturn(TextToSpeech.LANG_COUNTRY_AVAILABLE).when(passThrough) |
| .onLoadLanguage( |
| Mockito.anyString(), Mockito.anyString(), Mockito.anyString()); |
| |
| blockingInitAndVerify(MOCK_ENGINE, TextToSpeech.SUCCESS); |
| assertEquals(MOCK_ENGINE, mTts.getCurrentEngine()); |
| } |
| |
| @Override |
| public void tearDown() { |
| if (mTts != null) { |
| mTts.shutdown(); |
| } |
| } |
| |
| public void testEngineInitialized() throws Exception { |
| // Fail on an engine that doesn't exist. |
| blockingInitAndVerify("__DOES_NOT_EXIST__", TextToSpeech.ERROR); |
| |
| // Also, the "current engine" must be null |
| assertNull(mTts.getCurrentEngine()); |
| } |
| |
| public void testSetLanguage_delegation() { |
| IDelegate delegate = Mockito.mock(IDelegate.class); |
| MockableTextToSpeechService.setMocker(delegate); |
| |
| Mockito.doReturn(TextToSpeech.LANG_COUNTRY_VAR_AVAILABLE).when(delegate).onIsLanguageAvailable( |
| "eng", "USA", "variant"); |
| Mockito.doReturn(TextToSpeech.LANG_COUNTRY_VAR_AVAILABLE).when(delegate).onLoadLanguage( |
| "eng", "USA", "variant"); |
| |
| // Test 1 :Tests that calls to onLoadLanguage( ) are delegated through to the |
| // service without any caching or intermediate steps. |
| assertEquals(TextToSpeech.LANG_COUNTRY_VAR_AVAILABLE, mTts.setLanguage(new Locale("eng", "USA", "variant"))); |
| Mockito.verify(delegate, Mockito.atLeast(0)).onIsLanguageAvailable( |
| "eng", "USA", "variant"); |
| Mockito.verify(delegate, Mockito.atLeast(0)).onLoadLanguage( |
| "eng", "USA", "variant"); |
| } |
| |
| public void testSetLanguage_availableLanguage() throws Exception { |
| IDelegate delegate = Mockito.mock(IDelegate.class); |
| MockableTextToSpeechService.setMocker(delegate); |
| |
| // --------------------------------------------------------- |
| // Test 2 : Tests that when the language is successfully set |
| // like above (returns LANG_COUNTRY_AVAILABLE). That the |
| // request language changes from that point on. |
| Mockito.doReturn(TextToSpeech.LANG_COUNTRY_AVAILABLE).when(delegate).onIsLanguageAvailable( |
| "eng", "USA", "variant"); |
| Mockito.doReturn(TextToSpeech.LANG_COUNTRY_AVAILABLE).when(delegate).onIsLanguageAvailable( |
| "eng", "USA", ""); |
| Mockito.doReturn(TextToSpeech.LANG_COUNTRY_AVAILABLE).when(delegate).onLoadLanguage( |
| "eng", "USA", ""); |
| mTts.setLanguage(new Locale("eng", "USA", "variant")); |
| blockingCallSpeak("foo bar", delegate); |
| ArgumentCaptor<SynthesisRequest> req = ArgumentCaptor.forClass(SynthesisRequest.class); |
| Mockito.verify(delegate, Mockito.times(1)).onSynthesizeText(req.capture(), |
| Mockito.<SynthesisCallback>anyObject()); |
| |
| assertEquals("eng", req.getValue().getLanguage()); |
| assertEquals("USA", req.getValue().getCountry()); |
| assertEquals("", req.getValue().getVariant()); |
| assertEquals("en-US", req.getValue().getVoiceName()); |
| } |
| |
| public void testSetLanguage_unavailableLanguage() throws Exception { |
| IDelegate delegate = Mockito.mock(IDelegate.class); |
| MockableTextToSpeechService.setMocker(delegate); |
| |
| // --------------------------------------------------------- |
| // TEST 3 : Tests that the language that is set does not change when the |
| // engine reports it could not load the specified language. |
| Mockito.doReturn(TextToSpeech.LANG_NOT_SUPPORTED).when( |
| delegate).onIsLanguageAvailable("fra", "FRA", ""); |
| Mockito.doReturn(TextToSpeech.LANG_NOT_SUPPORTED).when( |
| delegate).onLoadLanguage("fra", "FRA", ""); |
| mTts.setLanguage(Locale.FRANCE); |
| blockingCallSpeak("le fou barre", delegate); |
| ArgumentCaptor<SynthesisRequest> req2 = ArgumentCaptor.forClass(SynthesisRequest.class); |
| Mockito.verify(delegate, Mockito.times(1)).onSynthesizeText(req2.capture(), |
| Mockito.<SynthesisCallback>anyObject()); |
| |
| // The params are basically unchanged. |
| assertEquals("eng", req2.getValue().getLanguage()); |
| assertEquals("USA", req2.getValue().getCountry()); |
| assertEquals("", req2.getValue().getVariant()); |
| assertEquals("en-US", req2.getValue().getVoiceName()); |
| } |
| |
| public void testIsLanguageAvailable() { |
| IDelegate delegate = Mockito.mock(IDelegate.class); |
| MockableTextToSpeechService.setMocker(delegate); |
| |
| // Test1: Simple end to end test. |
| Mockito.doReturn(TextToSpeech.LANG_COUNTRY_AVAILABLE).when( |
| delegate).onIsLanguageAvailable("eng", "USA", ""); |
| |
| assertEquals(TextToSpeech.LANG_COUNTRY_AVAILABLE, mTts.isLanguageAvailable(Locale.US)); |
| Mockito.verify(delegate, Mockito.times(1)).onIsLanguageAvailable( |
| "eng", "USA", ""); |
| } |
| |
| public void testDefaultLanguage_setsVoiceName() throws Exception { |
| IDelegate delegate = Mockito.mock(IDelegate.class); |
| MockableTextToSpeechService.setMocker(delegate); |
| Locale defaultLocale = Locale.getDefault(); |
| |
| // --------------------------------------------------------- |
| // Test that default language also sets the default voice |
| // name |
| Mockito.doReturn(TextToSpeech.LANG_COUNTRY_AVAILABLE). |
| when(delegate).onIsLanguageAvailable( |
| defaultLocale.getISO3Language(), |
| defaultLocale.getISO3Country().toUpperCase(), |
| defaultLocale.getVariant()); |
| Mockito.doReturn(TextToSpeech.LANG_COUNTRY_AVAILABLE). |
| when(delegate).onLoadLanguage( |
| defaultLocale.getISO3Language(), |
| defaultLocale.getISO3Country(), |
| defaultLocale.getVariant()); |
| |
| blockingCallSpeak("foo bar", delegate); |
| ArgumentCaptor<SynthesisRequest> req = ArgumentCaptor.forClass(SynthesisRequest.class); |
| Mockito.verify(delegate, Mockito.times(1)).onSynthesizeText(req.capture(), |
| Mockito.<SynthesisCallback>anyObject()); |
| |
| assertEquals(defaultLocale.getISO3Language(), req.getValue().getLanguage()); |
| assertEquals(defaultLocale.getISO3Country(), req.getValue().getCountry()); |
| assertEquals("", req.getValue().getVariant()); |
| assertEquals(defaultLocale.toLanguageTag(), req.getValue().getVoiceName()); |
| } |
| |
| |
| private void blockingCallSpeak(String speech, IDelegate mock) throws |
| InterruptedException { |
| final CountDownLatch latch = new CountDownLatch(1); |
| doCountDown(latch).when(mock).onSynthesizeText(Mockito.<SynthesisRequest>anyObject(), |
| Mockito.<SynthesisCallback>anyObject()); |
| mTts.speak(speech, TextToSpeech.QUEUE_ADD, null); |
| |
| awaitCountDown(latch, 5, TimeUnit.SECONDS); |
| } |
| |
| private void blockingInitAndVerify(final String engine, int errorCode) throws |
| InterruptedException { |
| TextToSpeech.OnInitListener listener = Mockito.mock( |
| TextToSpeech.OnInitListener.class); |
| |
| final CountDownLatch latch = new CountDownLatch(1); |
| doCountDown(latch).when(listener).onInit(errorCode); |
| |
| mTts = new TextToSpeech(getInstrumentation().getTargetContext(), |
| listener, engine, MOCK_PACKAGE, false /* use fallback package */); |
| |
| awaitCountDown(latch, 5, TimeUnit.SECONDS); |
| } |
| |
| public static abstract class CountDownBehaviour extends StubberImpl { |
| public CountDownBehaviour(Strictness strictness) { |
| super(strictness); |
| } |
| |
| /** Used to mock methods that return a result. */ |
| public abstract Stubber andReturn(Object result); |
| } |
| |
| public static CountDownBehaviour doCountDown(final CountDownLatch latch) { |
| return new CountDownBehaviour(Strictness.WARN) { |
| @Override |
| public <T> T when(T mock) { |
| return Mockito.doAnswer(new Answer<Void>() { |
| @Override |
| public Void answer(InvocationOnMock invocation) throws Exception { |
| latch.countDown(); |
| return null; |
| } |
| }).when(mock); |
| } |
| |
| @Override |
| public Stubber andReturn(final Object result) { |
| return new StubberImpl(Strictness.WARN) { |
| @Override |
| public <T> T when(T mock) { |
| return Mockito.doAnswer(new Answer<Object>() { |
| @Override |
| public Object answer(InvocationOnMock invocation) throws Exception { |
| latch.countDown(); |
| return result; |
| } |
| }).when(mock); |
| } |
| }; |
| } |
| }; |
| } |
| |
| public static void awaitCountDown(CountDownLatch latch, long timeout, TimeUnit unit) |
| throws InterruptedException { |
| Assert.assertTrue("Waited too long for method call", latch.await(timeout, unit)); |
| } |
| } |