Home | History | Annotate | Download | only in tts
      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
     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
     63     public void tearDown() {
     64         if (mTts != null) {
     65             mTts.shutdown();
     66         }
     67     }
     68 
     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 
     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 
     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 
    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 
    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 
    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 
    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 
    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 {
    214         public CountDownBehaviour(Strictness strictness) {
    215             super(strictness);
    216         }
    217 
    218         /** Used to mock methods that return a result. */
    219         public abstract Stubber andReturn(Object result);
    220     }
    221 
    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