Skip to content

Commit

Permalink
Cherry pick PR #2569: JavaScript Profiler Black box tests (#2818)
Browse files Browse the repository at this point in the history
b/326457979

This set of BlackBox tests causes Cobalt to SEGFAULT by forcing Garbage
Collection to execute while the JS Profiler is running. It adds a new
API to CrashLog called h5vcc.crashLog.forceGarbageCollection() for the
purposes of testing. Reverts [an existing revert in PR
2567](#2567). Tests are rewritten
to be less CPU-intensive; expecting fewer stack frames over a longer
period of time.

Test-On-Device: true

---------

Co-authored-by: Ahmed Elzeiny <[email protected]>
(cherry picked from commit 30dacbc)
  • Loading branch information
aelzeiny committed Apr 3, 2024
1 parent 9a45c37 commit 710c7be
Show file tree
Hide file tree
Showing 3 changed files with 129 additions and 0 deletions.
1 change: 1 addition & 0 deletions cobalt/black_box_tests/black_box_tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@
'disable_eval_with_csp',
'h5vcc_storage_write_verify_test',
'http_cache',
'javascript_profiler',
'persistent_cookie',
'scroll',
'service_worker_add_to_cache_test',
Expand Down
88 changes: 88 additions & 0 deletions cobalt/black_box_tests/testdata/javascript_profiler.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
<!DOCTYPE html>

<head>
<title>JavaScript Profiler Test</title>
<script async src="black_box_js_test_utils.js"></script>
<script>

const testPrimeProfiler = async () => {
const profiler = new Profiler({ sampleInterval: 10 /**ms**/, maxBufferSize: 10 /**number of samples*/ });

// sleep 10 seconds
await new Promise(resolve => setTimeout(resolve, 10000 /**ms**/));

// stop & check
const trace = await profiler.stop();
assertTrue(trace.samples.length > 0, "expected some stack traces");
};

const testSampleBufferFullProfiler = async () => {
const promise = new Promise((resolve, reject) => {
const profiler = new Profiler({ maxBufferSize: 1, sampleInterval: 10 });
profiler.addEventListener("samplebufferfull", () => {
profiler.stop().then(() => { resolve(true); });
});
});
const timeoutPromise = new Promise((resolve) => {
setTimeout(() => {
resolve(false);
}, 60000);
});

const result = await Promise.race([timeoutPromise, promise]);
assertTrue(result, "expected sample buffer to be full");
};

/**
* Creates a new Profiler and then deletes it before the sample buffer is full.
* Expects the SampleBuffer to be full and the callback to be executed.
*/
const testAbruptGarbageCollection = async () => {
const profilerPromise = new Promise((resolve) => {
window.profiler = new Profiler({ maxBufferSize: 100, sampleInterval: 10 });
delete window.profiler; // simulates the effects of GC.
resolve(true);
});

const survivePromise = new Promise((resolve) => {
setTimeout(() => {
resolve(true);
}, 10000);
});

// must survive for 10 seconds
const result = await Promise.all([survivePromise, profilerPromise]);
assertTrue(result.every(p => p), "expected cobalt to survive 10 sec if GC collects profiler");
};

window.addEventListener("load", async () => {
setupFinished();
// Test Case 1: Prime Profiler
if (window.location.search.includes("mode=testPrimeProfiler")) {
console.log("testPrimeProfiler");
await testPrimeProfiler();
}
// Test Case 2: SampleBufferFull Profiler
else if (window.location.search.includes("mode=testSampleBufferFullProfiler")) {
console.log("testSampleBufferFullProfiler");
await testSampleBufferFullProfiler();
}
// Test Case 3: GC Profiler
else if (window.location.search.includes("mode=testAbruptGarbageCollection")) {
console.log("testAbruptGarbageCollection");
await testAbruptGarbageCollection();
}
else {
console.log("Running all tests");
await testPrimeProfiler();
await testSampleBufferFullProfiler();
await testAbruptGarbageCollection();
}
console.log("DONE");
onEndTest();
});
</script>
</head>

<body>
</body>
40 changes: 40 additions & 0 deletions cobalt/black_box_tests/tests/javascript_profiler.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# Copyright 2024 The Cobalt Authors. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Tests if Cobalt client page can use window.Profiler."""

from __future__ import absolute_import
from __future__ import division
from __future__ import print_function

from cobalt.black_box_tests import black_box_tests
from cobalt.black_box_tests.threaded_web_server import ThreadedWebServer


class JavaScriptProfilerTest(black_box_tests.BlackBoxTestCase):
"""Ensure that the client can declare a `window.Profiler` object."""

def test_javascript_profiler_prime_profiler(self):
with ThreadedWebServer(binding_address=self.GetBindingAddress()) as server:
modes = [
'testPrimeProfiler',
'testSampleBufferFullProfiler',
'testAbruptGarbageCollection',
]
for mode in modes:
url = server.GetURL(
file_name=f'testdata/javascript_profiler.html?mode={mode}')
with self.CreateCobaltRunner(url=url) as runner:
runner.WaitForJSTestsSetup()
self.assertTrue(runner.JSTestsSucceeded(),
f'JavaScript profiler failed at case mode="{mode}".')

0 comments on commit 710c7be

Please sign in to comment.