Skip to content

Commit

Permalink
Merge pull request #4 from x0k/workers
Browse files Browse the repository at this point in the history
Move tests execution to the workers
  • Loading branch information
x0k authored Jun 12, 2024
2 parents e2b0b8c + a2fda7c commit f49c6f3
Show file tree
Hide file tree
Showing 25 changed files with 572 additions and 61 deletions.
Binary file modified bun.lockb
Binary file not shown.
6 changes: 3 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,17 +17,17 @@
"@php-wasm/web": "^0.7.20",
"@xterm/addon-fit": "^0.10.0",
"@xterm/xterm": "^5.5.0",
"astro": "^4.10.0",
"astro": "^4.10.1",
"astro-icon": "^1.1.0",
"fast-deep-equal": "^3.1.3",
"monaco-editor": "^0.49.0",
"monaco-vim": "^0.4.1",
"pyodide": "^0.26.0",
"pyodide": "^0.26.1",
"tailwindcss": "^3.4.4",
"typescript": "^5.4.5"
},
"devDependencies": {
"@iconify-json/lucide": "^1.1.190",
"@iconify-json/lucide": "^1.1.191",
"@iconify/svelte": "^4.0.2",
"@tailwindcss/typography": "^0.5.13",
"@types/color": "^3.0.6",
Expand Down
123 changes: 123 additions & 0 deletions src/adapters/testing-actor.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
import {
Actor,
MessageType,
WorkerConnection,
startRemote,
type Connection,
type EventMessage,
type IncomingMessage,
type OutgoingMessage,
} from "@/lib/actor";
import { createLogger } from "@/lib/logger";
import type { TestRunner, TestRunnerFactory } from "@/lib/testing";

interface Handlers<I, O> {
[key: string]: any;
init: (code: string) => Promise<void>;
run: (data: I) => Promise<O>;
}

type Incoming<I, O> = IncomingMessage<Handlers<I, O>>;

interface WriteEventMessage extends EventMessage<"write", string> {}
interface WritelnEventMessage extends EventMessage<"writeln", string> {}

type TestingActorEvent = WriteEventMessage | WritelnEventMessage;

type Outgoing<I, O> = OutgoingMessage<Handlers<I, O>, string> | TestingActorEvent;

class TestRunnerActor<I, O> extends Actor<Handlers<I, O>, string> {
private runner: TestRunner<I, O> | null = null;

constructor(
connection: Connection<Incoming<I, O>, Outgoing<I, O>>,
factory: TestRunnerFactory<I, O>
) {
const handlers: Handlers<I, O> = {
init: async (code) => {
this.runner = await factory({
code,
out: {
write(text) {
connection.send({
type: MessageType.Event,
event: "write",
payload: text,
});
},
writeln(text) {
connection.send({
type: MessageType.Event,
event: "writeln",
payload: text,
});
},
},
});
},
run: (input) => {
if (!this.runner) {
const err = new Error("Test runner not initialized");
connection.send({
type: MessageType.Event,
event: "error",
payload: err.message,
});
throw err;
}
return this.runner.run(input);
},
};
super(connection, handlers, String);
}
}

export function startTestRunnerActor<I, O>(factory: TestRunnerFactory<I, O>) {
const connection = new WorkerConnection<Incoming<I, O>, Outgoing<I, O>>(
self as unknown as Worker
);
const actor = new TestRunnerActor(connection, factory);
const stopConnection = connection.start();
const stopActor = actor.start();
return () => {
stopActor();
stopConnection();
};
}

interface WorkerConstructor {
new (): Worker;
}

export function makeRemoteTestRunnerFactory<I, O>(
Worker: WorkerConstructor
): TestRunnerFactory<I, O> {
return async ({ code, out }) => {
const worker = new Worker();
const connection = new WorkerConnection<Outgoing<I, O>, Incoming<I, O>>(
worker
);
const stopConnection = connection.start();
const log = createLogger(out);
const remote = startRemote<Handlers<I, O>, string, TestingActorEvent>(
log,
connection,
{
error: (err) => log.error(err),
write: (text) => out.write(text),
writeln: (text) => out.writeln(text),
}
);
await remote.init(code);
return {
async run(input) {
return remote.run(input);
},
[Symbol.dispose]() {
remote[Symbol.dispose]();
stopConnection();
worker.terminate();
},
};
};
}
43 changes: 22 additions & 21 deletions src/content/design-patterns/factory/editor.svelte
Original file line number Diff line number Diff line change
@@ -1,31 +1,32 @@
<script lang="ts">
import { Language, type TestRunnerFactory } from '@/lib/testing';
import { Language, type TestRunnerFactory } from "@/lib/testing";
import { makeRemoteTestRunnerFactory } from "@/adapters/testing-actor";
import Editor from '@/components/editor/editor.svelte';
import Editor from "@/components/editor/editor.svelte";
import { testsData, type Input, type Output } from './tests-data'
import { testRunnerFactory as phpTestRunnerFactory } from './php/test-runners'
import { jsTestRunnerFactory, tsTestRunnerFactory } from './js/test-runners'
import { testRunnerFactory as pyTestRunnerFactory } from './python/test-runners'
import { testsData, type Input, type Output } from "./tests-data";
import phpInitialValue from './php/code.php?raw'
import tsInitialValue from './js/code.ts?raw'
import jsInitialValue from './js/code.js?raw'
import pyInitialValue from './python/code.py?raw'
import { jsCode, JsWorker } from "./js";
import { tsCode, TsWorker } from "./ts";
import { phpCode, PhpWorker } from "./php";
import { pyCode, PyWorker } from "./python";
const INITIAL_VALUES: Record<Language, string> = {
[Language.PHP]: phpInitialValue,
[Language.TypeScript]: tsInitialValue,
[Language.JavaScript]: jsInitialValue,
[Language.Python]: pyInitialValue,
}
[Language.PHP]: phpCode,
[Language.TypeScript]: tsCode,
[Language.JavaScript]: jsCode,
[Language.Python]: pyCode,
};
const TEST_RUNNER_FACTORIES: Record<Language, TestRunnerFactory<Input, Output>> = {
[Language.PHP]: phpTestRunnerFactory,
[Language.TypeScript]: tsTestRunnerFactory,
[Language.JavaScript]: jsTestRunnerFactory,
[Language.Python]: pyTestRunnerFactory,
}
const TEST_RUNNER_FACTORIES: Record<
Language,
TestRunnerFactory<Input, Output>
> = {
[Language.PHP]: makeRemoteTestRunnerFactory(PhpWorker),
[Language.TypeScript]: makeRemoteTestRunnerFactory(TsWorker),
[Language.JavaScript]: makeRemoteTestRunnerFactory(JsWorker),
[Language.Python]: makeRemoteTestRunnerFactory(PyWorker),
};
</script>

