1
0
Fork 0

Add tests for StrictModeManager (#12013)

master
Tiger Oakes 2020-06-29 08:17:07 -07:00 committed by GitHub
parent 9d3dfd2a7e
commit 6bde0378a2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 192 additions and 43 deletions

View File

@ -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<String>().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)
}

View File

@ -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

View File

@ -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<FragmentManager.FragmentLifecycleCallbacks>()
StrictModeManager.changeStrictModePolicies(fragmentManager)
verify { fragmentManager.registerFragmentLifecycleCallbacks(capture(callbacks), false) }
confirmVerified(fragmentManager)
callbacks.captured.onFragmentResumed(fragmentManager, mockk())
verify { fragmentManager.unregisterFragmentLifecycleCallbacks(callbacks.captured) }
}
}

View File

@ -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)
}
}