Skip to content

Commit

Permalink
test: Implement Firefox testing with CDP
Browse files Browse the repository at this point in the history
Closes #12971
  • Loading branch information
marusak committed Nov 11, 2019
1 parent 687b83a commit 5ac0eb7
Show file tree
Hide file tree
Showing 17 changed files with 594 additions and 48 deletions.
78 changes: 63 additions & 15 deletions test/common/cdp.py
100755 → 100644
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,26 @@
TEST_DIR = os.path.normpath(os.path.dirname(os.path.realpath(os.path.join(__file__, ".."))))


def browser_path():
"""Return path to CDP browser.
def browser_path(browser):
if browser == "chromium":
return browser_path_chromium()
elif browser == "firefox":
return browser_path_firefox()
else:
raise SystemError("Unsupported browser")


def browser_path_firefox():
""" Return path to Firefox browser """
p = subprocess.check_output("which firefox || true",
shell=True, universal_newlines=True).strip()
if p:
return p
return None


def browser_path_chromium():
"""Return path to chromium browser.
Support the following locations:
- /usr/lib*/chromium-browser/headless_shell (chromium-headless RPM)
Expand Down Expand Up @@ -52,6 +70,8 @@ def __init__(self, lang=None, verbose=False, trace=False, inject_helpers=[]):
self.verbose = verbose
self.trace = trace
self.inject_helpers = inject_helpers
self.browser = os.environ.get("TEST_BROWSER", "chromium")
self.download_dir = tempfile.mkdtemp()
self._driver = None
self._browser = None
self._browser_home = None
Expand All @@ -74,7 +94,7 @@ def invoke(self, fn, **kwargs):
# frame support for Runtime.evaluate(): map frame name to
# executionContextId and insert into argument object; this must not be quoted
# see "Frame tracking" in cdp-driver.js for how this works
if fn == 'Runtime.evaluate' and self.cur_frame:
if fn == 'Runtime.evaluate':
cmd = "%s, contextId: getFrameExecId(%s)%s" % (cmd[:-2], jsquote(self.cur_frame), cmd[-2:])

if trace:
Expand Down Expand Up @@ -137,10 +157,39 @@ def find_cdp_port(self):

def get_browser_path(self):
if self._browser_path is None:
self._browser_path = browser_path()
self._browser_path = browser_path(self.browser)

return self._browser_path

def browser_cmd(self, cdp_port, env):
exe = self.get_browser_path()
if not exe:
raise SystemError(self.browser + " is not installed")

if self.browser == "chromium":
return [exe, "--headless", "--disable-gpu", "--no-sandbox", "--disable-setuid-sandbox",
"--disable-namespace-sandbox", "--disable-seccomp-filter-sandbox",
"--disable-sandbox-denial-logging", "--disable-pushstate-throttle",
"--window-size=1920x1200", "--remote-debugging-port=%i" % cdp_port, "about:blank"]
elif self.browser == "firefox":
subprocess.Popen(["firefox", "--headless", "--no-remote", "-CreateProfile", "blank"], env=env).communicate()
profile = glob.glob(os.path.join(self._browser_home, ".mozilla/firefox/*.blank"))[0]

with open(os.path.join(profile, "user.js"), "w") as f:
f.write("""
user_pref("remote.enabled", true);
user_pref("datareporting.policy.dataSubmissionEnabled", false);
user_pref("toolkit.telemetry.reportingpolicy.firstRun", false);
user_pref("dom.disable_beforeunload", true);
user_pref("browser.download.dir", "{0}");
user_pref("browser.download.folderList", 2);
""".format(self.download_dir))

with open(os.path.join(profile, "handlers.json"), "w") as f:
f.write('{"defaultHandlersVersion":{"en-US":4},"mimeTypes":{"application/xz":{"action":0,"extensions":["xz"]}}}')

return [exe, "-P", "blank", "--headless", "--window-size=1920,1200", "--remote-debugging-port=%i" % cdp_port, "--no-remote", "localhost"]

