aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--packages/bun-types/bun.d.ts2
-rw-r--r--packages/bun-types/events.d.ts2
-rw-r--r--packages/bun-types/jsc.d.ts8
-rw-r--r--test/harness.ts30
-rw-r--r--test/js/bun/http/serve.leak.ts6
-rw-r--r--test/js/bun/jsc/bun-jsc.test.ts (renamed from test/js/bun/jsc/bun-jsc.test.js)39
-rw-r--r--test/js/bun/net/tcp-server.test.ts42
-rw-r--r--test/js/bun/stream/direct-readable-stream.test.tsx35
-rw-r--r--test/js/node/events/event-emitter.test.ts32
9 files changed, 96 insertions, 100 deletions
diff --git a/packages/bun-types/bun.d.ts b/packages/bun-types/bun.d.ts
index bcfa0bf51..3773d3ebb 100644
--- a/packages/bun-types/bun.d.ts
+++ b/packages/bun-types/bun.d.ts
@@ -2694,7 +2694,7 @@ declare module "bun" {
*/
builder: PluginBuilder,
): void | Promise<void>;
- }): ReturnType<typeof options["setup"]>;
+ }): ReturnType<(typeof options)["setup"]>;
/**
* Deactivate all plugins
diff --git a/packages/bun-types/events.d.ts b/packages/bun-types/events.d.ts
index 2e9056618..96c97db32 100644
--- a/packages/bun-types/events.d.ts
+++ b/packages/bun-types/events.d.ts
@@ -121,7 +121,7 @@ declare module "events" {
* @param eventName The name of the event.
* @param listener The callback function
*/
- once(eventName: string | symbol, listener: (...args: any[]) => void): this;
+ once(eventName: string | symbol, listener: (this: this, ...args: any[]) => void): this;
/**
* Removes the specified `listener` from the listener array for the event named`eventName`.
*
diff --git a/packages/bun-types/jsc.d.ts b/packages/bun-types/jsc.d.ts
index b909d0348..df33db1ec 100644
--- a/packages/bun-types/jsc.d.ts
+++ b/packages/bun-types/jsc.d.ts
@@ -1,9 +1,9 @@
declare module "bun:jsc" {
export function describe(value: any): string;
export function describeArray(args: any[]): string;
- export function gcAndSweep(): void;
- export function fullGC(): void;
- export function edenGC(): void;
+ export function gcAndSweep(): number;
+ export function fullGC(): number;
+ export function edenGC(): number;
export function heapSize(): number;
export function heapStats(): {
heapSize: number;
@@ -29,7 +29,7 @@ declare module "bun:jsc" {
export function callerSourceOrigin(): string;
export function noFTL(func: Function): Function;
export function noOSRExitFuzzing(func: Function): Function;
- export function optimizeNextInvocation(func: Function): Function;
+ export function optimizeNextInvocation(func: Function): void;
export function numberOfDFGCompiles(func: Function): number;
export function releaseWeakRefs(): void;
export function totalCompileTime(func: Function): number;
diff --git a/test/harness.ts b/test/harness.ts
index 9c4ce8e56..9470d24ee 100644
--- a/test/harness.ts
+++ b/test/harness.ts
@@ -1,3 +1,7 @@
+import { gc as bunGC, unsafe } from "bun";
+import { heapStats } from "bun:jsc";
+import { expect } from "bun:test";
+
export const bunEnv: any = {
...process.env,
BUN_DEBUG_QUIET_LOGS: "1",
@@ -9,8 +13,18 @@ export function bunExe() {
return process.execPath;
}
-export function gc(force: boolean = true) {
- Bun.gc(force);
+export function gc(force = true) {
+ bunGC(force);
+}
+
+export async function expectObjectTypeCount(type: string, count: number, maxWait = 10000) {
+ gc();
+ for (const wait = 20; maxWait > 0; maxWait -= wait) {
+ if (heapStats().objectTypeCounts[type] === count) break;
+ await new Promise(resolve => setTimeout(resolve, wait));
+ gc();
+ }
+ expect(heapStats().objectTypeCounts[type]).toBe(count);
}
// we must ensure that finalizers are run
@@ -19,20 +33,18 @@ export function gcTick(trace = false) {
trace && console.trace("");
// console.trace("hello");
gc();
- return new Promise(resolve => {
- setTimeout(resolve, 0);
- });
+ return new Promise(resolve => setTimeout(resolve, 0));
}
export function withoutAggressiveGC(block: () => unknown) {
- if (!Bun.unsafe.gcAggressionLevel) return block();
+ if (!unsafe.gcAggressionLevel) return block();
- const origGC = Bun.unsafe.gcAggressionLevel();
- Bun.unsafe.gcAggressionLevel(0);
+ const origGC = unsafe.gcAggressionLevel();
+ unsafe.gcAggressionLevel(0);
try {
return block();
} finally {
- Bun.unsafe.gcAggressionLevel(origGC);
+ unsafe.gcAggressionLevel(origGC);
}
}
diff --git a/test/js/bun/http/serve.leak.ts b/test/js/bun/http/serve.leak.ts
index 09935d24d..a7b8a44f6 100644
--- a/test/js/bun/http/serve.leak.ts
+++ b/test/js/bun/http/serve.leak.ts
@@ -1,8 +1,8 @@
import { heapStats } from "bun:jsc";
-var prevCounts;
+var prevCounts: Record<string, number>;
export default {
- fetch(req) {
- const out = {};
+ fetch(req: Request) {
+ const out: Record<string, number> = {};
const counts = heapStats().objectTypeCounts;
for (const key in counts) {
if (prevCounts) {
diff --git a/test/js/bun/jsc/bun-jsc.test.js b/test/js/bun/jsc/bun-jsc.test.ts
index 6e6897eb3..aa93ce90a 100644
--- a/test/js/bun/jsc/bun-jsc.test.js
+++ b/test/js/bun/jsc/bun-jsc.test.ts
@@ -35,34 +35,39 @@ describe("bun:jsc", () => {
}
it("describe", () => {
- jscDescribe([]);
+ expect(jscDescribe([])).toBeDefined();
});
it("describeArray", () => {
- describeArray([1, 2, 3]);
+ expect(describeArray([1, 2, 3])).toBeDefined();
});
it("gcAndSweep", () => {
- gcAndSweep();
+ expect(gcAndSweep()).toBeGreaterThan(0);
});
it("fullGC", () => {
- fullGC();
+ expect(fullGC()).toBeGreaterThan(0);
});
it("edenGC", () => {
- edenGC();
+ expect(edenGC()).toBeGreaterThan(0);
});
it("heapSize", () => {
- expect(heapSize() > 0).toBe(true);
+ expect(heapSize()).toBeGreaterThan(0);
});
it("heapStats", () => {
- heapStats();
+ const stats = heapStats();
+ expect(stats.heapCapacity).toBeGreaterThan(0);
+ expect(stats.heapSize).toBeGreaterThan(0);
+ expect(stats.objectCount).toBeGreaterThan(0);
});
it("memoryUsage", () => {
- memoryUsage();
+ const usage = memoryUsage();
+ expect(usage.current).toBeGreaterThan(0);
+ expect(usage.peak).toBeGreaterThan(0);
});
it("getRandomSeed", () => {
- getRandomSeed(2);
+ expect(getRandomSeed()).toBeDefined();
});
it("setRandomSeed", () => {
- setRandomSeed(2);
+ expect(setRandomSeed(2)).toBeUndefined();
});
it("isRope", () => {
expect(isRope("a" + 123 + "b")).toBe(true);
@@ -75,23 +80,23 @@ describe("bun:jsc", () => {
it("noOSRExitFuzzing", () => {});
it("optimizeNextInvocation", () => {
count();
- optimizeNextInvocation(count);
+ expect(optimizeNextInvocation(count)).toBeUndefined();
count();
});
it("numberOfDFGCompiles", () => {
- expect(numberOfDFGCompiles(count) > 0).toBe(true);
+ expect(numberOfDFGCompiles(count)).toBeGreaterThan(0);
});
it("releaseWeakRefs", () => {
- releaseWeakRefs();
+ expect(releaseWeakRefs()).toBeUndefined();
});
it("totalCompileTime", () => {
- totalCompileTime(count);
+ expect(totalCompileTime(count)).toBeGreaterThanOrEqual(0);
});
it("reoptimizationRetryCount", () => {
- reoptimizationRetryCount(count);
+ expect(reoptimizationRetryCount(count)).toBeGreaterThanOrEqual(0);
});
it("drainMicrotasks", () => {
- drainMicrotasks();
+ expect(drainMicrotasks()).toBeUndefined();
});
it("startRemoteDebugger", () => {
// try {
@@ -103,6 +108,6 @@ describe("bun:jsc", () => {
// }
});
it("getProtectedObjects", () => {
- expect(getProtectedObjects().length > 0).toBe(true);
+ expect(getProtectedObjects().length).toBeGreaterThan(0);
});
});
diff --git a/test/js/bun/net/tcp-server.test.ts b/test/js/bun/net/tcp-server.test.ts
index ed5d04086..17f7df46b 100644
--- a/test/js/bun/net/tcp-server.test.ts
+++ b/test/js/bun/net/tcp-server.test.ts
@@ -1,11 +1,13 @@
import { listen, connect, TCPSocketListener, SocketHandler } from "bun";
import { describe, expect, it } from "bun:test";
-import * as JSC from "bun:jsc";
+import { expectObjectTypeCount } from "harness";
-var decoder = new TextDecoder();
+type Resolve = (value?: unknown) => void;
+type Reject = (reason?: any) => void;
+const decoder = new TextDecoder();
it("remoteAddress works", async () => {
- var resolve: () => void, reject: (e: any) => void;
+ var resolve: Resolve, reject: Reject;
var remaining = 2;
var prom = new Promise<void>((resolve1, reject1) => {
resolve = () => {
@@ -60,17 +62,17 @@ it("echo server 1 on 1", async () => {
// wrap it in a separate closure so the GC knows to clean it up
// the sockets & listener don't escape the closure
await (async function () {
- var resolve, reject, serverResolve, serverReject;
- var prom = new Promise((resolve1, reject1) => {
+ let resolve: Resolve, reject: Reject, serverResolve: Resolve, serverReject: Reject;
+ const prom = new Promise((resolve1, reject1) => {
resolve = resolve1;
reject = reject1;
});
- var serverProm = new Promise((resolve1, reject1) => {
+ const serverProm = new Promise((resolve1, reject1) => {
serverResolve = resolve1;
serverReject = reject1;
});
- var serverData, clientData;
+ let serverData: any, clientData: any;
const handlers = {
open(socket) {
socket.data.counter = 1;
@@ -129,7 +131,7 @@ it("echo server 1 on 1", async () => {
var server: TCPSocketListener<any> | undefined = listen({
socket: handlers,
hostname: "localhost",
- port: 8084,
+ port: 0,
data: {
isServer: true,
@@ -139,7 +141,7 @@ it("echo server 1 on 1", async () => {
const clientProm = connect({
socket: handlers,
hostname: "localhost",
- port: 8084,
+ port: server.port,
data: {
counter: 0,
},
@@ -151,24 +153,23 @@ it("echo server 1 on 1", async () => {
});
describe("tcp socket binaryType", () => {
- var port = 8085;
const binaryType = ["arraybuffer", "uint8array", "buffer"] as const;
for (const type of binaryType) {
it(type, async () => {
// wrap it in a separate closure so the GC knows to clean it up
// the sockets & listener don't escape the closure
await (async function () {
- var resolve, reject, serverResolve, serverReject;
- var prom = new Promise((resolve1, reject1) => {
+ let resolve: Resolve, reject: Reject, serverResolve: Resolve, serverReject: Reject;
+ const prom = new Promise((resolve1, reject1) => {
resolve = resolve1;
reject = reject1;
});
- var serverProm = new Promise((resolve1, reject1) => {
+ const serverProm = new Promise((resolve1, reject1) => {
serverResolve = resolve1;
serverReject = reject1;
});
- var serverData, clientData;
+ let serverData: any, clientData: any;
const handlers = {
open(socket) {
socket.data.counter = 1;
@@ -239,7 +240,7 @@ describe("tcp socket binaryType", () => {
var server: TCPSocketListener<any> | undefined = listen({
socket: handlers,
hostname: "localhost",
- port,
+ port: 0,
data: {
isServer: true,
counter: 0,
@@ -249,12 +250,11 @@ describe("tcp socket binaryType", () => {
const clientProm = connect({
socket: handlers,
hostname: "localhost",
- port,
+ port: server.port,
data: {
counter: 0,
},
});
- port++;
await Promise.all([prom, clientProm, serverProm]);
server.stop(true);
@@ -264,11 +264,9 @@ describe("tcp socket binaryType", () => {
}
});
-it("should not leak memory", () => {
- // Tell the garbage collector for sure that we're done with the sockets
- Bun.gc(true);
+it("should not leak memory", async () => {
// assert we don't leak the sockets
// we expect 1 because that's the prototype / structure
- expect(JSC.heapStats().objectTypeCounts.TCPSocket).toBe(1);
- expect(JSC.heapStats().objectTypeCounts.Listener).toBe(1);
+ await expectObjectTypeCount("Listener", 1);
+ await expectObjectTypeCount("TCPSocket", 1);
});
diff --git a/test/js/bun/stream/direct-readable-stream.test.tsx b/test/js/bun/stream/direct-readable-stream.test.tsx
index 6ef4014c4..f685bd634 100644
--- a/test/js/bun/stream/direct-readable-stream.test.tsx
+++ b/test/js/bun/stream/direct-readable-stream.test.tsx
@@ -6,10 +6,9 @@ import {
readableStreamToText,
serve,
} from "bun";
-import { heapStats } from "bun:jsc";
import { describe, expect, it } from "bun:test";
+import { expectObjectTypeCount, gc } from "harness";
import { renderToReadableStream as renderToReadableStreamBrowser } from "react-dom/server.browser";
-import { gc } from "harness";
import { renderToReadableStream as renderToReadableStreamBun } from "react-dom/server";
import React from "react";
@@ -222,8 +221,8 @@ describe("ReactDOM", () => {
for (let [inputString, reactElement] of fixtures) {
describe(`${renderToReadableStream.name}(${inputString})`, () => {
it("http server, 1 request", async () => {
- await (async function () {
- let server;
+ await (async () => {
+ var server;
try {
server = serve({
port: 0,
@@ -231,23 +230,19 @@ describe("ReactDOM", () => {
return new Response(await renderToReadableStream(reactElement));
},
});
- const resp = await fetch("http://localhost:" + server.port + "/");
- expect((await resp.text()).replaceAll("<!-- -->", "")).toBe(inputString);
- gc();
- } catch (e) {
- throw e;
+ const response = await fetch("http://localhost:" + server.port + "/");
+ const result = await response.text();
+ expect(result.replaceAll("<!-- -->", "")).toBe(inputString);
} finally {
server?.stop();
- gc();
}
})();
- gc();
- expect(heapStats().objectTypeCounts.ReadableHTTPResponseSinkController ?? 0).toBeLessThan(4);
+ await expectObjectTypeCount("ReadableHTTPResponseSinkController", 1);
});
const count = 4;
it(`http server, ${count} requests`, async () => {
var remain = count;
- await (async function () {
+ await (async () => {
var server;
try {
server = serve({
@@ -256,11 +251,9 @@ describe("ReactDOM", () => {
return new Response(await renderToReadableStream(reactElement));
},
});
- gc();
while (remain--) {
var attempt = remain + 1;
const response = await fetch("http://localhost:" + server.port + "/");
- gc();
const result = await response.text();
try {
expect(result.replaceAll("<!-- -->", "")).toBe(inputString);
@@ -268,19 +261,13 @@ describe("ReactDOM", () => {
e.message += "\nAttempt: " + attempt;
throw e;
}
-
- gc();
}
- } catch (e) {
- throw e;
} finally {
- server.stop();
+ server?.stop();
}
})();
-
- const { ReadableHTTPResponseSinkController = 0 } = heapStats().objectTypeCounts;
- expect(ReadableHTTPResponseSinkController).toBeLessThan(4);
- expect(remain + 1).toBe(0);
+ expect(remain).toBe(-1);
+ await expectObjectTypeCount("ReadableHTTPResponseSinkController", 1);
});
});
}
diff --git a/test/js/node/events/event-emitter.test.ts b/test/js/node/events/event-emitter.test.ts
index 2bb891778..0ffc5574e 100644
--- a/test/js/node/events/event-emitter.test.ts
+++ b/test/js/node/events/event-emitter.test.ts
@@ -1,10 +1,9 @@
import { test, describe, expect, it } from "bun:test";
-import fs from "node:fs";
-
+import { heapStats } from "bun:jsc";
+import { expectObjectTypeCount, gc } from "harness";
// this is also testing that imports with default and named imports in the same statement work
// our transpiler transform changes this to a var with import.meta.require
import EventEmitter, { getEventListeners, captureRejectionSymbol } from "node:events";
-import { heapStats } from "bun:jsc";
describe("EventEmitter", () => {
it("captureRejectionSymbol", () => {
@@ -91,15 +90,15 @@ const waysOfCreating = [
return foo;
},
() => {
- const FakeEmitter = function FakeEmitter() {
+ function FakeEmitter(this: any) {
return EventEmitter.call(this);
- };
+ }
Object.setPrototypeOf(FakeEmitter.prototype, EventEmitter.prototype);
Object.setPrototypeOf(FakeEmitter, EventEmitter);
- return new FakeEmitter();
+ return new (FakeEmitter as any)();
},
() => {
- const FakeEmitter = function FakeEmitter() {
+ const FakeEmitter: any = function FakeEmitter(this: any) {
EventEmitter.call(this);
};
Object.assign(FakeEmitter.prototype, EventEmitter.prototype);
@@ -117,9 +116,9 @@ for (let create of waysOfCreating) {
it(`${create.toString().slice(10, 40).replaceAll("\n", "\\n").trim()} should work`, () => {
var myEmitter = create();
var called = false;
- myEmitter.once("event", function () {
+ (myEmitter as EventEmitter).once("event", function () {
called = true;
- expect(this as any).toBe(myEmitter);
+ expect(this).toBe(myEmitter);
});
var firstEvents = myEmitter._events;
expect(myEmitter.listenerCount("event")).toBe(1);
@@ -143,13 +142,11 @@ test("EventEmitter.off", () => {
});
// Internally, EventEmitter has a JSC::Weak with the thisValue of the listener
-test("EventEmitter GCs", () => {
- Bun.gc(true);
+test("EventEmitter GCs", async () => {
+ gc();
- const startCount = heapStats().objectTypeCounts["EventEmitter"] || 0;
+ const startCount = heapStats().objectTypeCounts["EventEmitter"] ?? 0;
(function () {
- Bun.gc(true);
-
function EventEmitterSubclass(this: any) {
EventEmitter.call(this);
}
@@ -157,13 +154,10 @@ test("EventEmitter GCs", () => {
Object.setPrototypeOf(EventEmitterSubclass.prototype, EventEmitter.prototype);
Object.setPrototypeOf(EventEmitterSubclass, EventEmitter);
- var myEmitter = new EventEmitterSubclass();
+ var myEmitter = new (EventEmitterSubclass as any)();
myEmitter.on("foo", () => {});
myEmitter.emit("foo");
- Bun.gc(true);
})();
- Bun.gc(true);
- const endCount = heapStats().objectTypeCounts["EventEmitter"] || 0;
- expect(endCount).toBe(startCount);
+ await expectObjectTypeCount("EventEmitter", startCount);
});