Commit 69b0dc7d authored by Annie Sullivan's avatar Annie Sullivan Committed by Commit Bot

Reland AddLayoutInstabilityTest.OOPIFSubframeWeighting.

Original CL: https://chromium-review.googlesource.com/c/chromium/src/+/2218908

The CL was reverted due to failures:
https://chromium-swarm.appspot.com/task?id=4c70451b557f4a10
https://logs.chromium.org/logs/chromium/buildbucket/cr-buildbucket.appspot.com/8879124242719394576/+/steps/browser_tests/0/logs/Deterministic_failure:_LayoutInstabilityTest.OOPIFSubframeWeighting__status_CRASH_/0

../../chrome/browser/page_load_metrics/integration_tests/layout_instability_browsertest.cc:152: Failure
Value of: cls_values[i].has_value()
  Actual: false
Expected: true

This looks like one of the scores in the loop isn't getting set correctly. I'm not sure which,
so this CL also fixes the check of scores and is more explicit about which score is being checked.

Bug: 943668
Change-Id: Icd0203fa8b6521b83605ab1c1210376c2f7a8356
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2219112Reviewed-by: default avatarSteve Kobes <skobes@chromium.org>
Commit-Queue: Annie Sullivan <sullivan@chromium.org>
Cr-Commit-Position: refs/heads/master@{#772696}
parent 43233e9d
<script src="resources/testharness.js"></script> <script src="resources/testharness.js"></script>
<script src="/script/async_buffer.js"></script>
<script> <script>
// Tell testharness.js to not wait for 'real' tests; we only want // Tell testharness.js to not wait for 'real' tests; we only want
// testharness.js for its assertion helpers. // testharness.js for its assertion helpers.
...@@ -6,52 +7,6 @@ setup({'output': false}); ...@@ -6,52 +7,6 @@ setup({'output': false});
</script> </script>
<script> <script>
// 'AsyncBuffer' serves as a helper to buffer LCP reports asynchronously.
class AsyncBuffer {
constructor() {
// 'pending' is an array that will buffer entries reported through the
// PerformanceObserver and can be collected with 'pop'.
this.pending = [];
// 'resolve_fn' is a reference to the 'resolve' function of a
// Promise that blocks for new entries to arrive via 'push()'. Calling
// the function resolves the promise and unblocks calls to 'pop()'.
this.resolve_fn = null;
}
// Concatenates the given 'entries' list to this AsyncBuffer.
push(entries) {
if (entries.length == 0) {
throw new Error("Must not push an empty list of entries!");
}
this.pending = this.pending.concat(entries);
// If there are calls to 'pop' that are blocked waiting for items, signal
// that they can continue.
if (this.resolve_fn != null) {
this.resolve_fn();
this.resolve_fn = null;
}
}
// Takes the current pending entries from this AsyncBuffer. If there are no
// entries queued already, this will block until some show up.
async pop() {
if (this.pending.length == 0) {
// Need to instantiate a promise to block on. The next call to 'push'
// will resolve the promise once it has queued the entries.
await new Promise(resolve => {
this.resolve_fn = resolve;
});
}
assert_true(this.pending.length > 0);
const result = this.pending;
this.pending = [];
return result;
}
}
const buffer = new AsyncBuffer(); const buffer = new AsyncBuffer();
const po = new PerformanceObserver(entryList => { const po = new PerformanceObserver(entryList => {
buffer.push(entryList.getEntries()); buffer.push(entryList.getEntries());
......
<script src="/resources/testharness.js"></script>
<script src="/script/async_buffer.js"></script>
<script>
var buffer = new AsyncBuffer();
window.addEventListener('message', function() {
buffer.push(event.data);
}, false);
const block_for_next_cls_subframe = async () => {
return buffer.pop().then(seen_events => {
// This test case assumes each CLS entry is handled before the next could
// possibly be generated.
return seen_events[0];
});
};
// Shifts the div in the subframe vertically by 60px
const verticalSubframeShift = () => {
document.getElementById('i').contentWindow.postMessage('shift', '*');
};
// Shifts the iframe right by 50% of the viewport.
const horizontalIframeShift = () => {
document.getElementById('i').style.left = '600px';
document.getElementById('i').contentWindow.postMessage('unshift', '*');
};
const run_test = async () => {
// This test exerciess the following scenario
// - Shift a div in an iframe vertically
// - Report the layout shift to the test
// - Shift the div back, and at the same time, shift the iframe
// horizontally halfway outside the viewport.
// - Report the layout shift to the test
verticalSubframeShift();
const cls_0 = await block_for_next_cls_subframe();
horizontalIframeShift();
const cls_1 = await block_for_next_cls_subframe();
// Now that we've run through the scenario and collected our measurements,
// return them in a structure that the C++ side can easily query.
let output = [
{'score': cls_0.value},
{'score': cls_1.value}
];
return output;
};
</script>
<style>
#i {
border: 0;
position: absolute;
left: 0;
top: 0;
background-color: pink;
}
</style>
<iframe id="i" width="400" height="300"
src="/cross-site/foo.com/layout-instability/sub_frame.html"></iframe>
\ No newline at end of file
<script src="/resources/testharness.js"></script>
<script src="/script/async_buffer.js"></script>
<script>
var buffer = new AsyncBuffer();
const po = new PerformanceObserver(entryList => {
buffer.push(entryList.getEntries());
});
po.observe({type: 'layout-shift', buffered: true});
// The main frame sends a message to shift the 'j' div, and then
// this frame does the shift and then sends a postmessage with
// the resulting layout shift entry.
function receiveMessage(event) {
var top;
if (event.data == 'shift') {
top = '60px';
} else if (event.data == 'unshift') {
top = '';
} else {
return;
}
document.getElementById('j').style.top = top;
buffer.pop().then(seen_events => {
// This test case assumes each CLS entry is handled before the next could
// possibly be generated.
window.parent.postMessage({'value': seen_events[0].value}, '*');
});
}
window.addEventListener("message", receiveMessage, false);
</script>
<style>
#j {
position: relative;
width: 300px;
height: 100px;
background-color: purple;
}
</style>
<div id="j"></div>
\ No newline at end of file
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// 'AsyncBuffer' serves as a helper to buffer PerformanceObserver reports
// asynchronously.
class AsyncBuffer {
constructor() {
// 'pending' is an array that will buffer entries reported through the
// PerformanceObserver and can be collected with 'pop'.
this.pending = [];
// 'resolve_fn' is a reference to the 'resolve' function of a
// Promise that blocks for new entries to arrive via 'push()'. Calling
// the function resolves the promise and unblocks calls to 'pop()'.
this.resolve_fn = null;
}
// Concatenates the given 'entries' list to this AsyncBuffer.
push(entries) {
if (entries.length == 0) {
throw new Error("Must not push an empty list of entries!");
}
this.pending = this.pending.concat(entries);
// If there are calls to 'pop' that are blocked waiting for items, signal
// that they can continue.
if (this.resolve_fn != null) {
this.resolve_fn();
this.resolve_fn = null;
}
}
// Takes the current pending entries from this AsyncBuffer. If there are no
// entries queued already, this will block until some show up.
async pop() {
if (this.pending.length == 0) {
// Need to instantiate a promise to block on. The next call to 'push'
// will resolve the promise once it has queued the entries.
await new Promise(resolve => {
this.resolve_fn = resolve;
});
}
assert_true(this.pending.length > 0);
const result = this.pending;
this.pending = [];
return result;
}
}
\ No newline at end of file
...@@ -135,3 +135,47 @@ IN_PROC_BROWSER_TEST_F(LayoutInstabilityTest, Sources_Enclosure) { ...@@ -135,3 +135,47 @@ IN_PROC_BROWSER_TEST_F(LayoutInstabilityTest, Sources_Enclosure) {
IN_PROC_BROWSER_TEST_F(LayoutInstabilityTest, Sources_MaxImpact) { IN_PROC_BROWSER_TEST_F(LayoutInstabilityTest, Sources_MaxImpact) {
RunWPT("sources-maximpact.html", true); RunWPT("sources-maximpact.html", true);
} }
IN_PROC_BROWSER_TEST_F(LayoutInstabilityTest, OOPIFSubframeWeighting) {
Start();
StartTracing({"loading"});
Load("/layout-instability/main_frame.html");
content::EvalJsResult result = EvalJs(web_contents(), "run_test()");
EXPECT_EQ("", result.error);
// Verify that the JS API yielded two CLS reports for the subframe.
const auto& list = result.value.GetList();
EXPECT_EQ(2ul, list.size());
base::Optional<double> cls_first_value = list[0].FindDoublePath("score");
EXPECT_EQ(0.4 * (60.0 / 400.0), cls_first_value)
<< "The first shift value should be 300 * (100 + 60) * (60 / 400) / "
"(default viewport size 800 * 600)";
base::Optional<double> cls_second_value = list[1].FindDoublePath("score");
EXPECT_EQ(0.4 * (60.0 / 400.0), cls_second_value)
<< "The second shift value should be 300 * (100 + 60) * (60 / 400) / "
"(default viewport size 800 * 600)";
// Need to navigate away from the test html page to force metrics to get
// flushed/synced.
ui_test_utils::NavigateToURL(browser(), GURL("about:blank"));
// Check Trace Events.
std::unique_ptr<TraceAnalyzer> analyzer = StopTracingAndAnalyze();
TraceEventVector events;
analyzer->FindEvents(Query::EventNameIs("LayoutShift"), &events);
EXPECT_EQ(3ul, events.size());
std::unique_ptr<Value> data;
events[0]->GetArgAsValue("data", &data);
EXPECT_EQ(0.4 * (60.0 / 400.0), *data->FindDoubleKey("score"));
// Check UKM.
ExpectUKMPageLoadMetricNear(
PageLoad::kLayoutInstability_CumulativeShiftScoreName,
LayoutShiftUkmValue(0.35), 5);
// Check UMA.
auto samples = histogram_tester().GetAllSamples(
"PageLoad.LayoutInstability.CumulativeShiftScore");
EXPECT_EQ(1ul, samples.size());
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment