fenix/app/src/test/java/org/mozilla/fenix/components/TrackingProtectionPolicyFac...

660 lines
26 KiB
Kotlin

/* 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.components
import io.mockk.every
import io.mockk.mockk
import io.mockk.mockkObject
import mozilla.components.concept.engine.EngineSession.TrackingProtectionPolicy
import mozilla.components.concept.engine.EngineSession.TrackingProtectionPolicy.CookiePolicy
import mozilla.components.support.test.robolectric.testContext
import org.junit.Assert.assertArrayEquals
import org.junit.Assert.assertEquals
import org.junit.Assert.assertFalse
import org.junit.Assert.assertTrue
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mozilla.fenix.Config
import org.mozilla.fenix.R
import org.mozilla.fenix.ReleaseChannel
import org.mozilla.fenix.helpers.FenixRobolectricTestRunner
import org.mozilla.fenix.utils.Settings
@RunWith(FenixRobolectricTestRunner::class)
class TrackingProtectionPolicyFactoryTest {
private lateinit var all: String
private lateinit var social: String
private lateinit var thirdParty: String
private lateinit var unvisited: String
private lateinit var private: String
@Before
fun setup() {
mockkObject(Config)
every { Config.channel } returns ReleaseChannel.Nightly
all = testContext.resources.getString(R.string.all)
social = testContext.resources.getString(R.string.social)
thirdParty = testContext.resources.getString(R.string.third_party)
unvisited = testContext.resources.getString(R.string.unvisited)
private = testContext.resources.getString(R.string.private_string)
}
@Test
fun `WHEN useStrictMode is true then SHOULD return strict mode`() {
val expected = TrackingProtectionPolicy.strict()
val factory = TrackingProtectionPolicyFactory(
mockSettings(useStrict = true),
testContext.resources
)
val privateOnly =
factory.createTrackingProtectionPolicy(normalMode = false, privateMode = true)
val normalOnly =
factory.createTrackingProtectionPolicy(normalMode = true, privateMode = false)
val always = factory.createTrackingProtectionPolicy(normalMode = true, privateMode = true)
val none = factory.createTrackingProtectionPolicy(normalMode = false, privateMode = false)
expected.assertPolicyEquals(privateOnly, checkPrivacy = false)
expected.assertPolicyEquals(normalOnly, checkPrivacy = false)
expected.assertPolicyEquals(always, checkPrivacy = false)
TrackingProtectionPolicy.none().assertPolicyEquals(none, checkPrivacy = false)
}
@Test
fun `WHEN neither use strict nor use custom is true SHOULD return recommended mode`() {
val expected = TrackingProtectionPolicy.recommended()
val factory = TrackingProtectionPolicyFactory(
mockSettings(useStrict = false, useCustom = false),
testContext.resources
)
val privateOnly =
factory.createTrackingProtectionPolicy(normalMode = false, privateMode = true)
val normalOnly =
factory.createTrackingProtectionPolicy(normalMode = true, privateMode = false)
val always = factory.createTrackingProtectionPolicy(normalMode = true, privateMode = true)
val none = factory.createTrackingProtectionPolicy(normalMode = false, privateMode = false)
expected.assertPolicyEquals(privateOnly, checkPrivacy = false)
expected.assertPolicyEquals(normalOnly, checkPrivacy = false)
expected.assertPolicyEquals(always, checkPrivacy = false)
TrackingProtectionPolicy.none().assertPolicyEquals(none, checkPrivacy = false)
}
@Test
fun `GIVEN custom policy WHEN should not block cookies THEN tracking policy should not block cookies`() {
val expected = TrackingProtectionPolicy.select(
cookiePolicy = TrackingProtectionPolicy.CookiePolicy.ACCEPT_ALL,
trackingCategories = allTrackingCategories
)
val factory = TrackingProtectionPolicyFactory(
settingsForCustom(shouldBlockCookiesInCustom = false),
testContext.resources
)
val privateOnly =
factory.createTrackingProtectionPolicy(normalMode = false, privateMode = true)
val normalOnly =
factory.createTrackingProtectionPolicy(normalMode = true, privateMode = false)
val always = factory.createTrackingProtectionPolicy(normalMode = true, privateMode = true)
expected.assertPolicyEquals(privateOnly, checkPrivacy = false)
expected.assertPolicyEquals(normalOnly, checkPrivacy = false)
expected.assertPolicyEquals(always, checkPrivacy = false)
}
@Test
fun `GIVEN custom policy WHEN cookie policy block all THEN tracking policy should have cookie policy allow none`() {
val expected = TrackingProtectionPolicy.select(
cookiePolicy = TrackingProtectionPolicy.CookiePolicy.ACCEPT_NONE,
trackingCategories = allTrackingCategories
)
val factory = TrackingProtectionPolicyFactory(
settingsForCustom(
shouldBlockCookiesInCustom = true,
blockCookiesSelection = all
),
testContext.resources
)
val privateOnly =
factory.createTrackingProtectionPolicy(normalMode = false, privateMode = true)
val normalOnly =
factory.createTrackingProtectionPolicy(normalMode = true, privateMode = false)
val always = factory.createTrackingProtectionPolicy(normalMode = true, privateMode = true)
expected.assertPolicyEquals(privateOnly, checkPrivacy = false)
expected.assertPolicyEquals(normalOnly, checkPrivacy = false)
expected.assertPolicyEquals(always, checkPrivacy = false)
}
@Test
fun `GIVEN TCP is enabled by nimbus WHEN applyTCPIfNeeded THEN cookie policy should be TCP`() {
val settings: Settings = mockk(relaxed = true)
every { settings.enabledTotalCookieProtection } returns true
val policies = arrayOf(
TrackingProtectionPolicy.strict(), TrackingProtectionPolicy.recommended(),
TrackingProtectionPolicy.select()
)
for (policy in policies) {
val adaptedPolicy = policy.applyTCPIfNeeded(settings)
assertEquals(
CookiePolicy.ACCEPT_FIRST_PARTY_AND_ISOLATE_OTHERS,
adaptedPolicy.cookiePolicy
)
}
}
fun `GIVEN TCP is NOT enabled by nimbus WHEN applyTCPIfNeeded THEN reuse cookie policy`() {
val settings: Settings = mockk(relaxed = true)
every { settings.enabledTotalCookieProtection } returns false
val policies = arrayOf(
TrackingProtectionPolicy.strict(), TrackingProtectionPolicy.recommended(),
TrackingProtectionPolicy.select()
)
for (policy in policies) {
val adaptedPolicy = policy.applyTCPIfNeeded(settings)
assertEquals(
policy.cookiePolicy,
adaptedPolicy.cookiePolicy
)
}
}
@Test
fun `GIVEN custom policy WHEN cookie policy social THEN tracking policy should have cookie policy allow non-trackers`() {
val expected = TrackingProtectionPolicy.select(
cookiePolicy = TrackingProtectionPolicy.CookiePolicy.ACCEPT_NON_TRACKERS,
trackingCategories = allTrackingCategories
)
val factory = TrackingProtectionPolicyFactory(
settingsForCustom(
shouldBlockCookiesInCustom = true,
blockCookiesSelection = social
),
testContext.resources
)
val privateOnly =
factory.createTrackingProtectionPolicy(normalMode = false, privateMode = true)
val normalOnly =
factory.createTrackingProtectionPolicy(normalMode = true, privateMode = false)
val always = factory.createTrackingProtectionPolicy(normalMode = true, privateMode = true)
expected.assertPolicyEquals(privateOnly, checkPrivacy = false)
expected.assertPolicyEquals(normalOnly, checkPrivacy = false)
expected.assertPolicyEquals(always, checkPrivacy = false)
}
@Test
fun `GIVEN custom policy WHEN cookie policy accept visited THEN tracking policy should have cookie policy allow visited`() {
val expected = TrackingProtectionPolicy.select(
cookiePolicy = TrackingProtectionPolicy.CookiePolicy.ACCEPT_VISITED,
trackingCategories = allTrackingCategories
)
val factory = TrackingProtectionPolicyFactory(
settingsForCustom(
shouldBlockCookiesInCustom = true,
blockCookiesSelection = unvisited
),
testContext.resources
)
val privateOnly =
factory.createTrackingProtectionPolicy(normalMode = false, privateMode = true)
val normalOnly =
factory.createTrackingProtectionPolicy(normalMode = true, privateMode = false)
val always = factory.createTrackingProtectionPolicy(normalMode = true, privateMode = true)
expected.assertPolicyEquals(privateOnly, checkPrivacy = false)
expected.assertPolicyEquals(normalOnly, checkPrivacy = false)
expected.assertPolicyEquals(always, checkPrivacy = false)
}
@Test
fun `GIVEN custom policy WHEN cookie policy block third party THEN tracking policy should have cookie policy allow first party`() {
val expected = TrackingProtectionPolicy.select(
cookiePolicy = TrackingProtectionPolicy.CookiePolicy.ACCEPT_ONLY_FIRST_PARTY,
trackingCategories = allTrackingCategories
)
val factory = TrackingProtectionPolicyFactory(
settingsForCustom(
shouldBlockCookiesInCustom = true,
blockCookiesSelection = thirdParty
),
testContext.resources
)
val privateOnly =
factory.createTrackingProtectionPolicy(normalMode = false, privateMode = true)
val normalOnly =
factory.createTrackingProtectionPolicy(normalMode = true, privateMode = false)
val always = factory.createTrackingProtectionPolicy(normalMode = true, privateMode = true)
expected.assertPolicyEquals(privateOnly, checkPrivacy = false)
expected.assertPolicyEquals(normalOnly, checkPrivacy = false)
expected.assertPolicyEquals(always, checkPrivacy = false)
}
@Test
fun `GIVEN custom policy WHEN cookie policy unrecognized THEN tracking policy should have cookie policy block all`() {
val expected = TrackingProtectionPolicy.select(
cookiePolicy = TrackingProtectionPolicy.CookiePolicy.ACCEPT_NONE,
trackingCategories = allTrackingCategories
)
val factory = TrackingProtectionPolicyFactory(
settingsForCustom(
shouldBlockCookiesInCustom = true,
blockCookiesSelection = "some text!"
),
testContext.resources
)
val privateOnly =
factory.createTrackingProtectionPolicy(normalMode = false, privateMode = true)
val normalOnly =
factory.createTrackingProtectionPolicy(normalMode = true, privateMode = false)
val always = factory.createTrackingProtectionPolicy(normalMode = true, privateMode = true)
expected.assertPolicyEquals(privateOnly, checkPrivacy = false)
expected.assertPolicyEquals(normalOnly, checkPrivacy = false)
expected.assertPolicyEquals(always, checkPrivacy = false)
}
@Test
fun `all cookies_options_entry_values values should create policies without crashing`() {
testContext.resources.getStringArray(R.array.cookies_options_entry_values).forEach {
TrackingProtectionPolicyFactory(
settingsForCustom(
shouldBlockCookiesInCustom = true,
blockCookiesSelection = it
),
testContext.resources
)
.createTrackingProtectionPolicy(normalMode = true, privateMode = true)
}
}
@Test
fun `factory should construct policies with privacy settings that match their inputs`() {
val allFactories = listOf(
TrackingProtectionPolicyFactory(
mockSettings(useStrict = true),
testContext.resources
),
TrackingProtectionPolicyFactory(
mockSettings(useStrict = false, useCustom = false),
testContext.resources
)
)
allFactories.map {
it.createTrackingProtectionPolicy(normalMode = true, privateMode = false)
}.forEach {
assertTrue(it.useForRegularSessions)
assertFalse(it.useForPrivateSessions)
}
allFactories.map {
it.createTrackingProtectionPolicy(normalMode = false, privateMode = true)
}.forEach {
assertTrue(it.useForPrivateSessions)
assertFalse(it.useForRegularSessions)
}
allFactories.map {
it.createTrackingProtectionPolicy(normalMode = true, privateMode = true)
}.forEach {
assertTrue(it.useForRegularSessions)
assertTrue(it.useForPrivateSessions)
}
// `normalMode = true, privateMode = true` can never be shown to the user
}
@Test
fun `factory should follow global ETP settings by default`() {
var useETPFactory = TrackingProtectionPolicyFactory(
mockSettings(useTrackingProtection = true),
testContext.resources
)
var policy = useETPFactory.createTrackingProtectionPolicy()
assertTrue(policy.useForPrivateSessions)
assertTrue(policy.useForRegularSessions)
useETPFactory = TrackingProtectionPolicyFactory(
mockSettings(useTrackingProtection = false),
testContext.resources
)
policy = useETPFactory.createTrackingProtectionPolicy()
assertEquals(policy, TrackingProtectionPolicy.none())
}
@Test
fun `custom tabs should respect their privacy rules`() {
val allSettings = listOf(
settingsForCustom(
shouldBlockCookiesInCustom = false,
blockTrackingContentInCustom = all
),
settingsForCustom(
shouldBlockCookiesInCustom = true,
blockCookiesSelection = all,
blockTrackingContentInCustom = all
),
settingsForCustom(
shouldBlockCookiesInCustom = true,
blockCookiesSelection = all,
blockTrackingContentInCustom = all
),
settingsForCustom(
shouldBlockCookiesInCustom = true,
blockCookiesSelection = unvisited,
blockTrackingContentInCustom = all
),
settingsForCustom(
shouldBlockCookiesInCustom = true,
blockCookiesSelection = thirdParty,
blockTrackingContentInCustom = all
),
settingsForCustom(
shouldBlockCookiesInCustom = true,
blockCookiesSelection = "some text!",
blockTrackingContentInCustom = all
)
)
val privateSettings = listOf(
settingsForCustom(
shouldBlockCookiesInCustom = false,
blockTrackingContentInCustom = private
),
settingsForCustom(
shouldBlockCookiesInCustom = true,
blockCookiesSelection = all,
blockTrackingContentInCustom = private
),
settingsForCustom(
shouldBlockCookiesInCustom = true,
blockCookiesSelection = all,
blockTrackingContentInCustom = private
),
settingsForCustom(
shouldBlockCookiesInCustom = true,
blockCookiesSelection = unvisited,
blockTrackingContentInCustom = private
),
settingsForCustom(
shouldBlockCookiesInCustom = true,
blockCookiesSelection = thirdParty,
blockTrackingContentInCustom = private
),
settingsForCustom(
shouldBlockCookiesInCustom = true,
blockCookiesSelection = "some text!",
blockTrackingContentInCustom = private
)
)
allSettings.map {
TrackingProtectionPolicyFactory(
it,
testContext.resources
).createTrackingProtectionPolicy(
normalMode = true,
privateMode = true
)
}
.forEach {
assertTrue(it.useForRegularSessions)
assertTrue(it.useForPrivateSessions)
}
privateSettings.map {
TrackingProtectionPolicyFactory(
it,
testContext.resources
).createTrackingProtectionPolicy(
normalMode = true,
privateMode = true
)
}
.forEach {
assertFalse(it.useForRegularSessions)
assertTrue(it.useForPrivateSessions)
}
}
@Test
fun `GIVEN custom policy WHEN default tracking policies THEN tracking policies should match default`() {
val defaultTrackingCategories = arrayOf(
TrackingProtectionPolicy.TrackingCategory.AD,
TrackingProtectionPolicy.TrackingCategory.ANALYTICS,
TrackingProtectionPolicy.TrackingCategory.SOCIAL,
TrackingProtectionPolicy.TrackingCategory.MOZILLA_SOCIAL
)
val expected = TrackingProtectionPolicy.select(
cookiePolicy = TrackingProtectionPolicy.CookiePolicy.ACCEPT_NONE,
trackingCategories = defaultTrackingCategories
)
val factory = TrackingProtectionPolicyFactory(
settingsForCustom(
shouldBlockCookiesInCustom = true,
blockTrackingContent = false,
blockFingerprinters = false,
blockCryptominers = false
),
testContext.resources
)
val actual = factory.createTrackingProtectionPolicy(normalMode = true, privateMode = true)
expected.assertPolicyEquals(actual, checkPrivacy = false)
}
@Test
fun `GIVEN custom policy WHEN all tracking policies THEN tracking policies should match all`() {
val expected = TrackingProtectionPolicy.select(
cookiePolicy = TrackingProtectionPolicy.CookiePolicy.ACCEPT_NONE,
trackingCategories = allTrackingCategories
)
val factory = TrackingProtectionPolicyFactory(
settingsForCustom(
shouldBlockCookiesInCustom = true,
blockTrackingContent = true,
blockFingerprinters = true,
blockCryptominers = true
),
testContext.resources
)
val actual = factory.createTrackingProtectionPolicy(normalMode = true, privateMode = true)
expected.assertPolicyEquals(actual, checkPrivacy = false)
}
@Test
fun `GIVEN custom policy WHEN some tracking policies THEN tracking policies should match passed policies`() {
val someTrackingCategories = arrayOf(
TrackingProtectionPolicy.TrackingCategory.AD,
TrackingProtectionPolicy.TrackingCategory.ANALYTICS,
TrackingProtectionPolicy.TrackingCategory.SOCIAL,
TrackingProtectionPolicy.TrackingCategory.MOZILLA_SOCIAL,
TrackingProtectionPolicy.TrackingCategory.FINGERPRINTING
)
val expected = TrackingProtectionPolicy.select(
cookiePolicy = TrackingProtectionPolicy.CookiePolicy.ACCEPT_NONE,
trackingCategories = someTrackingCategories
)
val factory = TrackingProtectionPolicyFactory(
settingsForCustom(
shouldBlockCookiesInCustom = true,
blockTrackingContent = false,
blockFingerprinters = true,
blockCryptominers = false,
blockRedirectTrackers = true
),
testContext.resources
)
val actual = factory.createTrackingProtectionPolicy(normalMode = true, privateMode = true)
expected.assertPolicyEquals(actual, checkPrivacy = false)
}
@Test
fun `GIVEN custom policy WHEN some tracking policies THEN purge cookies`() {
val expected = TrackingProtectionPolicy.select(
cookiePolicy = TrackingProtectionPolicy.CookiePolicy.ACCEPT_NONE,
trackingCategories = allTrackingCategories,
cookiePurging = true
)
val factory = TrackingProtectionPolicyFactory(
settingsForCustom(shouldBlockCookiesInCustom = true),
testContext.resources
)
val privateOnly =
factory.createTrackingProtectionPolicy(normalMode = false, privateMode = true)
val normalOnly =
factory.createTrackingProtectionPolicy(normalMode = true, privateMode = false)
val always = factory.createTrackingProtectionPolicy(normalMode = true, privateMode = true)
expected.assertPolicyEquals(privateOnly, checkPrivacy = false)
expected.assertPolicyEquals(normalOnly, checkPrivacy = false)
expected.assertPolicyEquals(always, checkPrivacy = false)
}
@Test
fun `GIVEN strict policy WHEN some tracking policies THEN purge cookies`() {
val expected = TrackingProtectionPolicy.strict()
val factory = TrackingProtectionPolicyFactory(
mockSettings(
useStrict = true,
useTrackingProtection = true
),
testContext.resources
)
val privateOnly =
factory.createTrackingProtectionPolicy(normalMode = false, privateMode = true)
val normalOnly =
factory.createTrackingProtectionPolicy(normalMode = true, privateMode = false)
val always = factory.createTrackingProtectionPolicy(normalMode = true, privateMode = true)
assertEquals(privateOnly.cookiePurging, expected.cookiePurging)
assertEquals(normalOnly.cookiePurging, expected.cookiePurging)
assertEquals(always.cookiePurging, expected.cookiePurging)
}
@Test
fun `GIVEN standard policy WHEN some tracking policies THEN purge cookies`() {
val expected = TrackingProtectionPolicy.recommended()
val factory = TrackingProtectionPolicyFactory(
mockSettings(
useStrict = false,
useCustom = false,
useTrackingProtection = true
),
testContext.resources
)
val privateOnly =
factory.createTrackingProtectionPolicy(normalMode = false, privateMode = true)
val normalOnly =
factory.createTrackingProtectionPolicy(normalMode = true, privateMode = false)
val always = factory.createTrackingProtectionPolicy(normalMode = true, privateMode = true)
assertEquals(privateOnly.cookiePurging, expected.cookiePurging)
assertEquals(normalOnly.cookiePurging, expected.cookiePurging)
assertEquals(always.cookiePurging, expected.cookiePurging)
}
private fun mockSettings(
useStrict: Boolean = false,
useCustom: Boolean = false,
useTrackingProtection: Boolean = false
): Settings = mockk {
every { enabledTotalCookieProtection } returns false
every { useStrictTrackingProtection } returns useStrict
every { useCustomTrackingProtection } returns useCustom
every { shouldUseTrackingProtection } returns useTrackingProtection
}
@Suppress("LongParameterList")
private fun settingsForCustom(
shouldBlockCookiesInCustom: Boolean,
blockTrackingContentInCustom: String = all, // ["private", "all"]
blockCookiesSelection: String = all, // values from R.array.cookies_options_entry_values
blockTrackingContent: Boolean = true,
blockFingerprinters: Boolean = true,
blockCryptominers: Boolean = true,
blockRedirectTrackers: Boolean = true
): Settings = mockSettings(useStrict = false, useCustom = true).apply {
every { blockTrackingContentSelectionInCustomTrackingProtection } returns blockTrackingContentInCustom
every { blockCookiesInCustomTrackingProtection } returns shouldBlockCookiesInCustom
every { blockCookiesSelectionInCustomTrackingProtection } returns blockCookiesSelection
every { blockTrackingContentInCustomTrackingProtection } returns blockTrackingContent
every { blockFingerprintersInCustomTrackingProtection } returns blockFingerprinters
every { blockCryptominersInCustomTrackingProtection } returns blockCryptominers
every { blockRedirectTrackersInCustomTrackingProtection } returns blockRedirectTrackers
}
private fun TrackingProtectionPolicy.assertPolicyEquals(
actual: TrackingProtectionPolicy,
checkPrivacy: Boolean
) {
assertEquals(this.cookiePolicy, actual.cookiePolicy)
assertEquals(this.strictSocialTrackingProtection, actual.strictSocialTrackingProtection)
// E.g., atm, RECOMMENDED == AD + ANALYTICS + SOCIAL + TEST + MOZILLA_SOCIAL + CRYPTOMINING.
// If all of these are set manually, the equality check should not fail
if (this.trackingCategories.toInt() != actual.trackingCategories.toInt()) {
assertArrayEquals(this.trackingCategories, actual.trackingCategories)
}
if (checkPrivacy) {
assertEquals(this.useForPrivateSessions, actual.useForPrivateSessions)
assertEquals(this.useForRegularSessions, actual.useForRegularSessions)
}
}
private fun Array<TrackingProtectionPolicy.TrackingCategory>.toInt(): Int {
return fold(initial = 0) { acc, next -> acc + next.id }
}
private val allTrackingCategories = arrayOf(
TrackingProtectionPolicy.TrackingCategory.AD,
TrackingProtectionPolicy.TrackingCategory.ANALYTICS,
TrackingProtectionPolicy.TrackingCategory.SOCIAL,
TrackingProtectionPolicy.TrackingCategory.MOZILLA_SOCIAL,
TrackingProtectionPolicy.TrackingCategory.SCRIPTS_AND_SUB_RESOURCES,
TrackingProtectionPolicy.TrackingCategory.FINGERPRINTING,
TrackingProtectionPolicy.TrackingCategory.CRYPTOMINING
)
}