From 9c75ca91d4e60b656de044209ad7f837411d6b60 Mon Sep 17 00:00:00 2001 From: Jarred Sumner Date: Thu, 25 Apr 2024 15:47:24 -0700 Subject: [PATCH] Add a few tests for --target --- src/compile_target.zig | 42 +++++++ src/js/internal-for-testing.ts | 5 + test/bundler/bundler_compile_cross.test.ts | 130 +++++++++++++++++++++ test/bundler/expectBundled.ts | 22 +++- 4 files changed, 196 insertions(+), 3 deletions(-) create mode 100644 test/bundler/bundler_compile_cross.test.ts diff --git a/src/compile_target.zig b/src/compile_target.zig index 5b3756269d..9a0717e65e 100644 --- a/src/compile_target.zig +++ b/src/compile_target.zig @@ -436,3 +436,45 @@ pub fn defineValues(this: *const CompileTarget) []const []const u8 { }, } } + +const JSC = bun.JSC; +pub fn createCompileTargetBindings(globalObject: *JSC.JSGlobalObject) JSC.JSValue { + var object = JSC.JSValue.createEmptyObject(globalObject, 2); + const getDefaultTarget = struct { + pub fn getDefaultTarget(global: *JSC.JSGlobalObject, _: *JSC.CallFrame) callconv(.C) JSC.JSValue { + const target = CompileTarget{}; + var str = bun.String.createFormat("{}", .{target}) catch |err| { + global.throwError(err, "Failed"); + return .zero; + }; + defer str.deref(); + return str.toJS(global); + } + }.getDefaultTarget; + const from_fn = struct { + pub fn from(global: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(.C) JSC.JSValue { + const name_value = callframe.arguments(1).ptr[0]; + const input = name_value.toSlice(global, bun.default_allocator); + defer input.deinit(); + const target = CompileTarget.from(input.slice()); + + var str = bun.String.createFormat("{}", .{target}) catch |err| { + global.throwError(err, "Failed"); + return .zero; + }; + defer str.deref(); + return str.toJS(global); + } + }.from; + object.put( + globalObject, + bun.String.init("getDefaultTarget"), + JSC.createCallback(globalObject, null, 0, &getDefaultTarget), + ); + object.put( + globalObject, + bun.String.init("from"), + JSC.createCallback(globalObject, null, 1, &from_fn), + ); + return object; +} diff --git a/src/js/internal-for-testing.ts b/src/js/internal-for-testing.ts index fe9c089698..6376f96313 100644 --- a/src/js/internal-for-testing.ts +++ b/src/js/internal-for-testing.ts @@ -34,3 +34,8 @@ export const upgrade_test_helpers = $zig("upgrade_command.zig", "upgrade_js_bind openTempDirWithoutSharingDelete: () => void; closeTempDirHandle: () => void; }; + +export const CompileTarget = $zig("compile_target.zig", "createCompileTargetBindings") as { + getDefaultTarget: () => `bun-${string}`; + from: (input: string) => `bun-${string}`; +}; diff --git a/test/bundler/bundler_compile_cross.test.ts b/test/bundler/bundler_compile_cross.test.ts new file mode 100644 index 0000000000..25d55a494f --- /dev/null +++ b/test/bundler/bundler_compile_cross.test.ts @@ -0,0 +1,130 @@ +import { bunEnv } from "harness"; +import { itBundled, testForFile } from "./expectBundled"; +var { describe, test, expect } = testForFile(import.meta.path); +import { CompileTarget } from "bun:internal-for-testing"; + +describe("bundler", async () => { + itBundled("compile_cross/DetectDefaultTarget", { + compile: true, + target: CompileTarget.getDefaultTarget(), + env: { + ...bunEnv, + BUN_COMPILE_TARGET_TARBALL_URL: + "https://localhost:999999/deliberately-invalid-url-to-check-that-it-detects-default-target-correctly", + }, + files: { + "/entry.ts": /* js */ ` + console.log("Hello, world!"); + `, + }, + run: { stdout: "Hello, world!" }, + }); + itBundled("compile_cross/DetectDefaultTarget2", { + compile: true, + target: "bun", + env: { + ...bunEnv, + BUN_COMPILE_TARGET_TARBALL_URL: + "https://localhost:999999/deliberately-invalid-url-to-check-that-it-detects-default-target-correctly", + }, + files: { + "/entry.ts": /* js */ ` + console.log("Hello, world!"); + `, + }, + run: { stdout: "Hello, world!" }, + }); + itBundled("compile_cross/FailOnInvalidTarget", { + compile: true, + target: `bun-poop`, + env: { + ...bunEnv, + BUN_COMPILE_TARGET_TARBALL_URL: + "https://localhost:999999/deliberately-invalid-url-to-check-that-it-detects-default-target-correctly", + }, + files: { + "/entry.ts": /* js */ ` + console.log("Hello, world!"); + `, + }, + bundleErrors: { + "": [`Unsupported target "poop" in "bun-poop"`], + }, + }); + itBundled("compile_cross/FailOnWildcardVersion", { + compile: true, + target: `bun-v1.*`, + env: { + ...bunEnv, + BUN_COMPILE_TARGET_TARBALL_URL: + "https://localhost:999999/deliberately-invalid-url-to-check-that-it-detects-default-target-correctly", + }, + files: { + "/entry.ts": /* js */ ` + console.log("Hello, world!"); + `, + }, + bundleErrors: { + "": [ + `Please pass a complete version number to --target. For example, --target=bun-v` + + Bun.version.replace("-debug", ""), + ], + }, + }); + itBundled("compile_cross/FailsOnInvalidURL", { + compile: true, + target: `bun-linux-arm64-v1.0.9999`, + env: { + ...bunEnv, + BUN_COMPILE_TARGET_TARBALL_URL: "https://localhost:999999/deliberately-invalid-url", + }, + files: { + "/entry.ts": /* js */ ` + console.log("Hello, world!"); + `, + }, + bundleErrors: { + "": [`failed to download cross-compiled bun executable`], + }, + }); + + // we allow using a local file if it has the exact name we expect + // this is undocumented behavior, but it will make it easier to write tests in the future + itBundled("compile_cross/UsesFileFromSameLocation", { + compile: true, + target: `bun-linux-arm64-v1.999.9999`, + env: { + ...bunEnv, + BUN_COMPILE_TARGET_TARBALL_URL: "https://localhost:999999/deliberately-invalid-url", + }, + files: { + "/entry.ts": /* js */ ` + console.log("Hello, world!"); + `, + // expects "bun" to be trimmed + [CompileTarget.from("-linux-arm64-v1.999.9999")]: ``, + }, + }); + + // expects "bun" to be trimmed + const sameLocationTarget = CompileTarget.from(`-${process.platform}-${process.arch}-v1.0.9999`); + + itBundled("compile_cross/RunsFileFromSameLocation", { + compile: true, + target: sameLocationTarget, + env: { + ...bunEnv, + BUN_COMPILE_TARGET_TARBALL_URL: "https://localhost:999999/deliberately-invalid-url", + }, + files: { + "/entry.ts": /* js */ ` + console.log("Hello, world!"); + `, + + ["/" + sameLocationTarget]: Buffer.from(await Bun.file(process.execPath).arrayBuffer()), + }, + run: { + stdout: "Hello, world!", + }, + }); +}); diff --git a/test/bundler/expectBundled.ts b/test/bundler/expectBundled.ts index 0af0e6f55d..224a743541 100644 --- a/test/bundler/expectBundled.ts +++ b/test/bundler/expectBundled.ts @@ -46,13 +46,29 @@ function dedent(str: string | TemplateStringsArray, ...args: any[]) { let currentFile: string | undefined; function errorOrWarnParser(isError = true) { - const prefix = isError ? "error: " : "warn: "; - return function (text: string) { + let defaultPrefix = isError ? "error: " : "warn: "; + return function parser(text: string) { + let prefix = defaultPrefix; var i = 0; var list = []; while (i < text.length) { let errorLineI = text.indexOf(prefix, i); if (errorLineI === -1) { + // Sometimes, our errors start with the error name itself + // e.g. "ConnectionRefused: connect ECONNREFUSED 127.0.0.1:8080" + // In those cases, it IS an error, but it doesn't start with "error: ", + if (list.length === 0 && prefix === defaultPrefix && !text.includes(prefix)) { + const firstPrefix = text.indexOf(": ", 0); + if (firstPrefix !== -1) { + const possiblePrefix = text.slice(0, firstPrefix).trim(); + if (/^([a-zA-Z0-9_]+)$/.test(possiblePrefix)) { + prefix = possiblePrefix.trim() + ": "; + i = 0; + continue; + } + } + } + return list; } const message = text.slice(errorLineI + prefix.length, text.indexOf("\n", errorLineI + 1)); @@ -175,7 +191,7 @@ export interface BundlerTestInput { /** Defaults to `/out` */ outdir?: string; /** Defaults to "browser". "bun" is set to "node" when using esbuild. */ - target?: "bun" | "node" | "browser"; + target?: "bun" | "node" | "browser" | `bun-${string}`; publicPath?: string; keepNames?: boolean; legalComments?: "none" | "inline" | "eof" | "linked" | "external";