From 6bde0378a223b74f9c22e5e664a250ff7534dc11 Mon Sep 17 00:00:00 2001 From: Tiger Oakes Date: Mon, 29 Jun 2020 08:17:07 -0700 Subject: [PATCH] Add tests for StrictModeManager (#12013) --- .../org/mozilla/fenix/StrictModeManager.kt | 82 +++++++++---------- .../java/org/mozilla/fenix/ext/StrictMode.kt | 1 + .../mozilla/fenix/StrictModeManagerTest.kt | 73 +++++++++++++++++ .../org/mozilla/fenix/ext/StrictModeTest.kt | 79 ++++++++++++++++++ 4 files changed, 192 insertions(+), 43 deletions(-) create mode 100644 app/src/test/java/org/mozilla/fenix/StrictModeManagerTest.kt create mode 100644 app/src/test/java/org/mozilla/fenix/ext/StrictModeTest.kt diff --git a/app/src/main/java/org/mozilla/fenix/StrictModeManager.kt b/app/src/main/java/org/mozilla/fenix/StrictModeManager.kt index 16f788679..5ff1932bb 100644 --- a/app/src/main/java/org/mozilla/fenix/StrictModeManager.kt +++ b/app/src/main/java/org/mozilla/fenix/StrictModeManager.kt @@ -1,6 +1,6 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public
 -* License, v. 2.0. If a copy of the MPL was not distributed with this
 -* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ package org.mozilla.fenix @@ -8,7 +8,6 @@ import android.os.Build import android.os.StrictMode import androidx.fragment.app.Fragment import androidx.fragment.app.FragmentManager -import kotlin.collections.HashSet /** * Manages strict mode settings for the application. @@ -16,38 +15,39 @@ import kotlin.collections.HashSet object StrictModeManager { /*** - * Enables strict mode for debug purposes. meant to be run only in the main process. - * @param setPenaltyDialog boolean value to decide setting the dialog box as a penalty. - */ - fun enableStrictMode(setPenaltyDialog: Boolean) { - if (Config.channel.isDebug) { - val threadPolicy = StrictMode.ThreadPolicy.Builder() - .detectAll() - .penaltyLog() - if (setPenaltyDialog && - !strictModeExceptionList.contains(Build.MANUFACTURER)) { - threadPolicy.penaltyDialog() - } - StrictMode.setThreadPolicy(threadPolicy.build()) - var builder = StrictMode.VmPolicy.Builder() - .detectLeakedSqlLiteObjects() - .detectLeakedClosableObjects() - .detectLeakedRegistrationObjects() - .detectActivityLeaks() - .detectFileUriExposure() - .penaltyLog() - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) builder = - builder.detectContentUriWithoutPermission() - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { - if (setPenaltyDialog) { - builder.permitNonSdkApiUsage() - } else { - builder.detectNonSdkApiUsage() - } - } - StrictMode.setVmPolicy(builder.build()) + * Enables strict mode for debug purposes. meant to be run only in the main process. + * @param setPenaltyDialog boolean value to decide setting the dialog box as a penalty. + */ + fun enableStrictMode(setPenaltyDialog: Boolean) { + if (Config.channel.isDebug) { + val threadPolicy = StrictMode.ThreadPolicy.Builder() + .detectAll() + .penaltyLog() + if (setPenaltyDialog && Build.MANUFACTURER !in strictModeExceptionList) { + threadPolicy.penaltyDialog() } + StrictMode.setThreadPolicy(threadPolicy.build()) + + val builder = StrictMode.VmPolicy.Builder() + .detectLeakedSqlLiteObjects() + .detectLeakedClosableObjects() + .detectLeakedRegistrationObjects() + .detectActivityLeaks() + .detectFileUriExposure() + .penaltyLog() + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + builder.detectContentUriWithoutPermission() + } + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { + if (setPenaltyDialog) { + builder.permitNonSdkApiUsage() + } else { + builder.detectNonSdkApiUsage() + } + } + StrictMode.setVmPolicy(builder.build()) } + } /** * Revert strict mode to disable penalty dialog. Tied to fragment lifecycle since strict mode @@ -55,8 +55,7 @@ object StrictModeManager { * specific fragment. */ fun changeStrictModePolicies(fragmentManager: FragmentManager) { - fragmentManager.registerFragmentLifecycleCallbacks(object : - FragmentManager.FragmentLifecycleCallbacks() { + fragmentManager.registerFragmentLifecycleCallbacks(object : FragmentManager.FragmentLifecycleCallbacks() { override fun onFragmentResumed(fm: FragmentManager, f: Fragment) { enableStrictMode(false) fm.unregisterFragmentLifecycleCallbacks(this) @@ -64,6 +63,9 @@ object StrictModeManager { }, false) } + private const val MANUFACTURE_HUAWEI: String = "HUAWEI" + private const val MANUFACTURE_ONE_PLUS: String = "OnePlus" + /** * There are certain manufacturers that have custom font classes for the OS systems. * These classes violates the [StrictMode] policies on startup. As a workaround, we create @@ -71,11 +73,5 @@ object StrictModeManager { * To add a new manufacturer to the list, log "Build.MANUFACTURER" from the device to get the * exact name of the manufacturer. */ - private val strictModeExceptionList = HashSet().also { - it.add(MANUFACTURE_HUAWEI) - it.add(MANUFACTURE_ONE_PLUS) - } - - private const val MANUFACTURE_HUAWEI: String = "HUAWEI" - private const val MANUFACTURE_ONE_PLUS: String = "OnePlus" + private val strictModeExceptionList = setOf(MANUFACTURE_HUAWEI, MANUFACTURE_ONE_PLUS) } diff --git a/app/src/main/java/org/mozilla/fenix/ext/StrictMode.kt b/app/src/main/java/org/mozilla/fenix/ext/StrictMode.kt index daf344933..ecb73dc39 100644 --- a/app/src/main/java/org/mozilla/fenix/ext/StrictMode.kt +++ b/app/src/main/java/org/mozilla/fenix/ext/StrictMode.kt @@ -1,6 +1,7 @@ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + package org.mozilla.fenix.ext import android.os.StrictMode diff --git a/app/src/test/java/org/mozilla/fenix/StrictModeManagerTest.kt b/app/src/test/java/org/mozilla/fenix/StrictModeManagerTest.kt new file mode 100644 index 000000000..0ddd003d9 --- /dev/null +++ b/app/src/test/java/org/mozilla/fenix/StrictModeManagerTest.kt @@ -0,0 +1,73 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +package org.mozilla.fenix + +import android.os.StrictMode +import androidx.fragment.app.FragmentManager +import io.mockk.MockKAnnotations +import io.mockk.confirmVerified +import io.mockk.every +import io.mockk.impl.annotations.MockK +import io.mockk.mockk +import io.mockk.mockkObject +import io.mockk.mockkStatic +import io.mockk.slot +import io.mockk.unmockkObject +import io.mockk.unmockkStatic +import io.mockk.verify +import org.junit.After +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.mozilla.fenix.helpers.FenixRobolectricTestRunner + +@RunWith(FenixRobolectricTestRunner::class) +class StrictModeManagerTest { + + @MockK(relaxUnitFun = true) private lateinit var fragmentManager: FragmentManager + + @Before + fun setup() { + MockKAnnotations.init(this) + mockkStatic(StrictMode::class) + mockkObject(Config) + } + + @After + fun teardown() { + unmockkStatic(StrictMode::class) + unmockkObject(Config) + } + + @Test + fun `test enableStrictMode in release`() { + every { Config.channel } returns ReleaseChannel.FenixProduction + StrictModeManager.enableStrictMode(false) + + verify(exactly = 0) { StrictMode.setThreadPolicy(any()) } + verify(exactly = 0) { StrictMode.setVmPolicy(any()) } + } + + @Test + fun `test enableStrictMode in debug`() { + every { Config.channel } returns ReleaseChannel.FenixDebug + StrictModeManager.enableStrictMode(false) + + verify { StrictMode.setThreadPolicy(any()) } + verify { StrictMode.setVmPolicy(any()) } + } + + @Test + fun `test changeStrictModePolicies`() { + val callbacks = slot() + + StrictModeManager.changeStrictModePolicies(fragmentManager) + verify { fragmentManager.registerFragmentLifecycleCallbacks(capture(callbacks), false) } + confirmVerified(fragmentManager) + + callbacks.captured.onFragmentResumed(fragmentManager, mockk()) + verify { fragmentManager.unregisterFragmentLifecycleCallbacks(callbacks.captured) } + } +} diff --git a/app/src/test/java/org/mozilla/fenix/ext/StrictModeTest.kt b/app/src/test/java/org/mozilla/fenix/ext/StrictModeTest.kt new file mode 100644 index 000000000..515949d2a --- /dev/null +++ b/app/src/test/java/org/mozilla/fenix/ext/StrictModeTest.kt @@ -0,0 +1,79 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +package org.mozilla.fenix.ext + +import android.os.StrictMode +import io.mockk.Runs +import io.mockk.every +import io.mockk.just +import io.mockk.mockk +import io.mockk.mockkObject +import io.mockk.mockkStatic +import io.mockk.unmockkObject +import io.mockk.unmockkStatic +import io.mockk.verify +import org.junit.After +import org.junit.Assert.assertEquals +import org.junit.Assert.assertNotNull +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.mozilla.fenix.Config +import org.mozilla.fenix.ReleaseChannel +import org.mozilla.fenix.helpers.FenixRobolectricTestRunner + +@RunWith(FenixRobolectricTestRunner::class) +class StrictModeTest { + + private lateinit var threadPolicy: StrictMode.ThreadPolicy + private lateinit var functionBlock: () -> String + + @Before + fun setup() { + threadPolicy = StrictMode.ThreadPolicy.LAX + functionBlock = mockk() + mockkStatic(StrictMode::class) + mockkObject(Config) + + every { StrictMode.setThreadPolicy(threadPolicy) } just Runs + every { functionBlock() } returns "Hello world" + } + + @After + fun teardown() { + unmockkStatic(StrictMode::class) + unmockkObject(Config) + } + + @Test + fun `runs function block in release`() { + every { Config.channel } returns ReleaseChannel.FenixProduction + assertEquals("Hello world", threadPolicy.resetPoliciesAfter(functionBlock)) + verify(exactly = 0) { StrictMode.setThreadPolicy(any()) } + } + + @Test + fun `runs function block in debug`() { + every { Config.channel } returns ReleaseChannel.FenixDebug + assertEquals("Hello world", threadPolicy.resetPoliciesAfter(functionBlock)) + verify { StrictMode.setThreadPolicy(threadPolicy) } + } + + @Test + fun `sets thread policy even if function throws`() { + every { Config.channel } returns ReleaseChannel.FenixDebug + every { functionBlock() } throws IllegalStateException() + var exception: IllegalStateException? = null + + try { + threadPolicy.resetPoliciesAfter(functionBlock) + } catch (e: IllegalStateException) { + exception = e + } + + verify { StrictMode.setThreadPolicy(threadPolicy) } + assertNotNull(exception) + } +}