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

Implement end2end tests #11

Merged
merged 14 commits into from
Jan 24, 2025
34 changes: 34 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
name: CI

on:
push:
branches: ["main"]
pull_request:
branches: ["main"]

# Allows you to run this workflow manually from the Actions tab
workflow_dispatch:

jobs:
build:
name: Build
runs-on: macos-latest
strategy:
matrix:
browser: [chrome, firefox, safari]
steps:
- name: Install Firefox
if: ${{ matrix.browser == 'firefox' }}
run: brew install --cask firefox
- name: Checkout Branch
uses: actions/checkout@v3
- name: Setup Node
uses: actions/setup-node@v3
with:
node-version: 18.13.0
- name: Install
run: npm install
- name: Run tests
run: |
echo "Running in $BROWSER"
npm run test:${{ matrix.browser }}
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
.DS_Store
.directory
/node_modules

# Ignore auto-generated files by VS & VSCode.
/.vs/
Expand Down
57 changes: 43 additions & 14 deletions JetStreamDriver.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,35 +27,47 @@

const measureTotalTimeAsSubtest = false; // Once we move to preloading all resources, it would be good to turn this on.

const defaultIterationCount = 120;
const defaultWorstCaseCount = 4;

globalThis.performance ??= Date;
globalThis.RAMification ??= false;
globalThis.testIterationCount ??= undefined;
globalThis.testIterationCountMap ??= new Map();
globalThis.testWorstCaseCount ??= undefined;
globalThis.testWorstCaseCountMap ??= new Map();
globalThis.dumpJSONResults ??= false;
globalThis.customTestList ??= [];
globalThis.startDelay ??= undefined;

let shouldReport = false;
let startDelay;

function getIntParam(urlParams, key) {
if (!urlParams.has(key))
return undefined
const rawValue = urlParams.get(key);
const value = parseInt(rawValue);
if (value <= 0)
throw new Error(`Expected positive value for ${key}, but got ${rawValue}`)
return value
}

if (typeof(URLSearchParams) !== "undefined") {
const urlParameters = new URLSearchParams(window.location.search);
shouldReport = urlParameters.has('report') && urlParameters.get('report').toLowerCase() == 'true';
if (shouldReport)
startDelay = 4000;
if (urlParameters.has('startDelay'))
startDelay = urlParameters.get('startDelay');
globalThis.startDelay = 4000;
if (urlParameters.has('test'))
customTestList = urlParameters.getAll("test");

globalThis.startDelay = getIntParam(urlParameters, "startDelay");
Copy link
Contributor

Choose a reason for hiding this comment

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

This seems like it will always set startDelay to undefined when the there's no startDelay URL parameter. I think that's fine for testIterationCount and testWorstCaseCount since they'd always be undefined on the web as it is. Although maybe it's worth changing there too so folks aren't surprised?

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 hoped to make the initialization explicit by the lines above globalThis.startDelay ??= undefined; at least for me that's a bit more consistent across all configuration params.

Medium term I'd like to adapt the Params object from Speedometer (this also allows us to easily copy over the ?developerMode UI).

Maybe I missed your point here, but when running via the shell we now also always initialize startDelay=undefined.
With the html runner we'd initialize to undefined by default (and set it again to undefined if there is no matching url param)

But generally not feeling strongly about this one :)

Copy link
Contributor

Choose a reason for hiding this comment

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

I think the issue is when you do index.html?report=true startDelay ends up as undefined rather than 4000. If you moved the report check below this and changed it to if (shouldReport) globalThis.startDelay ??= 4000 I think that would fix it.

Safari, at least, relies on the startDelay to make sure the browser has calmed down after launching enough to prevent noise.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

ok, then I understood correctly.
Keeping the old behavior with report=true sg.

globalThis.testIterationCount = getIntParam(urlParameters, "iterationCount");
globalThis.testWorstCaseCount = getIntParam(urlParameters, "worstCaseCount");
}

// Used for the promise representing the current benchmark run.
this.currentResolve = null;
this.currentReject = null;

const defaultIterationCount = 120;
const defaultWorstCaseCount = 4;

let showScoreDetails = false;
let categoryScores = null;

