Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make testharness tests run in a top-level browsing context #13966

Merged
merged 3 commits into from
Dec 11, 2018

Conversation

jgraham
Copy link
Contributor

@jgraham jgraham commented Nov 7, 2018

fixes #13418


This change is Reviewable

@jgraham
Copy link
Contributor Author

jgraham commented Nov 7, 2018

@jugglinmike This is the alternative to #13881. @lukebjerring if you have the ability to generate a complete run of this to look for regressions that would be very helpful. I fixed some problems I found in a m-c run, but there were a few other issues remaining.

@foolip foolip changed the title Make testharness tests run in a top-level browisng context Make testharness tests run in a top-level browsing context Nov 7, 2018
@jgraham jgraham force-pushed the wptrunner_tlbc branch 3 times, most recently from 5acbb3d to 0e5347a Compare November 7, 2018 14:52
Copy link
Member

@gsnedders gsnedders left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

By no means a complete review, but from a quick look through.

protocol.base.execute_script(self.script % format_map, async=True)
test_window = protocol.testharness.get_test_window(self.window_id, parent_window)

protocol.base.execute_script("window.open('about:blank', '%s', 'noopener')" % self.window_id)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note that Edge will shortly be using this and doesn't support noopener.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Opening from an iframe apparently works in older versions of Safari:

var iframe = document.createElement('iframe');
document.body.appendChild(iframe);
iframe.contentWindow.open('about:blank', 'window_id');
document.body.removeChild(iframe);

Though Edge's behavior isn't what we want. From the child window:

> !!opener
true
> opener.document
X Permission denied

We can just null out the window's opener property:

var win = open('about:blank');
win.opener = null;

This feels a little artificial. Is it important that the window be disowned specifically? If so, maybe we could inject an anchor with target="_blank" rel="noreferrer" and click on the link. That's spec'd to disown the window, and it works in Edge.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I"m not super worried about this; any test that depends on the opener being null at the moment is already broken. I only used noopener to prove that we aren't depending on the opener any more,

In the medium term I hope that everyone will implement the Create Window webdriver endpoint, which is inteded to have exactly the behaviour we require here and also returns the handle of the new window so we can delete the window finding code. I'm happy to do the more complex solution in the interim if people think that it's important, but otherwise I would tend to argue that this good enough for now.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think we need to worry about older versions of Safari; it's almost exclusively Edge that's my concern here.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does anyone happen to have Edge set up so we can verify if this PR works there?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

let's try explicitly setting window.open().opener = null, because I don't think that can make anything worse?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You don't think wrong; I tried this and Chrome crashes (or maybe there's an uncaught exception due to trying to set a readonly property).

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

tools/wptrunner/wptrunner/products.py Show resolved Hide resolved
@lukebjerring
Copy link
Contributor

lukebjerring commented Nov 7, 2018

@jgraham
Started the scrapes, you'll eventually see them @ https://wpt.fyi/test-runs?label=testdriver-message-passing

EDIT: In case someone with access to https://wpt.fyi/admin/results/upload wants to get a PR into wpt.fyi in future, I just:

@lukebjerring
Copy link
Contributor

Whoops - @jgraham I didn't realise affected tests would be like, 19 css tests.
@jugglinmike how did you trigger a complete run for #13881 ?

@jugglinmike
Copy link
Contributor

@lukebjerring
Copy link
Contributor

Err, my bad on pushing 3d0c723, reverted.

tools/wptrunner/wptrunner/testharnessreport.js Outdated Show resolved Hide resolved
protocol.base.execute_script(self.script % format_map, async=True)
test_window = protocol.testharness.get_test_window(self.window_id, parent_window)

protocol.base.execute_script("window.open('about:blank', '%s', 'noopener')" % self.window_id)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Opening from an iframe apparently works in older versions of Safari:

var iframe = document.createElement('iframe');
document.body.appendChild(iframe);
iframe.contentWindow.open('about:blank', 'window_id');
document.body.removeChild(iframe);

Though Edge's behavior isn't what we want. From the child window:

> !!opener
true
> opener.document
X Permission denied

