summaryrefslogtreecommitdiffstats
path: root/mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/ContentDelegateMultipleSessionsTest.kt
diff options
context:
space:
mode:
Diffstat (limited to 'mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/ContentDelegateMultipleSessionsTest.kt')
-rw-r--r--mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/ContentDelegateMultipleSessionsTest.kt156
1 files changed, 156 insertions, 0 deletions
diff --git a/mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/ContentDelegateMultipleSessionsTest.kt b/mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/ContentDelegateMultipleSessionsTest.kt
new file mode 100644
index 0000000000..c85e57eb81
--- /dev/null
+++ b/mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/ContentDelegateMultipleSessionsTest.kt
@@ -0,0 +1,156 @@
+/* -*- Mode: Java; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: nil; -*-
+ * Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+package org.mozilla.geckoview.test
+
+import androidx.annotation.AnyThread
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.MediumTest
+import org.hamcrest.Matchers.* // ktlint-disable no-wildcard-imports
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mozilla.geckoview.* // ktlint-disable no-wildcard-imports
+import org.mozilla.geckoview.GeckoSession.ContentDelegate
+import org.mozilla.geckoview.test.rule.GeckoSessionTestRule.AssertCalled
+import org.mozilla.geckoview.test.rule.GeckoSessionTestRule.IgnoreCrash
+
+@RunWith(AndroidJUnit4::class)
+@MediumTest
+class ContentDelegateMultipleSessionsTest : BaseSessionTest() {
+ val contentProcNameRegex = ".*:tab\\d+$".toRegex()
+
+ @AnyThread
+ fun killAllContentProcesses() {
+ val contentProcessPids = sessionRule.getAllSessionPids()
+ for (pid in contentProcessPids) {
+ sessionRule.killContentProcess(pid)
+ }
+ }
+
+ fun resetContentProcesses() {
+ val isMainSessionAlreadyOpen = mainSession.isOpen()
+ killAllContentProcesses()
+
+ if (isMainSessionAlreadyOpen) {
+ mainSession.waitUntilCalled(object : ContentDelegate {
+ @AssertCalled(count = 1)
+ override fun onKill(session: GeckoSession) {
+ }
+ })
+ }
+
+ mainSession.open()
+ }
+
+ fun getE10sProcessCount(): Int {
+ val extensionProcessPref = "extensions.webextensions.remote"
+ val isExtensionProcessEnabled = (sessionRule.getPrefs(extensionProcessPref)[0] as Boolean)
+ val e10sProcessCountPref = "dom.ipc.processCount"
+ var numContentProcesses = (sessionRule.getPrefs(e10sProcessCountPref)[0] as Int)
+
+ if (isExtensionProcessEnabled && numContentProcesses > 1) {
+ // Extension process counts against the content process budget
+ --numContentProcesses
+ }
+
+ return numContentProcesses
+ }
+
+ // This function ensures that a second GeckoSession that shares the same
+ // content process as mainSession is returned to the test:
+ //
+ // First, we assume that we're starting with a known initial state with respect
+ // to sessions and content processes:
+ // * mainSession is the only session, it is open, and its content process is the only
+ // content process (but note that the content process assigned to mainSession is
+ // *not* guaranteed to be ":tab0").
+ // * With multi-e10s configured to run N content processes, we create and open
+ // an additional N content processes. With the default e10s process allocation
+ // scheme, this means that the first N-1 new sessions we create each get their
+ // own content process. The Nth new session is assigned to the same content
+ // process as mainSession, which is the session we want to return to the test.
+ fun getSecondGeckoSession(): GeckoSession {
+ val numContentProcesses = getE10sProcessCount()
+
+ // If we change the content process allocation scheme, this function will need to be
+ // fixed to ensure that we still have two test sessions in at least one content
+ // process (with one of those sessions being mainSession).
+ val additionalSessions = Array(numContentProcesses) { _ -> sessionRule.createOpenSession() }
+
+ // The second session that shares a process with mainSession should be at
+ // the end of the array.
+ return additionalSessions.last()
+ }
+
+ @Before
+ fun setup() {
+ resetContentProcesses()
+ }
+
+ @IgnoreCrash
+ @Test
+ fun crashContentMultipleSessions() {
+ // We need to make sure all sessions in a given content process receive onCrash
+ // or onKill. To test this, we need to make sure we have two tabs sharing the same process.
+ val newSession = getSecondGeckoSession()
+
+ // We can inadvertently catch the `onCrash` call for the cached session if we don't specify
+ // individual sessions here. Therefore, assert 'onCrash' is called for the two sessions
+ // individually...
+ val mainSessionCrash = GeckoResult<Void>()
+ val newSessionCrash = GeckoResult<Void>()
+
+ // ...but we use GeckoResult.allOf for waiting on the aggregated results
+ val allCrashesFound = GeckoResult.allOf(mainSessionCrash, newSessionCrash)
+
+ sessionRule.delegateUntilTestEnd(object : ContentDelegate {
+ fun reportCrash(session: GeckoSession) {
+ if (session == mainSession) {
+ mainSessionCrash.complete(null)
+ } else if (session == newSession) {
+ newSessionCrash.complete(null)
+ }
+ }
+
+ // Slower devices may not catch crashes in a timely manner, so we check to see
+ // if either `onKill` or `onCrash` is called
+ override fun onCrash(session: GeckoSession) {
+ reportCrash(session)
+ }
+ override fun onKill(session: GeckoSession) {
+ reportCrash(session)
+ }
+ })
+
+ mainSession.loadUri(CONTENT_CRASH_URL)
+
+ sessionRule.waitForResult(allCrashesFound)
+ }
+
+ @IgnoreCrash
+ @Test
+ fun killContentMultipleSessions() {
+ val newSession = getSecondGeckoSession()
+
+ val mainSessionKilled = GeckoResult<Void>()
+ val newSessionKilled = GeckoResult<Void>()
+
+ val allKillEventsReceived = GeckoResult.allOf(mainSessionKilled, newSessionKilled)
+
+ sessionRule.delegateUntilTestEnd(object : ContentDelegate {
+ override fun onKill(session: GeckoSession) {
+ if (session == mainSession) {
+ mainSessionKilled.complete(null)
+ } else if (session == newSession) {
+ newSessionKilled.complete(null)
+ }
+ }
+ })
+
+ killAllContentProcesses()
+
+ sessionRule.waitForResult(allKillEventsReceived)
+ }
+}