Expand Down Expand Up @@ -83,6 +95,8 @@ function getIterationCount(plan) {
function getWorstCaseCount(plan) {
if (testWorstCaseCountMap.has(plan.name))
return testWorstCaseCountMap.get(plan.name);
if (testWorstCaseCount)
return testWorstCaseCount;
if (plan.worstCaseCount)
return plan.worstCaseCount;
return defaultWorstCaseCount;
Expand Down Expand Up @@ -220,6 +234,7 @@ const fileLoader = (function() {

class Driver {
constructor() {
this.isReady = false;
this.benchmarks = [];
this.blobDataCache = { };
this.loadCache = { };
Expand Down Expand Up @@ -314,6 +329,11 @@ class Driver {

this.reportScoreToRunBenchmarkRunner();
this.dumpJSONResultsIfNeeded();
if (isInBrowser) {
globalThis.dispatchEvent(new CustomEvent("JetStreamDone", {
detail: this.resultsObject()
}));
}
}

runCode(string)
Expand Down Expand Up @@ -426,8 +446,12 @@ class Driver {
await this.prefetchResourcesForBrowser();
await this.fetchResources();
this.prepareToRun();
if (isInBrowser && startDelay !== undefined) {
setTimeout(() => this.start(), startDelay);
this.isReady = true;
if (isInBrowser) {
globalThis.dispatchEvent(new Event("JetStreamReady"));
if (shouldReport) {
setTimeout(() => this.start(), globalThis.startDelay);
}
}
}

Expand Down Expand Up @@ -479,7 +503,7 @@ class Driver {
}
}

resultsJSON()
resultsObject()
{
const results = {};
for (const benchmark of this.benchmarks) {
Expand All @@ -498,8 +522,13 @@ class Driver {
}

results = {"JetStream3.0": {"metrics" : {"Score" : ["Geometric"]}, "tests" : results}};
return results;

}

return JSON.stringify(results);
resultsJSON()
{
return JSON.stringify(this.resultsObject());
}

dumpJSONResultsIfNeeded()
Expand Down Expand Up @@ -564,7 +593,7 @@ class Benchmark {
results.push(Math.max(1, end - start));
}
if (__benchmark.validate)
__benchmark.validate();
__benchmark.validate(${this.iterations});
top.currentResolve(results);`;
}

Expand Down Expand Up @@ -1006,7 +1035,7 @@ class AsyncBenchmark extends DefaultBenchmark {
results.push(Math.max(1, end - start));
}
if (__benchmark.validate)
__benchmark.validate();
__benchmark.validate(${this.iterations});
top.currentResolve(results);
}
doRun().catch((error) => { top.currentReject(error); });`
Expand Down
2 changes: 1 addition & 1 deletion RexBench/FlightPlanner/benchmark.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ class Benchmark {
}
}

validate()
validate(iterations)
{
for (let flightPlan of expectedFlightPlans) {
flightPlan.calculate();
Expand Down
2 changes: 1 addition & 1 deletion RexBench/OfflineAssembler/benchmark.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ class Benchmark {
this.ast = parse("LowLevelInterpreter.asm");
}

validate()
validate(iterations)
{
let astDumpedAsLines = this.ast.dump().split("\n");

Expand Down
5 changes: 4 additions & 1 deletion RexBench/UniPoker/benchmark.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,11 @@ class Benchmark {
playHands(this._players);
}

validate()
validate(iterations)
{
if (!iterations)
throw "Invalid iterations"
const playerExpectations = getPlayerExpectations(iterations)
if (this._players.length != playerExpectations.length)
throw "Expect " + playerExpectations.length + ", but actually have " + this._players.length;
if (isInBrowser) {
Expand Down
24 changes: 15 additions & 9 deletions RexBench/UniPoker/expected.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,15 @@
*/
"use strict";

const defaultIterationCount = 120;

class PlayerExpectation
{
constructor(wins, handTypeCounts)
constructor(iterations, wins, handTypeCounts)
{
this._wins = wins;
this._handTypeCounts = handTypeCounts;
const factor = iterations / defaultIterationCount;
this._wins = wins * factor;
this._handTypeCounts = handTypeCounts.map(each => each * factor);
}

validate(player)
Expand Down Expand Up @@ -62,10 +65,13 @@ PlayerExpectation._handTypes = [
"Straight Flushes",
"Royal Flushes"
];

var playerExpectations = [];

playerExpectations.push(new PlayerExpectation(60120, [120600, 102000, 10080, 5040, 1440, 360, 480, 0, 0, 0]));
playerExpectations.push(new PlayerExpectation(60480, [120360, 99600, 11760, 6120, 1200, 360, 480, 120, 0, 0]));
playerExpectations.push(new PlayerExpectation(61440, [121200, 99720, 11040, 6120, 1080, 480, 360, 0, 0, 0]));
playerExpectations.push(new PlayerExpectation(57960, [121320, 100440, 11040, 5760, 840, 480, 120, 0, 0, 0]));

function getPlayerExpectations(iterations) {
const playerExpectations = [];
playerExpectations.push(new PlayerExpectation(iterations, 60120, [120600, 102000, 10080, 5040, 1440, 360, 480, 0, 0, 0]));
playerExpectations.push(new PlayerExpectation(iterations, 60480, [120360, 99600, 11760, 6120, 1200, 360, 480, 120, 0, 0]));
playerExpectations.push(new PlayerExpectation(iterations, 61440, [121200, 99720, 11040, 6120, 1080, 480, 360, 0, 0, 0]));
playerExpectations.push(new PlayerExpectation(iterations, 57960, [121320, 100440, 11040, 5760, 840, 480, 120, 0, 0, 0]));
return playerExpectations;
}
2 changes: 1 addition & 1 deletion bigint/bigdenary-benchmark.js
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ class Benchmark {
areFirstAndLastResultsEqual(() => bd1.dividedBy(bd2));
}

validate() {
validate(iterations) {
if (!this._allFirstAndLastResultsAreEqual)
throw new Error("Expected all first and last results to be equal, but they aren't.");
}
Expand Down
2 changes: 1 addition & 1 deletion bigint/paillier-benchmark.js
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ class Benchmark {
this._decryptedMulIsCorrect &&= decryptedMul === 123456789012345678900n;
}

validate() {
validate(iterations) {
if (!this._c1DecryptedIsCorrect)
throw new Error("Bad value: c1Decrypted!");

Expand Down
2 changes: 1 addition & 1 deletion generators/js-tokens.js
Original file line number Diff line number Diff line change
Expand Up @@ -451,7 +451,7 @@ class Benchmark {
}
}

validate() {
validate(iterations) {
if (this.tokenCount !== 113975)
throw new Error(`this.tokenCount of ${this.tokenCount} is invalid!`);
}
Expand Down
2 changes: 1 addition & 1 deletion generators/lazy-collections.js
Original file line number Diff line number Diff line change
Expand Up @@ -613,7 +613,7 @@ class Benchmark {
this.totalLength += binaryTree().length;
}

validate() {
validate(iterations) {
if (this.totalLength !== 635)
throw new Error(`this.totalLength of ${this.totalLength} is invalid!`);
}
Expand Down
Loading