We can just null out the window's opener property:

var win = open('about:blank');
win.opener = null;

This feels a little artificial. Is it important that the window be disowned specifically? If so, maybe we could inject an anchor with target="_blank" rel="noreferrer" and click on the link. That's spec'd to disown the window, and it works in Edge.

} else if (document.readyState !== "loading") {
callback(true);
} else {
document.addEventListener("DOMContentLoaded", () => callback(true));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's make this readyStateChange being ready to match WebDriver.

Traditionally testharness tests ran in a auxillary browsing context
opened using window.open and with access to the opener. This works
well because the long-lived nature of the opener helps to avoid some
of the race conditions that would otherwise occur. But it doesn't work
*that* well; the recent refactor to stop continually focusing the
opener broke tests that alter document.domain or otherwise prevent the
opener being same-domain with the test window. And future platform
features may cause the opener to be nulled out entirely so even a
postMessage based fix wouldn't work.

To solve all of this, this patch refactors things so that the initial
window doesn't contain any logic at all and is just used to keep the
browser alive between tests. Most of the logic moves to
testharnessreport.js which is loaded once per test. In order to get
the right timeout when timeout_multiplier is set this requires an
addition to the product API in wptrunner to expose a function for
getting the timeout multiplier. This allows us to get the
timeout_multiplier for testharness tests upfront and avoids the need
to change the content of testharnessreport when we start running
testharness tests, or to restart the server for each test type.

The main issue with the single-window implementation is that we need
to start injecting script once the test page has loaded
testharnessreport.js. For most browsers we are able to use
pageLoadStrategy=eager to control this; in that case we can start
running tests once DOMContentLoaded is reached. Chrome doesn't support
this pageLoadStrategy, however, so we have to fake support with custom
script.
@jgraham
Copy link
Contributor Author

jgraham commented Nov 9, 2018

This seems to fix /html/dom/usvstring-reflection.html in Chrome.

@jugglinmike
Copy link
Contributor

@gsnedders
Copy link
Member

@jgraham so what's the status of this now?

@jgraham
Copy link
Contributor Author

jgraham commented Nov 20, 2018

Works on Firefox and Chrome on Linux and macOS. Status for Edge and Safari is unknown.

@foolip
Copy link
Member

foolip commented Nov 23, 2018

Safari looks happy on Azure Pipelines. If you trigger Azure Pipelines again even more stuff will run.

@gsnedders
Copy link
Member

In Edge almost all (all?) the testharness.js tests now give

JavascriptErrorException: javascript error (500): Object doesn't support property or method '__wptrunner_process_next_event'

@gsnedders
Copy link
Member

I couldn't later reproduce that, so I've no idea what's up with that; let's assume this works.

Copy link
Member

@gsnedders gsnedders left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed 27 of 27 files at r1, 3 of 3 files at r2, 2 of 2 files at r3.
Reviewable status: all files reviewed, 5 unresolved discussions (waiting on @jgraham, @frivoal, @annevk, @zqzhang, @zcorpan, @dbaron, @jdm, @foolip, and @domenic)


tools/wptrunner/wptrunner/products.py, line 34 at r1 (raw file):

    def __init__(self, config, product):
        module = product_module(config, product)
        data = module.__wptrunner__

What would it take to have a function (or just a variable) in that module that returned a Product?


tools/wptrunner/wptrunner/products.py, line 52 at r1 (raw file):

def load_product(config, product, load_cls=False):

What's the point of the load_cls arg here? When does it make sense to call this with it being True; why wouldn't you just construct the class yourself?


tools/wptrunner/wptrunner/browsers/firefox.py, line 103 at r1 (raw file):

    capabilities = {}
    if test_type == "testharness":
        capabilities["pageLoadStrategy"] = "eager"

Can we not make this default to eager, somehow? The fact we have all of this is to deal with ChromeDriver not supporting it, right?


tools/wptrunner/wptrunner/executors/base.py, line 525 at r1 (raw file):

    def process_complete(self, url, payload):
        rv = [strip_server(url)] + payload

Is this an earlier underlying bug? What caught this?


tools/wptrunner/wptrunner/executors/executormarionette.py, line 221 at r1 (raw file):

                return test_window

            time.sleep(0.1)

This is addressing the fact that closing windows is async, right?


tools/wptserve/wptserve/stash.py, line 175 at r1 (raw file):

        return value

Given you're adding loads of whitespace, can you enable whichever flake8 check checks for them, or does that still have lots of failures?

Copy link
Contributor Author

@jgraham jgraham left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewable status: all files reviewed, 5 unresolved discussions (waiting on @gsnedders, @frivoal, @annevk, @zqzhang, @zcorpan, @dbaron, @jdm, @foolip, and @domenic)


tools/wptrunner/wptrunner/products.py, line 34 at r1 (raw file):

Previously, gsnedders (Geoffrey Sneddon) wrote…

What would it take to have a function (or just a variable) in that module that returned a Product?

A new PR to implement that.


tools/wptrunner/wptrunner/products.py, line 52 at r1 (raw file):

Previously, gsnedders (Geoffrey Sneddon) wrote…

What's the point of the load_cls arg here? When does it make sense to call this with it being True; why wouldn't you just construct the class yourself?

Fair point. Should that block this PR?


tools/wptrunner/wptrunner/browsers/firefox.py, line 103 at r1 (raw file):

Previously, gsnedders (Geoffrey Sneddon) wrote…

Can we not make this default to eager, somehow? The fact we have all of this is to deal with ChromeDriver not supporting it, right?

Chromedriver doesn't support it and it's not clear that it's right for other test types. We could set this in the executor, but you'd still need to set it once per executor.


tools/wptrunner/wptrunner/executors/base.py, line 525 at r1 (raw file):

Previously, gsnedders (Geoffrey Sneddon) wrote…

Is this an earlier underlying bug? What caught this?

I think this is a change because we aren't returning the same URL as before.


tools/wptrunner/wptrunner/executors/executormarionette.py, line 221 at r1 (raw file):

Previously, gsnedders (Geoffrey Sneddon) wrote…

This is addressing the fact that closing windows is async, right?

s/closing/opening/ and yes.


tools/wptserve/wptserve/stash.py, line 175 at r1 (raw file):

Previously, gsnedders (Geoffrey Sneddon) wrote…

Given you're adding loads of whitespace, can you enable whichever flake8 check checks for them, or does that still have lots of failures?

I didn't check but I would expect lots of failures. We should do that as a seperate PR.

Copy link
Member

@gsnedders gsnedders left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewable status: :shipit: complete! all files reviewed, all discussions resolved (waiting on @frivoal, @annevk, @zqzhang, @zcorpan, @dbaron, @jdm, @foolip, and @domenic)


tools/wptrunner/wptrunner/products.py, line 34 at r1 (raw file):

Previously, jgraham wrote…

A new PR to implement that.

Given you've done half the work here, it's probably worth actually prioritising this now…


tools/wptrunner/wptrunner/products.py, line 52 at r1 (raw file):

Previously, jgraham wrote…

Fair point. Should that block this PR?

Let's actually keep this as is on the assumption we'll refactor so the module itself gives the Product and this provides an abstraction to make this easier, and remove this then.


tools/wptrunner/wptrunner/browsers/firefox.py, line 103 at r1 (raw file):

Previously, jgraham wrote…

Chromedriver doesn't support it and it's not clear that it's right for other test types. We could set this in the executor, but you'd still need to set it once per executor.

I guess both of these suck. Meh.

@foolip
Copy link
Member

foolip commented Dec 13, 2018

This caused test regressions, see #14495. I've reverted the first commit in #14496 and left the other two commits making test changes that seem valid with or without this change.

@foolip
Copy link
Member

foolip commented Dec 13, 2018

@jgraham @gsnedders I see that regressions were looked for in #13966 (comment), but that was a month before this was merged. It would have been great to do another trial right before landing.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
css-conditional css-transitions html infra wptrunner The automated test runner, commonly called through ./wpt run wptserve
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Avoid window.open in the test runner
7 participants