# 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/.
import os
import sys
# add this directory to the path
sys.path.append(os.path.dirname(__file__))
from marionette_driver import Wait
from session_store_test_case import SessionStoreTestCase
def wait_for_fog(marionette):
# Glean's blocking test APIs (testGetValue) park the main thread forever if
# Glean is still pre-init, and FOG is initialized from a startup idle task,
# so it can lag the point where the browser reports itself started up.
Wait(marionette, timeout=60).until(
lambda _: marionette.execute_script(
"""
return Services.fog.initialized;
"""
),
message="FOG should be initialized before reading Glean metrics.",
)
def inline(title):
return f"data:text/html;charset=utf-8,
{title}"
TEST_WINDOWS = set([
(
inline("Page 1"),
inline("Page 2"),
),
])
class RestoreCountsTestCase(SessionStoreTestCase):
"""The number_of_* counts are recorded on every restore attempt, including
attempts that end up restoring nothing."""
def _quit_and_restore(self):
self.marionette.quit()
self.marionette.start_session()
self.marionette.set_context("chrome")
def _read_counts(self):
wait_for_fog(self.marionette)
# On a normal restore these are recorded from a deadline timer that
# runs COLLECT_RESULTS_AFTER_MS after the last window was restored,
# so they aren't there the moment the session starts.
Wait(self.marionette, timeout=40).until(
lambda _: self.marionette.execute_script(
"""
return Glean.sessionRestore.numberOfTabsRestored.testGetValue()
!= null;
"""
),
message="The restore counts should be recorded.",
)
return self.marionette.execute_script(
"""
let counts = {};
for (let name of [
"numberOfTabsRestored",
"numberOfWindowsRestored",
"numberOfEagerTabsRestored",
]) {
counts[name] = Glean.sessionRestore[name].testGetValue();
}
return counts;
"""
)
class TestCountsAfterRestore(RestoreCountsTestCase):
def setUp(self):
super().setUp(
startup_page=3,
include_private=False,
restore_on_demand=False,
test_windows=TEST_WINDOWS,
)
def test_counts_are_recorded_when_tabs_are_restored(self):
self._quit_and_restore()
counts = self._read_counts()
for name, value in counts.items():
self.assertIsNotNone(value, f"{name} should be recorded.")
self.assertGreater(
counts["numberOfTabsRestored"]["sum"],
0,
"Tabs were restored, so the count is not zero.",
)
class TestCountsWhenNothingRestored(RestoreCountsTestCase):
def setUp(self):
super().setUp(
startup_page=3,
include_private=False,
restore_on_demand=False,
test_windows=TEST_WINDOWS,
)
def test_counts_are_recorded_when_no_window_is_restored(self):
# A session whose only windows are in _closedWindows restores no
# window at all, so the deadline timer that normally reports these
# counts is never started. Real sessions reach this on macOS, where
# quitting after closing the last window saves it only in
# _closedWindows. Build that state from the live session, and since
# SessionStore rewrites sessionstore.jsonlz4 on shutdown, write it to
# a separate file and swap it in once the browser has quit.
self.marionette.execute_async_script(
"""
let [resolve] = arguments;
const { SessionStore } = ChromeUtils.importESModule(
"moz-src:///browser/components/sessionstore/SessionStore.sys.mjs"
);
let state = JSON.parse(SessionStore.getBrowserState());
state._closedWindows = state.windows;
state.windows = [];
IOUtils.writeJSON(
PathUtils.join(PathUtils.profileDir, "restore-counts-fixture.jsonlz4"),
state,
{ compress: true }
).then(resolve);
"""
)
self.marionette.quit()
profile_dir = self.marionette.instance.profile.profile
os.replace(
os.path.join(profile_dir, "restore-counts-fixture.jsonlz4"),
os.path.join(profile_dir, "sessionstore.jsonlz4"),
)
self.marionette.start_session()
self.marionette.set_context("chrome")
counts = self._read_counts()
for name, value in counts.items():
self.assertIsNotNone(
value, f"{name} should be recorded even with nothing restored."
)
self.assertEqual(
value["sum"], 0, f"{name} should report zero, not be absent."
)