def start(self):
environ = os.environ.copy()
if self.lang:
Expand Down Expand Up @@ -168,19 +217,12 @@ def start(self):
except KeyError:
pass

exe = self.get_browser_path()
if not exe:
raise SystemError("chromium not installed")

# sandboxing does not work in Docker container
self._browser = subprocess.Popen(
[exe, "--headless", "--disable-gpu", "--no-sandbox", "--disable-setuid-sandbox",
"--disable-namespace-sandbox", "--disable-seccomp-filter-sandbox",
"--disable-sandbox-denial-logging", "--disable-pushstate-throttle",
"--window-size=1920x1200", "--remote-debugging-port=%i" % cdp_port, "about:blank"],
env=environ, close_fds=True, preexec_fn=lambda: resource.setrlimit(resource.RLIMIT_CORE, (0, 0)))
self.browser_cmd(cdp_port, environ), env=environ, close_fds=True,
preexec_fn=lambda: resource.setrlimit(resource.RLIMIT_CORE, (0, 0)))
if self.verbose:
sys.stderr.write("Started %s (pid %i) on port %i\n" % (exe, self._browser.pid, cdp_port))
sys.stderr.write("Started %s (pid %i) on port %i\n" % (self._browser_path, self._browser.pid, cdp_port))

# wait for CDP to be up
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
Expand All @@ -197,7 +239,7 @@ def start(self):
if self.trace:
# enable frame/execution context debugging if tracing is on
environ["TEST_CDP_DEBUG"] = "1"
self._driver = subprocess.Popen(["%s/chromium-cdp-driver.js" % os.path.dirname(__file__), str(cdp_port)],
self._driver = subprocess.Popen(["{0}/{1}-cdp-driver.js".format(os.path.dirname(__file__), self.browser), str(cdp_port)],
env=environ,
stdout=subprocess.PIPE,
stdin=subprocess.PIPE,
Expand All @@ -209,6 +251,10 @@ def start(self):
src = f.read()
# HACK: injecting sizzle fails on missing `document` in assert()
src = src.replace('function assert( fn ) {', 'function assert( fn ) { return true;')
# HACK: sizzle tracks document and when we switch frames, it sees the old document
# although we execute it in different context.
if (self.browser == "firefox"):
src = src.replace('context = context || document;', 'context = context || window.document;')
self.invoke("Page.addScriptToEvaluateOnNewDocument", source=src, no_trace=True)

def kill(self):
Expand All @@ -219,6 +265,8 @@ def kill(self):
self._driver.wait()
self._driver = None

shutil.rmtree(self.download_dir, ignore_errors=True)

if self._browser:
if self.verbose:
sys.stderr.write("Killing browser (pid %i)\n" % self._browser.pid)
Expand Down
5 changes: 3 additions & 2 deletions test/common/chromium-cdp-driver.js
Original file line number Diff line number Diff line change
Expand Up @@ -167,8 +167,7 @@ function setupFrameTracking(client) {
// map frame names to frame IDs; root frame has no name, no need to track that
client.Page.frameNavigated(info => {
debug("frameNavigated " + JSON.stringify(info));
if (info.frame.name)
frameNameToFrameId[info.frame.name] = info.frame.id;
frameNameToFrameId[info.frame.name || "cockpit1"] = info.frame.id;

// were we waiting for this frame to be loaded?
if (frameWaitPromiseResolve && frameWaitName === info.frame.name) {
Expand Down Expand Up @@ -208,6 +207,8 @@ function setupFrameTracking(client) {

// helper functions for testlib.py which are too unwieldy to be poked in from Python
function getFrameExecId(frame) {
if (frame === null)
frame = "cockpit1";
var frameId = frameNameToFrameId[frame];
if (!frameId)
throw Error(`Frame ${frame} is unknown`);
Expand Down

0 comments on commit 5ac0eb7

Please sign in to comment.