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 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
     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
     62     public void tearDown() {
     63         if (mTts != null) {
     64             mTts.shutdown();
     65         }
     66     }
     67 
     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 
     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 
     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 
    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 
    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 
    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 
    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 
    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. */
    214         public abstract Stubber andReturn(Object result);
    215     }
    216 
    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