<Editor
Expand Down
3 changes: 3 additions & 0 deletions src/content/design-patterns/factory/js/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export { default as jsCode } from "./code.js?raw";

export { default as JsWorker } from './worker?worker'
29 changes: 0 additions & 29 deletions src/content/design-patterns/factory/js/test-runners.ts

This file was deleted.

22 changes: 22 additions & 0 deletions src/content/design-patterns/factory/js/worker.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { createLogger } from "@/lib/logger";
import type { TestRunnerConfig } from "@/lib/testing";
import { JsTestRunner } from "@/lib/testing/js";
import { startTestRunnerActor } from "@/adapters/testing-actor";

import { type Input, type Output } from "../tests-data";
import type { PaymentSystemType } from "../reference";

interface TestingModule {
payment(type: PaymentSystemType, base: number, amount: number): number;
}

class SimpleJsTestRunner extends JsTestRunner<TestingModule, Input, Output> {
async executeTest(m: TestingModule, input: Input): Promise<Output> {
return m.payment(input.paymentSystem, input.base, input.amount);
}
}

startTestRunnerActor(
async ({ code, out }: TestRunnerConfig) =>
new SimpleJsTestRunner(createLogger(out), code)
);
3 changes: 3 additions & 0 deletions src/content/design-patterns/factory/php/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export { default as phpCode } from './code.php?raw'

export { default as PhpWorker } from './worker?worker'
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import {
PHPTestRunner,
phpRuntimeFactory,
} from "@/lib/testing/php";
import { startTestRunnerActor } from "@/adapters/testing-actor";

import { type Input, type Output } from "../tests-data";
import { PaymentSystemType } from "../reference";
Expand All @@ -27,5 +28,7 @@ class SimpleTestRunner extends PHPTestRunner<Input, Output> {
}
}

export const testRunnerFactory = async ({ code, out }: TestRunnerConfig) =>
new SimpleTestRunner(out, new FailSafePHP(phpRuntimeFactory), code);
startTestRunnerActor(
async ({ code, out }: TestRunnerConfig) =>
new SimpleTestRunner(out, new FailSafePHP(phpRuntimeFactory), code)
);
3 changes: 3 additions & 0 deletions src/content/design-patterns/factory/python/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export { default as pyCode } from "./code.py?raw"

export { default as PyWorker } from "./worker?worker"
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import type { TestRunnerConfig } from '@/lib/testing';
import type { TestRunnerConfig } from "@/lib/testing";
import { PyTestRunner, pyRuntimeFactory } from "@/lib/testing/python";
import { startTestRunnerActor } from "@/adapters/testing-actor";

import { type Input, type Output } from "../tests-data";

Expand All @@ -9,5 +10,7 @@ class SimpleTestRunner extends PyTestRunner<Input, Output> {
}
}

export const testRunnerFactory = async ({ code, out }: TestRunnerConfig) =>
new SimpleTestRunner(await pyRuntimeFactory(out), code);
startTestRunnerActor(
async ({ code, out }: TestRunnerConfig) =>
new SimpleTestRunner(await pyRuntimeFactory(out), code)
);
3 changes: 3 additions & 0 deletions src/content/design-patterns/factory/ts/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export { default as tsCode } from './code.ts?raw'

export { default as TsWorker } from './worker?worker'
22 changes: 22 additions & 0 deletions src/content/design-patterns/factory/ts/worker.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { createLogger } from "@/lib/logger";
import type { TestRunnerConfig } from "@/lib/testing";
import { TsTestRunner } from "@/lib/testing/js";
import { startTestRunnerActor } from "@/adapters/testing-actor";

import { type Input, type Output } from "../tests-data";
import type { PaymentSystemType } from "../reference";

interface TestingModule {
payment(type: PaymentSystemType, base: number, amount: number): number;
}

class SimpleTsTestRunner extends TsTestRunner<TestingModule, Input, Output> {
async executeTest(m: TestingModule, input: Input): Promise<Output> {
return m.payment(input.paymentSystem, input.base, input.amount);
}
}

startTestRunnerActor(
async ({ code, out }: TestRunnerConfig) =>
new SimpleTsTestRunner(createLogger(out), code)
);
Loading

0 comments on commit f49c6f3

Please sign in to comment.