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

Enable Multivalue again #984

Merged
merged 2 commits into from
Oct 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 33 additions & 24 deletions buildCore.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,21 @@ import { execSync } from "child_process";
import fs from "fs";

let toolchain = "";
let targetFolder = "debug";
let profile = "debug";
let cargoFlags = "";
let rustFlags = "-C target-feature=+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext,+simd128,+extended-const";
let rustFlags =
"-C target-feature=+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext,+simd128,+extended-const,+multivalue";
let wasmBindgenFlags = "";
let target = "wasm32-unknown-unknown";
let targetFolder = target;

if (process.argv.some((v) => v === "--max-opt")) {
// Do a fully optimized build ready for deployment.
targetFolder = "max-opt";
profile = "max-opt";
cargoFlags = "--profile max-opt";
} else if (process.argv.some((v) => v === "--release")) {
// Do an optimized build.
targetFolder = "release";
profile = "release";
cargoFlags = "--release";
} else {
// Do a debug build.
Expand All @@ -23,19 +26,28 @@ if (process.argv.some((v) => v === "--max-opt")) {
// Use WASM features that may not be supported by all the browsers.
if (process.argv.some((v) => v === "--unstable")) {
// Relaxed SIMD is not supported by Firefox and Safari yet.
// Tail calls are not supported by Safari and wasm-bindgen yet.
rustFlags += ",+relaxed-simd"; //,+tail-call";
rustFlags += ",+relaxed-simd";

// Tail calls are not supported by Safari yet.
rustFlags += ",+tail-call";

// Reference types are broken in webpack (or rather its underlying webassemblyjs):
// https://github.com/LiveSplit/LiveSplitOne/issues/630
// wasmBindgenFlags += " --reference-types";
}

// Use the nightly toolchain, which enables some more optimizations.
if (process.argv.some((v) => v === "--nightly")) {
toolchain = "+nightly";
cargoFlags += " -Z build-std=std,panic_abort -Z build-std-features=panic_immediate_abort";
rustFlags += ",+multivalue -Z wasm-c-abi=spec";
cargoFlags +=
" -Z build-std=std,panic_abort -Z build-std-features=panic_immediate_abort";
rustFlags += " -Z wasm-c-abi=spec";
target = "../wasm32-multivalue.json";
targetFolder = "wasm32-multivalue";

// Virtual function elimination requires LTO, so we can only do it for
// max-opt builds.
if (targetFolder == "max-opt") {
if (profile == "max-opt") {
// Seems like cargo itself calls rustc to check for file name patterns,
// but it forgets to pass the LTO flag that we specified in the
// Cargo.toml, so the virtual-function-elimination complains that it's
Expand All @@ -45,33 +57,30 @@ if (process.argv.some((v) => v === "--nightly")) {
}
}

execSync(
`cargo ${toolchain} run`,
{
cwd: "livesplit-core/capi/bind_gen",
stdio: "inherit",
},
);
execSync(`cargo ${toolchain} run`, {
cwd: "livesplit-core/capi/bind_gen",
stdio: "inherit",
});

execSync(
`cargo ${toolchain} rustc -p livesplit-core-capi --crate-type cdylib --features wasm-web,web-rendering --target wasm32-unknown-unknown ${cargoFlags}`,
`cargo ${toolchain} rustc -p livesplit-core-capi --crate-type cdylib --features wasm-web,web-rendering --target ${target} ${cargoFlags}`,
{
cwd: "livesplit-core",
stdio: "inherit",
env: {
...process.env,
'RUSTFLAGS': rustFlags,
RUSTFLAGS: rustFlags,
},
},
}
);

execSync(
`wasm-bindgen ${wasmBindgenFlags} livesplit-core/target/wasm32-unknown-unknown/${targetFolder}/livesplit_core.wasm --out-dir src/livesplit-core`,
`wasm-bindgen ${wasmBindgenFlags} livesplit-core/target/${targetFolder}/${profile}/livesplit_core.wasm --out-dir src/livesplit-core`,
{
stdio: "inherit",
},
}
);

fs
.createReadStream("livesplit-core/capi/bindings/wasm_bindgen/index.ts")
.pipe(fs.createWriteStream("src/livesplit-core/index.ts"));
fs.createReadStream("livesplit-core/capi/bindings/wasm_bindgen/index.ts").pipe(
fs.createWriteStream("src/livesplit-core/index.ts")
);
164 changes: 119 additions & 45 deletions test/rendering-test.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,9 @@
import fs from "fs";
import path from "path";
import { createServer } from 'http-server';
import {
Builder,
By,
until,
} from "selenium-webdriver";
import { createServer } from "http-server";
import { Builder, By, until } from "selenium-webdriver";
import chrome from "selenium-webdriver/chrome.js";
import { } from "chromedriver";
import {} from "chromedriver";
import imghash from "imghash";
import hex64 from "hex64";
import leven from "leven";
Expand Down Expand Up @@ -39,30 +35,37 @@ describe("Layout Rendering Tests", function () {
const clickElement = async (selector) => {
const element = await findElement(selector);
await element.click();
}
};

const loadFile = async (filePath) => {
const inputElement = await findElement(By.id("file-input"));
await inputElement.sendKeys(path.resolve(filePath));
await driver.sleep(500);
}
};

const hashToBinary = (hash) => {
const buffer = Buffer.from(hash, "base64");
const values = Array.from(buffer.values());
return values.map((value) => value.toString(2).padStart(8, "0")).join("");
return values
.map((value) => value.toString(2).padStart(8, "0"))
.join("");
};

before(async () => {
console.log('Starting server...');
console.log("Starting server...");

await startServer();

console.log('Server started!');
console.log('Preparing WebDriver for tests...');
console.log("Server started!");
console.log("Preparing WebDriver for tests...");

const options = new chrome.Options().windowSize({ width: 1200, height: 2400 }).addArguments("--headless");
driver = await new Builder().forBrowser("chrome").setChromeOptions(options).build();
const options = new chrome.Options()
.windowSize({ width: 1200, height: 2400 })
.addArguments("--headless");
driver = await new Builder()
.forBrowser("chrome")
.setChromeOptions(options)
.build();

await driver.get("http://localhost:8081");

Expand All @@ -75,29 +78,41 @@ describe("Layout Rendering Tests", function () {
this.style.display = "none";
this.id = "file-input";
document.body.appendChild(this);
}
};
});

if (!fs.existsSync(SCREENSHOTS_FOLDER)) {
fs.mkdirSync(SCREENSHOTS_FOLDER);
}

console.log('Ready to run tests!');
console.log("Ready to run tests!");
});

