From 47a1bfdca2d542772321da28dfef303df692275e Mon Sep 17 00:00:00 2001 From: Sonderful Date: Fri, 23 Nov 2018 23:31:51 -0800 Subject: [PATCH 1/2] Removing DiscoveryActivity, unused location permission DiscoveryActivity is unreachable for a normal user of the app. It seems to have been untouched for two years. Removing for now, easy to revive if someone still needs it. Also removing the location permission that was required for beacons. Missed in previous commits. --- mobile/src/main/AndroidManifest.xml | 2 - .../main/java/mycroft/ai/DiscoveryActivity.kt | 124 ------------- .../src/main/java/mycroft/ai/MainActivity.kt | 3 - .../ai/utils/NetworkAutoDiscoveryUtil.kt | 167 ------------------ 4 files changed, 296 deletions(-) delete mode 100755 mobile/src/main/java/mycroft/ai/DiscoveryActivity.kt delete mode 100755 mobile/src/main/java/mycroft/ai/utils/NetworkAutoDiscoveryUtil.kt diff --git a/mobile/src/main/AndroidManifest.xml b/mobile/src/main/AndroidManifest.xml index 8f7c9ceb..bf431d2b 100755 --- a/mobile/src/main/AndroidManifest.xml +++ b/mobile/src/main/AndroidManifest.xml @@ -27,7 +27,6 @@ - - . - * - */ - -package mycroft.ai - -import android.content.Context -import android.os.Bundle -import android.util.Log - -import java.net.ServerSocket - - -import android.net.nsd.NsdManager -import android.net.nsd.NsdManager.DiscoveryListener -import android.net.nsd.NsdManager.ResolveListener -import android.net.nsd.NsdServiceInfo -import android.annotation.SuppressLint -import android.app.Activity - -@SuppressLint("NewApi") -class DiscoveryActivity : Activity() { - - internal lateinit var mDiscoveryListener: DiscoveryListener - - internal var mServiceName: String? = null - internal var mServiceInfo: NsdServiceInfo? = null - internal var mServerSocket: ServerSocket? = null - internal var mLocalPort: Int = 0 - - internal lateinit var mNsdManager: NsdManager - - internal val TAG = "ServiceDiscovery" - internal val SERVICE_TYPE = "_mycroft._tcp" - internal val SERVICE_NAME = "MycroftAI Websocket" - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - setContentView(R.layout.activity_main) - - mNsdManager = applicationContext.getSystemService(Context.NSD_SERVICE) as NsdManager - - initializeDiscoveryListener() - - mNsdManager.discoverServices( - SERVICE_TYPE, NsdManager.PROTOCOL_DNS_SD, mDiscoveryListener) - - } - - fun initializeDiscoveryListener() { - - // Instantiate a new DiscoveryListener - mDiscoveryListener = object : NsdManager.DiscoveryListener { - - // Called as soon as service discovery begins. - override fun onDiscoveryStarted(regType: String) { - Log.d(TAG, "Service discovery started") - } - - override fun onServiceFound(service: NsdServiceInfo) { - // A service was found! Do something with it. - Log.d(TAG, "Service discovery success: $service") - if (service.serviceType != SERVICE_TYPE) { - // Service type is the string containing the protocol and - // transport layer for this service. - Log.d(TAG, "Mycroft found!: " + service.serviceType + " " + service.host + " " + service.port) - resolveService(service) - } - } - - override fun onServiceLost(service: NsdServiceInfo) { - // When the network service is no longer available. - // Internal bookkeeping code goes here. - Log.e(TAG, "service lost: $service") - } - - override fun onDiscoveryStopped(serviceType: String) { - Log.i(TAG, "Discovery stopped: $serviceType") - } - - override fun onStartDiscoveryFailed(serviceType: String, errorCode: Int) { - Log.e(TAG, "Discovery failed: Error code: $errorCode") - mNsdManager.stopServiceDiscovery(this) - } - - override fun onStopDiscoveryFailed(serviceType: String, errorCode: Int) { - Log.e(TAG, "Discovery failed: Error code: $errorCode") - mNsdManager.stopServiceDiscovery(this) - } - } - } - - private fun resolveService(service: NsdServiceInfo) { - mNsdManager.resolveService(service, object : ResolveListener { - - override fun onServiceResolved(serviceInfo: NsdServiceInfo) { - Log.d(TAG, "Resolving service...") - Log.i(TAG, serviceInfo.host.toString()) - Log.i(TAG, "Port: " + serviceInfo.port) - } - - override fun onResolveFailed(serviceInfo: NsdServiceInfo, errorCode: Int) { - // TODO Auto-generated method stub - Log.d(TAG, "Service resolve failed!") - } - }) - } -} \ No newline at end of file diff --git a/mobile/src/main/java/mycroft/ai/MainActivity.kt b/mobile/src/main/java/mycroft/ai/MainActivity.kt index d3264582..eb5a9029 100644 --- a/mobile/src/main/java/mycroft/ai/MainActivity.kt +++ b/mobile/src/main/java/mycroft/ai/MainActivity.kt @@ -118,9 +118,6 @@ class MainActivity : AppCompatActivity() { } registerReceivers() - - // start the discovery activity (testing only) - // startActivity(new Intent(this, DiscoveryActivity.class)); } override fun onCreateOptionsMenu(menu: Menu): Boolean { diff --git a/mobile/src/main/java/mycroft/ai/utils/NetworkAutoDiscoveryUtil.kt b/mobile/src/main/java/mycroft/ai/utils/NetworkAutoDiscoveryUtil.kt deleted file mode 100755 index 19cc2108..00000000 --- a/mobile/src/main/java/mycroft/ai/utils/NetworkAutoDiscoveryUtil.kt +++ /dev/null @@ -1,167 +0,0 @@ -/* - * Copyright (c) 2017. Mycroft AI, Inc. - * - * This file is part of Mycroft-Android a client for Mycroft Core. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - */ - -package mycroft.ai.utils - -import android.content.Context -import android.net.nsd.NsdServiceInfo -import android.net.nsd.NsdManager -import android.util.Log - -/** - * Created by paul on 2016/06/28. - */ -class NetworkAutoDiscoveryUtil(internal var mContext: Context) { - private var mServiceName = "MycroftAI Websocket" - private var chosenServiceInfo: NsdServiceInfo? = null - private var mNsdManager: NsdManager = mContext.getSystemService(Context.NSD_SERVICE) as NsdManager - - //declare late init as initialisation is after the fact. - internal lateinit var mResolveListener: NsdManager.ResolveListener - - private var mDiscoveryListener: NsdManager.DiscoveryListener? = null - private var mRegistrationListener: NsdManager.RegistrationListener? = null - - - fun initializeNsd() { - initializeResolveListener() - //mNsdManager.init(mContext.getMainLooper(), this); - } - - fun initializeDiscoveryListener() { - mDiscoveryListener = object : NsdManager.DiscoveryListener { - override fun onDiscoveryStarted(regType: String) { - Log.d(TAG, "Service discovery started") - } - - override fun onServiceFound(service: NsdServiceInfo) { - Log.d(TAG, "Service discovery success$service") - if (service.serviceType != SERVICE_TYPE) { - Log.d(TAG, "Unknown Service Type: " + service.serviceType) - } else if (service.serviceName == mServiceName) { - Log.d(TAG, "Same machine: $mServiceName") - } else if (service.serviceName.contains(mServiceName)) { - mNsdManager.resolveService(service, mResolveListener) - } - } - - override fun onServiceLost(service: NsdServiceInfo) { - Log.e(TAG, "service lost$service") - if (chosenServiceInfo == service) { - chosenServiceInfo = null - } - } - - override fun onDiscoveryStopped(serviceType: String) { - Log.i(TAG, "Discovery stopped: $serviceType") - } - - override fun onStartDiscoveryFailed(serviceType: String, errorCode: Int) { - Log.e(TAG, "Discovery failed: Error code:$errorCode") - } - - override fun onStopDiscoveryFailed(serviceType: String, errorCode: Int) { - Log.e(TAG, "Discovery failed: Error code:$errorCode") - } - } - } - - fun initializeResolveListener() { - mResolveListener = object : NsdManager.ResolveListener { - override fun onResolveFailed(serviceInfo: NsdServiceInfo, errorCode: Int) { - Log.e(TAG, "Resolve failed$errorCode") - } - - override fun onServiceResolved(serviceInfo: NsdServiceInfo) { - Log.e(TAG, "Resolve Succeeded. $serviceInfo") - if (serviceInfo.serviceName == mServiceName) { - Log.d(TAG, "Same IP.") - return - } - chosenServiceInfo = serviceInfo - } - } - } - - fun initializeRegistrationListener() { - mRegistrationListener = object : NsdManager.RegistrationListener { - override fun onServiceRegistered(NsdServiceInfo: NsdServiceInfo) { - mServiceName = NsdServiceInfo.serviceName - Log.d(TAG, "Service registered: $mServiceName") - } - - override fun onRegistrationFailed(arg0: NsdServiceInfo, arg1: Int) { - Log.d(TAG, "Service registration failed: $arg1") - } - - override fun onServiceUnregistered(arg0: NsdServiceInfo) { - Log.d(TAG, "Service unregistered: " + arg0.serviceName) - } - - override fun onUnregistrationFailed(serviceInfo: NsdServiceInfo, errorCode: Int) { - Log.d(TAG, "Service unregistration failed: $errorCode") - } - } - } - - fun registerService(port: Int) { - tearDown() // Cancel any previous registration request - initializeRegistrationListener() - val serviceInfo = NsdServiceInfo() - serviceInfo.port = port - serviceInfo.serviceName = mServiceName - serviceInfo.serviceType = SERVICE_TYPE - mNsdManager.registerService( - serviceInfo, NsdManager.PROTOCOL_DNS_SD, mRegistrationListener) - } - - fun discoverServices() { - stopDiscovery() // Cancel any existing discovery request - initializeDiscoveryListener() - mNsdManager.discoverServices( - SERVICE_TYPE, NsdManager.PROTOCOL_DNS_SD, mDiscoveryListener) - } - - fun stopDiscovery() { - if (mDiscoveryListener != null) { - try { - mNsdManager.stopServiceDiscovery(mDiscoveryListener) - } finally { - } - mDiscoveryListener = null - } - } - - fun tearDown() { - if (mRegistrationListener != null) { - try { - mNsdManager.unregisterService(mRegistrationListener) - } finally { - } - mRegistrationListener = null - } - } - - companion object { - - private val TAG = "NetworkDiscovery" - private val SERVICE_TYPE = " _mycroft._tcp" - } -} From d1e5c81cbeedea0e56651b9f9a4cbc0a9b8757d4 Mon Sep 17 00:00:00 2001 From: Sonderful Date: Sat, 24 Nov 2018 00:58:05 -0800 Subject: [PATCH 2/2] Upgrading many dependencies, rewrote unit tests in Kotlin * Many dependencies were out of date, so I updated them. * Several dependencies were unused (palette library). * Unit test dependencies were JUnit 4, switched to JUnit 5 and MockK for Kotlin, instead of Mockito. * Removed Powermock, which was completely unnecessary for mocking the android.util.Log class. * Removed example unit test class. --- build.gradle | 12 +- mobile/build.gradle | 51 +++--- mobile/src/main/java/mycroft/ai/TTSManager.kt | 9 +- .../test/java/mycroft/ai/ExampleUnitTest.java | 37 ----- .../src/test/java/mycroft/ai/LogAnswer.java | 61 -------- .../test/java/mycroft/ai/TTSManagerTest.java | 148 ------------------ .../test/java/mycroft/ai/TTSManagerTest.kt | 61 ++++++++ 7 files changed, 97 insertions(+), 282 deletions(-) delete mode 100755 mobile/src/test/java/mycroft/ai/ExampleUnitTest.java delete mode 100755 mobile/src/test/java/mycroft/ai/LogAnswer.java delete mode 100755 mobile/src/test/java/mycroft/ai/TTSManagerTest.java create mode 100644 mobile/src/test/java/mycroft/ai/TTSManagerTest.kt diff --git a/build.gradle b/build.gradle index 576611ea..28ebe199 100755 --- a/build.gradle +++ b/build.gradle @@ -1,6 +1,6 @@ // Top-level build file where you can add configuration options common to all sub-projects/modules. buildscript { - ext.kotlin_version = "1.2.51" + ext.kotlin_version = "1.3.10" repositories { google() jcenter() @@ -11,7 +11,7 @@ buildscript { classpath 'com.android.tools.build:gradle:3.2.1' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" classpath "org.jetbrains.kotlin:kotlin-android-extensions:$kotlin_version" - classpath 'io.fabric.tools:gradle:1.+' + classpath 'io.fabric.tools:gradle:1.26.1' // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files @@ -62,9 +62,7 @@ subprojects { ext { minSdkVersion = 19 targetSdkVersion = 27 - compileSdkVersion = 27 - buildToolsVersion = "27.0.3" - kotlinVersion = "1.2.51" - supportVersion = "27.0.1" - + compileSdkVersion = 28 + buildToolsVersion = "28.0.3" + supportVersion = "28.0.0" } \ No newline at end of file diff --git a/mobile/build.gradle b/mobile/build.gradle index 01f850bd..4f9102f1 100755 --- a/mobile/build.gradle +++ b/mobile/build.gradle @@ -44,6 +44,10 @@ android { dataBinding { enabled=true } + + testOptions { + unitTests.returnDefaultValues = true + } } repositories { @@ -54,41 +58,36 @@ repositories { dependencies { implementation fileTree(include: ['*.jar'], dir: 'libs') - implementation "com.android.support:appcompat-v7:$rootProject.supportVersion" - implementation "com.android.support:design:$rootProject.supportVersion" - implementation "com.android.support:support-v4:$rootProject.supportVersion" + implementation "org.jetbrains.kotlin:kotlin-stdlib:$rootProject.kotlin_version" - // layout deps + // Support Libraries and Google Play Services + implementation "com.android.support:appcompat-v7:$rootProject.supportVersion" implementation "com.android.support:cardview-v7:$rootProject.supportVersion" + implementation "com.android.support:design:$rootProject.supportVersion" implementation "com.android.support:recyclerview-v7:$rootProject.supportVersion" + implementation "com.android.support:support-core-utils:$rootProject.supportVersion" + implementation 'com.google.android.gms:play-services-wearable:16.0.1' - implementation "com.android.support:palette-v7:$rootProject.supportVersion" + implementation 'org.java-websocket:Java-WebSocket:1.3.0' - // Unit test dependencies - testImplementation 'org.mockito:mockito-core:1.10.19' - testImplementation 'org.powermock:powermock-api-mockito:1.6.4' - testImplementation 'org.powermock:powermock-module-junit4-rule-agent:1.6.4' - testImplementation 'org.powermock:powermock-module-junit4-rule:1.6.4' - testImplementation 'org.powermock:powermock-module-junit4:1.6.4' - testImplementation 'junit:junit:4.12' - // Instrumentation dependencies - androidTestImplementation 'com.android.support.test.espresso:espresso-core:2.2.2' - androidTestImplementation 'com.android.support.test:runner:0.5' - androidTestImplementation "com.android.support:support-annotations:$rootProject.supportVersion" - implementation('com.crashlytics.sdk.android:crashlytics:2.5.7@aar') { + implementation 'io.reactivex.rxjava2:rxjava:2.2.3' + implementation 'io.reactivex.rxjava2:rxandroid:2.1.0' + + implementation('com.crashlytics.sdk.android:crashlytics:2.9.6@aar') { transitive = true } - implementation 'com.google.android.gms:play-services-wearable:9.4.0' - wearApp project(':wear') + implementation project(':shared') - //kotlin - implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" - //rxjava,rxandroid - implementation 'io.reactivex.rxjava2:rxjava:2.2.3' - implementation 'io.reactivex.rxjava2:rxandroid:2.1.0' + wearApp project(':wear') - implementation 'org.java-websocket:Java-WebSocket:1.3.0' + // Unit test dependencies + testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.3.1' + testImplementation 'org.junit.jupiter:junit-jupiter-api:5.3.1' + testImplementation 'io.mockk:mockk:1.8.13.kotlin13' - implementation "com.android.support:support-core-utils:$rootProject.supportVersion" + // Instrumentation dependencies + androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2' + androidTestImplementation 'com.android.support.test:runner:1.0.2' + androidTestImplementation "com.android.support:support-annotations:$rootProject.supportVersion" } diff --git a/mobile/src/main/java/mycroft/ai/TTSManager.kt b/mobile/src/main/java/mycroft/ai/TTSManager.kt index a183064c..65509d51 100755 --- a/mobile/src/main/java/mycroft/ai/TTSManager.kt +++ b/mobile/src/main/java/mycroft/ai/TTSManager.kt @@ -46,11 +46,14 @@ import java.util.Locale */ class TTSManager { + val notInitializedErrorMessage = "TTS Not Initialized" + val initializationFailedErrorMessage = "Initialization Failed!" /** * Backing TTS for this instance. Should not (ever) be null. */ private lateinit var mTts: TextToSpeech + /** * Whether the TTS is available for use (i.e. loaded into memory) */ @@ -71,7 +74,7 @@ class TTSManager { logError("This Language is not supported") } } else { - logError("Initialization Failed!") + logError(initializationFailedErrorMessage) } } @@ -108,7 +111,7 @@ class TTSManager { if (isLoaded) mTts.speak(text, TextToSpeech.QUEUE_ADD, null) else { - logError("TTS Not Initialized") + logError(notInitializedErrorMessage) } } @@ -117,7 +120,7 @@ class TTSManager { if (isLoaded) mTts.speak(text, TextToSpeech.QUEUE_FLUSH, null) else - logError("TTS Not Initialized") + logError(notInitializedErrorMessage) } /** diff --git a/mobile/src/test/java/mycroft/ai/ExampleUnitTest.java b/mobile/src/test/java/mycroft/ai/ExampleUnitTest.java deleted file mode 100755 index 5de091b1..00000000 --- a/mobile/src/test/java/mycroft/ai/ExampleUnitTest.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright (c) 2017. Mycroft AI, Inc. - * - * This file is part of Mycroft-Android a client for Mycroft Core. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - */ - -package mycroft.ai; - -import org.junit.Test; - -import static org.junit.Assert.*; - -/** - * Example local unit test, which will execute on the development machine (host). - * - * @see Testing documentation - */ -public class ExampleUnitTest { - @Test - public void addition_isCorrect() throws Exception { - assertEquals(4, 2 + 2); - } -} \ No newline at end of file diff --git a/mobile/src/test/java/mycroft/ai/LogAnswer.java b/mobile/src/test/java/mycroft/ai/LogAnswer.java deleted file mode 100755 index 96396391..00000000 --- a/mobile/src/test/java/mycroft/ai/LogAnswer.java +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright (c) 2017. Mycroft AI, Inc. - * - * This file is part of Mycroft-Android a client for Mycroft Core. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - */ - -package mycroft.ai; - -import org.mockito.invocation.InvocationOnMock; -import org.mockito.stubbing.Answer; - -import java.io.PrintStream; -import java.nio.charset.Charset; -import java.util.Locale; - -/** - * Useful for mocking {@link android.util.Log} static methods. - *

- * Use it like so: - * {@code - * Mockito.when(Log.v(anyString(), anyString())).then(new LogAnswer(System.out)); - * } - *

- * - * @author Philip Cohn-Cort - */ -public class LogAnswer implements Answer { - - protected final PrintStream stream; - - public LogAnswer(PrintStream stream) { - this.stream = stream; - } - - @Override - public Integer answer(InvocationOnMock invocation) throws Throwable { - String tag = invocation.getArgumentAt(0, String.class); - String msg = invocation.getArgumentAt(1, String.class); - - String name = invocation.getMethod().getName(); - - String format = String.format(Locale.US, "[Log.%s] %s: %s", name, tag, msg); - stream.println(format); - - return format.getBytes(Charset.forName("UTF8")).length; - } -} \ No newline at end of file diff --git a/mobile/src/test/java/mycroft/ai/TTSManagerTest.java b/mobile/src/test/java/mycroft/ai/TTSManagerTest.java deleted file mode 100755 index 15127220..00000000 --- a/mobile/src/test/java/mycroft/ai/TTSManagerTest.java +++ /dev/null @@ -1,148 +0,0 @@ -/* - * Copyright (c) 2017. Mycroft AI, Inc. - * - * This file is part of Mycroft-Android a client for Mycroft Core. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - */ - -package mycroft.ai; - -import android.os.Bundle; -import android.speech.tts.TextToSpeech; -import android.util.Log; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.ArgumentCaptor; -import org.powermock.core.classloader.annotations.PrepareForTest; -import org.powermock.modules.junit4.PowerMockRunner; - -import java.util.HashMap; -import java.util.Locale; - -import static org.junit.Assert.*; -import static org.mockito.Matchers.any; -import static org.mockito.Matchers.anyString; -import static org.mockito.Mockito.atLeast; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.powermock.api.mockito.PowerMockito.mock; -import static org.powermock.api.mockito.PowerMockito.mockStatic; -import static org.powermock.api.mockito.PowerMockito.when; - -/** - * @author Philip Cohn-Cort - */ -@RunWith(PowerMockRunner.class) -@PrepareForTest({Log.class}) -public class TTSManagerTest { - - @Before - public void setUp() throws Exception { - mockStatic(Log.class); - - LogAnswer stdOut = new LogAnswer(System.out); - when(Log.v(anyString(), anyString())).then(stdOut); - when(Log.d(anyString(), anyString())).then(stdOut); - when(Log.i(anyString(), anyString())).then(stdOut); - - LogAnswer stdErr = new LogAnswer(System.err); - when(Log.w(anyString(), anyString())).then(stdErr); - when(Log.e(anyString(), anyString())).then(stdErr); - when(Log.wtf(anyString(), anyString())).then(stdErr); - } - - @Test - public void testAddingToQueueBeforeIsLoaded() throws Exception { - TextToSpeech mock = mock(TextToSpeech.class); - TTSManager ttsManager = new TTSManager(mock); - - TTSManager.TTSListener mockListener = mock(TTSManager.TTSListener.class); - ttsManager.setTTSListener(mockListener); - - ttsManager.addQueue("text a"); - - verify(mockListener).onError("TTS Not Initialized"); - } - - @Test - public void testAddingToQueueAfterIsLoaded() throws Exception { - TextToSpeech tts = mock(TextToSpeech.class); - when(tts.setLanguage(any(Locale.class))).thenReturn(TextToSpeech.LANG_AVAILABLE); - - TTSManager ttsManager = new TTSManager(tts); - - TTSManager.TTSListener mockListener = mock(TTSManager.TTSListener.class); - ttsManager.setTTSListener(mockListener); - - ttsManager.getOnInitListener().onInit(TextToSpeech.SUCCESS); - - verify(mockListener, never()).onError(anyString()); - - ttsManager.addQueue("text a"); - - verify(mockListener, never()).onError(anyString()); - } - - @Test - public void testAddingToQueueTriggersSpeak() throws Exception { - TextToSpeech tts = mock(TextToSpeech.class); - when(tts.setLanguage(any(Locale.class))).thenReturn(TextToSpeech.LANG_AVAILABLE); - - TTSManager ttsManager = new TTSManager(tts); - - TTSManager.TTSListener mockListener = mock(TTSManager.TTSListener.class); - ttsManager.setTTSListener(mockListener); - - ttsManager.getOnInitListener().onInit(TextToSpeech.SUCCESS); - - ttsManager.addQueue("text a"); - - // Make sure that one of the speak methods was called, but we don't care which - - ArgumentCaptor paramCaptor1 = ArgumentCaptor.forClass(Integer.class); - ArgumentCaptor paramCaptor2 = ArgumentCaptor.forClass(Integer.class); - - //noinspection unchecked - verify(tts, atLeast(0)).speak(anyString(), paramCaptor1.capture(), any(HashMap.class)); - verify(tts, atLeast(0)).speak(any(CharSequence.class), paramCaptor2.capture(), any(Bundle.class), anyString()); - - assertTrue("One of the speak methods should have been called.", - !paramCaptor1.getAllValues().isEmpty() - || - !paramCaptor2.getAllValues().isEmpty() - ); - } - - @Test - public void testFailureDoesNotSetIsLoaded() throws Exception { - TextToSpeech tts = mock(TextToSpeech.class); - when(tts.setLanguage(any(Locale.class))).thenReturn(TextToSpeech.LANG_AVAILABLE); - - TTSManager ttsManager = new TTSManager(tts); - - TTSManager.TTSListener mockListener = mock(TTSManager.TTSListener.class); - ttsManager.setTTSListener(mockListener); - - ttsManager.getOnInitListener().onInit(TextToSpeech.ERROR); - - ttsManager.addQueue("text a"); - - verify(mockListener, times(2)).onError(anyString()); - } -} \ No newline at end of file diff --git a/mobile/src/test/java/mycroft/ai/TTSManagerTest.kt b/mobile/src/test/java/mycroft/ai/TTSManagerTest.kt new file mode 100644 index 00000000..7ec6e8bd --- /dev/null +++ b/mobile/src/test/java/mycroft/ai/TTSManagerTest.kt @@ -0,0 +1,61 @@ +package mycroft.ai + +import android.speech.tts.TextToSpeech +import io.mockk.impl.annotations.RelaxedMockK +import io.mockk.junit5.MockKExtension +import io.mockk.verify +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test + +import org.junit.jupiter.api.extension.ExtendWith + +@ExtendWith(MockKExtension::class) +class TTSManagerTest { + private val someSpeech = "some speech" + + private lateinit var subject: TTSManager + + @RelaxedMockK + private lateinit var textToSpeech: TextToSpeech + + @RelaxedMockK + private lateinit var ttsListener: TTSManager.TTSListener + + @BeforeEach + fun setUp() { + subject = TTSManager(textToSpeech) + } + + @Test + fun testAddQueue_BeforeIsLoaded_CallsOnError() { + subject.setTTSListener(ttsListener) + subject.addQueue(someSpeech) + + verify(exactly = 1) { ttsListener.onError(eq(subject.notInitializedErrorMessage)) } + } + + @Test + fun testAddQueue_WhenLoaded_DoesNotCallOnError() { + subject.onInitListener.onInit(TextToSpeech.SUCCESS) + verify(exactly = 0) { ttsListener.onError(any()) } + subject.addQueue(someSpeech) + verify(exactly = 0) { ttsListener.onError(any()) } + } + + @Test + fun testAddQueue_WhenLoaded_TriggersSpeak() { + subject.onInitListener.onInit(TextToSpeech.SUCCESS) + subject.addQueue(someSpeech) + verify(exactly = 1) { textToSpeech.speak(any(), any(), any()) } + } + + @Test + fun testInitFailure_DoesNotSetLoadedTrue() { + subject.setTTSListener(ttsListener) + subject.onInitListener.onInit(TextToSpeech.ERROR) + subject.addQueue(someSpeech) + verify(exactly = 1) { ttsListener.onError(eq(subject.initializationFailedErrorMessage)) } + verify(exactly = 1) { ttsListener.onError(eq(subject.notInitializedErrorMessage)) } + + } +} \ No newline at end of file