const testRendering = (layoutName, splitsName, expectedHash) => {
it(`Renders the ${layoutName} layout with the ${splitsName} splits correctly`, async function () {
this.timeout(10000);

await clickElement(By.xpath(".//button[contains(text(), 'Layout')]"));
await clickElement(By.xpath(".//button[contains(text(), 'Import')]"));
await clickElement(
By.xpath(".//button[contains(text(), 'Layout')]")
);
await clickElement(
By.xpath(".//button[contains(text(), 'Import')]")
);
await loadFile(`${LAYOUTS_FOLDER}/${layoutName}.ls1l`);
await clickElement(By.xpath(".//button[contains(text(), 'Back')]"));

await clickElement(By.xpath(".//button[contains(text(), 'Splits')]"));
await clickElement(By.xpath(".//button[contains(text(), 'Import')]"));
await clickElement(
By.xpath(".//button[contains(text(), 'Splits')]")
);
await clickElement(
By.xpath(".//button[contains(text(), 'Import')]")
);
await loadFile(`${SPLITS_FOLDER}/${splitsName}.lss`);
await clickElement(By.xpath("(.//button[contains(@aria-label, 'Open Splits')])[last()]"));
await clickElement(
By.xpath(
"(.//button[contains(@aria-label, 'Open Splits')])[last()]"
)
);
await clickElement(By.xpath(".//button[contains(text(), 'Back')]"));

const layoutElement = await findElement(By.className("layout"));
Expand All @@ -113,8 +128,14 @@ describe("Layout Rendering Tests", function () {
const actualHashHex = await imghash.hash(tempFilePath, 24);
const actualHash = hex64.encode(actualHashHex);

const actualScreenshotPath = `${SCREENSHOTS_FOLDER}/${layoutName}_${splitsName}_${actualHash.replace("/", "$")}.png`;
const expectedScreenshotPath = `${SCREENSHOTS_FOLDER}/${layoutName}_${splitsName}_${expectedHash.replace("/", "$")}.png`;
const actualScreenshotPath = `${SCREENSHOTS_FOLDER}/${layoutName}_${splitsName}_${actualHash.replace(
"/",
"$"
)}.png`;
const expectedScreenshotPath = `${SCREENSHOTS_FOLDER}/${layoutName}_${splitsName}_${expectedHash.replace(
"/",
"$"
)}.png`;

fs.renameSync(tempFilePath, actualScreenshotPath);

Expand All @@ -126,42 +147,95 @@ describe("Layout Rendering Tests", function () {
let showWarning = false;
try {
if (fs.existsSync(expectedScreenshotPath)) {
const actualImage = PNG.sync.read(fs.readFileSync(actualScreenshotPath));
const expectedImage = PNG.sync.read(fs.readFileSync(expectedScreenshotPath));
const actualImage = PNG.sync.read(
fs.readFileSync(actualScreenshotPath)
);
const expectedImage = PNG.sync.read(
fs.readFileSync(expectedScreenshotPath)
);
const { width, height } = actualImage;
const diff = new PNG({ width, height });

const numPixelsDifferent = pixelmatch(actualImage.data, expectedImage.data, diff.data, width, height, { threshold: 0.2 });
const numPixelsDifferent = pixelmatch(
actualImage.data,
expectedImage.data,
diff.data,
width,
height,
{ threshold: 0.2 }
);

if (numPixelsDifferent === 0) {
showWarning = true;
}

fs.writeFileSync(`${SCREENSHOTS_FOLDER}/${layoutName}_${splitsName}_diff.png`, PNG.sync.write(diff));
fs.writeFileSync(
`${SCREENSHOTS_FOLDER}/${layoutName}_${splitsName}_diff.png`,
PNG.sync.write(diff)
);
}
}
finally {
} finally {
if (showWarning) {
console.warn(`Render match despite mismatching hashes (${layoutName} layout with ${splitsName} splits)! ` +
`Expected hash: ${expectedHash}, actual hash: ${actualHash}`);
console.warn(
`Render match despite mismatching hashes (${layoutName} layout with ${splitsName} splits)! ` +
`Expected hash: ${expectedHash}, actual hash: ${actualHash}`
);
} else {
throw Error(`Render mismatch (${layoutName} layout with ${splitsName} splits)! ` +
`Expected hash: ${expectedHash}, actual hash: ${actualHash}`)
throw Error(
`Render mismatch (${layoutName} layout with ${splitsName} splits)! ` +
`Expected hash: ${expectedHash}, actual hash: ${actualHash}`
);
}
}
}
});
}

testRendering("all_components", "default", "PwD-XAAAAAA2AAAO____________________AAAAAAAAAAAA____AAAA____AAAA____AAAAf_AAd_AAAAD-Nz___34HbwAG");
testRendering("all_components", "pmw3", "PwD-XAAAAAA2AAAO_________sAPf8AAf4AC3_AH_-AOX-APXfAGX-AP18AOf-APXeAOX-AOD7AOQIAIAAD-b_j_____LwBs");
testRendering("default", "default", "________8AADVVVVAAAAAAAAAAAA____________AAAAAAAAAAAA____VVVVAAAA____AAAAVVVVAAAAAADgAAD_________");
testRendering("default", "pmw3", "b_gAb9-HV8AG__AHV_AG7kAGX-AGV-AOX8APX_gOX-AP28AO_-APX-APXeAOUQAOX-AOX_gP_-AfAAAAAADgAAD_q97_____");
testRendering("splits_two_rows", "celeste", "b4AAYAAH________bwAA4AAHb-AA4AAHb-AAYAAHbwAA____bwAAYAAH_9VV____b_AAYAAH__wA____b_AAYAAPb8AAYAAP");
testRendering("splits_with_labels", "celeste", "AAAAAAA8AAA3AAA3AAA3________WAAHfwAHfwAHTwAHVVVVAAAA0gAH_wAH_wAH3wAH_________IAP34AP34AP38APAAAA");
testRendering("title_centered_no_game_icon", "celeste", "________MzMzADAAADwAADwA________VX1VADwAAAAAAAAA____ABkAAP8DAP8DAP8DAP8D________V_1XAGAAAAAAAAAA");
testRendering("title_centered_with_game_icon", "celeste", "________AiIiQDAAYDwAYDwA________YDwAYDwAYAAAYAAAd393YBkAYL8DYP8DYP8DYP8D________d_1XYGAAAAAAAAAA");
testRendering("title_left_no_attempt_count", "celeste", "________MzMz4AAA8AAA-AAA_________VVV-AAAAAAAAAAA____GQAA_wAA_wAA_wAA_wAA________f_1XIAAAAAAAAAAA");
};

testRendering(
"all_components",
"default",
"fwB-WgAAAAA2AAAO____________________AAAAAAAAAAAA____AAAA____AAAA____AAAAf_AAf_AAAAD-Mj___34HbwAG"
);
testRendering(
"all_components",
"pmw3",
"fwB-WgAAAAA2AAAO_________8AGf8AAf4AC3_AH_-APX-APXfAGX-APV8AOX-APX-APX-AOL_AOQIAIAAD-b_j_____LgAs"
);
testRendering(
"default",
"default",
"________8AADVVVVAAAAAAAAAAAA____________AAAAAAAAAAAA____VVVVAAAA____AAAAVVVVAAAAAADgAAD_________"
);
testRendering(
"default",
"pmw3",
"b_gAb_-GV8AG3_AHV_AGXuAGX-AGV-AOX-AOX_AOX-AP38AO3-APX-APXeAOWWAOXeAOX_gO_-AfAAAAAADgAAD_q97_____"
);
testRendering(
"splits_two_rows",
"celeste",
"b4AAYAAH________bwAA4AAHb-AA4AAHb-AAYAAHbwAA____bwAAYAAH_9VV____b_AAYAAH__wA____b_AAYAAPb8AAYAAP"
);
testRendering(
"splits_with_labels",
"celeste",
"AAAAAAA8AAA_AAA3AAAn________WAAHfwAHfwAHXwAHVVVVAAAA2wAH_wAH_wAH3wAH________9IAP_4AP34AP34APAAAA"
);
testRendering(
"title_centered_no_game_icon",
"celeste",
"________MzMzADwAADwAADwA________VX1VADwAAAAAAAAA____ABkDAP8DAP8DAP8DAP8D________V_1XACEAAAAAAAAA"
);
testRendering(
"title_centered_with_game_icon",
"celeste",
"________AjIiQDwAYDwAYDwA________YDwAYDwAYAAAYAAAczszYBkDYP8DYP8DYP8DYP8D________d_1XYCEAAAAAAAAA"
);
testRendering(
"title_left_no_attempt_count",
"celeste",
"________IiIi8AAA-AAA-AAA_________VVV-AAAAAAAAAAA____GQAA_wAA_wAA_wAA_wAA________f_1XKQAAAAAAAAAA"
);

after(async () => {
await driver.quit();
Expand Down
Loading