mirror of
https://github.com/bitburner-official/bitburner-src.git
synced 2026-04-16 14:28:36 +02:00
UI: Add "Run" action to run current script in editor (#1987)
This commit is contained in:
@@ -39,6 +39,7 @@ import { isLegacyScript, legacyScriptExtension, resolveScriptFilePath, ScriptFil
|
||||
import { root } from "./Paths/Directory";
|
||||
import { getErrorMessageWithStackAndCause } from "./utils/ErrorHelper";
|
||||
import { exceptionAlert } from "./utils/helpers/exceptionAlert";
|
||||
import { Result } from "./types";
|
||||
|
||||
export const NetscriptPorts = new Map<PortNumber, Port>();
|
||||
|
||||
@@ -451,18 +452,66 @@ export function loadAllRunningScripts(): void {
|
||||
}
|
||||
}
|
||||
|
||||
export function createRunningScriptInstance(
|
||||
server: BaseServer,
|
||||
scriptPath: ScriptFilePath,
|
||||
ramOverride: number | null | undefined,
|
||||
threads: number,
|
||||
args: ScriptArg[],
|
||||
): Result<{ runningScript: RunningScript }> {
|
||||
const script = server.scripts.get(scriptPath);
|
||||
if (!script) {
|
||||
return {
|
||||
success: false,
|
||||
message: `Script ${scriptPath} does not exist on ${server.hostname}.`,
|
||||
};
|
||||
}
|
||||
|
||||
if (!server.hasAdminRights) {
|
||||
return {
|
||||
success: false,
|
||||
message: `You do not have root access on ${server.hostname}.`,
|
||||
};
|
||||
}
|
||||
|
||||
const singleRamUsage = ramOverride ?? script.getRamUsage(server.scripts);
|
||||
if (!singleRamUsage) {
|
||||
return {
|
||||
success: false,
|
||||
message: `Cannot calculate RAM usage of ${scriptPath}. Reason: ${script.ramCalculationError}`,
|
||||
};
|
||||
}
|
||||
const ramUsage = singleRamUsage * threads;
|
||||
const ramAvailable = server.maxRam - server.ramUsed;
|
||||
if (ramUsage > ramAvailable + 0.001) {
|
||||
return {
|
||||
success: false,
|
||||
message: `Cannot run ${scriptPath} (t=${threads}) on ${server.hostname}. This script requires ${formatRam(
|
||||
ramUsage,
|
||||
)} of RAM.`,
|
||||
};
|
||||
}
|
||||
|
||||
const runningScript = new RunningScript(script, singleRamUsage, args);
|
||||
return {
|
||||
success: true,
|
||||
runningScript,
|
||||
};
|
||||
}
|
||||
|
||||
/** Run a script from inside another script (run(), exec(), spawn(), etc.) */
|
||||
export function runScriptFromScript(
|
||||
caller: string,
|
||||
host: BaseServer,
|
||||
scriptname: ScriptFilePath,
|
||||
server: BaseServer,
|
||||
scriptPath: ScriptFilePath,
|
||||
args: ScriptArg[],
|
||||
workerScript: WorkerScript,
|
||||
runOpts: CompleteRunOptions,
|
||||
): number {
|
||||
const script = host.scripts.get(scriptname);
|
||||
if (!script) {
|
||||
workerScript.log(caller, () => `Could not find script '${scriptname}' on '${host.hostname}'`);
|
||||
// This does not adjust server RAM usage or change any state, so it is safe to call before performing other checks
|
||||
const result = createRunningScriptInstance(server, scriptPath, runOpts.ramOverride, runOpts.threads, args);
|
||||
if (!result.success) {
|
||||
workerScript.log(caller, () => result.message);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -471,49 +520,24 @@ export function runScriptFromScript(
|
||||
runOpts.preventDuplicates &&
|
||||
getRunningScriptsByArgs(
|
||||
{ workerScript, function: "runScriptFromScript", functionPath: "internal.runScriptFromScript" },
|
||||
scriptname,
|
||||
host.hostname,
|
||||
scriptPath,
|
||||
server.hostname,
|
||||
args,
|
||||
) !== null
|
||||
) {
|
||||
workerScript.log(caller, () => `'${scriptname}' is already running on '${host.hostname}'`);
|
||||
workerScript.log(caller, () => `'${scriptPath}' is already running on '${server.hostname}'`);
|
||||
return 0;
|
||||
}
|
||||
|
||||
const singleRamUsage = runOpts.ramOverride ?? script.getRamUsage(host.scripts);
|
||||
if (!singleRamUsage) {
|
||||
workerScript.log(caller, () => `Ram usage could not be calculated for ${scriptname}`);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Check if admin rights on host, fail if not.
|
||||
if (!host.hasAdminRights) {
|
||||
workerScript.log(caller, () => `You do not have root access on '${host.hostname}'`);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Calculate ram usage including thread count
|
||||
const ramUsage = singleRamUsage * runOpts.threads;
|
||||
|
||||
// Check if there is enough ram to run the script, fail if not.
|
||||
const ramAvailable = host.maxRam - host.ramUsed;
|
||||
if (ramUsage > ramAvailable + 0.001) {
|
||||
workerScript.log(
|
||||
caller,
|
||||
() =>
|
||||
`Cannot run script '${scriptname}' (t=${runOpts.threads}) on '${host.hostname}' because there is not enough available RAM!`,
|
||||
);
|
||||
return 0;
|
||||
}
|
||||
// Able to run script
|
||||
workerScript.log(
|
||||
caller,
|
||||
() => `'${scriptname}' on '${host.hostname}' with ${runOpts.threads} threads and args: ${arrayToString(args)}.`,
|
||||
() => `'${scriptPath}' on '${server.hostname}' with ${runOpts.threads} threads and args: ${arrayToString(args)}.`,
|
||||
);
|
||||
const runningScriptObj = new RunningScript(script, singleRamUsage, args);
|
||||
const runningScriptObj = result.runningScript;
|
||||
runningScriptObj.parent = workerScript.pid;
|
||||
runningScriptObj.threads = runOpts.threads;
|
||||
runningScriptObj.temporary = runOpts.temporary;
|
||||
|
||||
return startWorkerScript(runningScriptObj, host, workerScript);
|
||||
return startWorkerScript(runningScriptObj, server, workerScript);
|
||||
}
|
||||
|
||||
@@ -47,6 +47,7 @@ import {
|
||||
import { SpecialServers } from "../../Server/data/SpecialServers";
|
||||
import { SnackbarEvents } from "../../ui/React/Snackbar";
|
||||
import { ToastVariant } from "@enums";
|
||||
import { createRunningScriptInstance, startWorkerScript } from "../../NetscriptWorker";
|
||||
|
||||
// Extend acorn-walk to support TypeScript nodes.
|
||||
extendAcornWalkForTypeScriptNodes(walk.base);
|
||||
@@ -219,6 +220,32 @@ function Root(props: IProps): React.ReactElement {
|
||||
rerender();
|
||||
}, [rerender]);
|
||||
|
||||
const run = useCallback(() => {
|
||||
if (currentScript === null) {
|
||||
return;
|
||||
}
|
||||
// Check if "currentScript" is a script. It may be a text file.
|
||||
if (!hasScriptExtension(currentScript.path)) {
|
||||
dialogBoxCreate(`Cannot run ${currentScript.path}. It is not a script.`);
|
||||
return;
|
||||
}
|
||||
// Check if the current script's server is valid.
|
||||
const server = GetServer(currentScript.hostname);
|
||||
if (server === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Always save before doing anything else.
|
||||
save();
|
||||
|
||||
const result = createRunningScriptInstance(server, currentScript.path, null, 1, []);
|
||||
if (!result.success) {
|
||||
dialogBoxCreate(result.message);
|
||||
return;
|
||||
}
|
||||
startWorkerScript(result.runningScript, server);
|
||||
}, [save]);
|
||||
|
||||
useEffect(() => {
|
||||
function keydown(event: KeyboardEvent): void {
|
||||
if (Settings.DisableHotkeys) {
|
||||
@@ -234,10 +261,14 @@ function Root(props: IProps): React.ReactElement {
|
||||
event.preventDefault();
|
||||
Router.toPage(Page.Terminal);
|
||||
}
|
||||
if (keyBindingTypes.has(ScriptEditorAction.Run)) {
|
||||
event.preventDefault();
|
||||
run();
|
||||
}
|
||||
}
|
||||
document.addEventListener("keydown", keydown);
|
||||
return () => document.removeEventListener("keydown", keydown);
|
||||
}, [save]);
|
||||
}, [save, run]);
|
||||
|
||||
function infLoop(ast: AST, code: string): void {
|
||||
if (editorRef.current === null || currentScript === null || isLegacyScript(currentScript.path)) {
|
||||
@@ -568,7 +599,7 @@ function Root(props: IProps): React.ReactElement {
|
||||
|
||||
{statusBarRef.current}
|
||||
|
||||
<Toolbar onSave={save} editor={editorRef.current} />
|
||||
<Toolbar onSave={save} onRun={run} editor={editorRef.current} />
|
||||
</div>
|
||||
{!currentScript && <NoOpenScripts />}
|
||||
</>
|
||||
|
||||
@@ -29,9 +29,10 @@ type IStandaloneCodeEditor = monaco.editor.IStandaloneCodeEditor;
|
||||
interface IProps {
|
||||
editor: IStandaloneCodeEditor | null;
|
||||
onSave: () => void;
|
||||
onRun: () => void;
|
||||
}
|
||||
|
||||
export function Toolbar({ editor, onSave }: IProps) {
|
||||
export function Toolbar({ editor, onSave, onRun }: IProps) {
|
||||
const [ramInfoOpen, { on: openRAMInfo, off: closeRAMInfo }] = useBoolean(false);
|
||||
const [optionsOpen, { on: openOptions, off: closeOptions }] = useBoolean(false);
|
||||
|
||||
@@ -76,6 +77,11 @@ export function Toolbar({ editor, onSave }: IProps) {
|
||||
Terminal
|
||||
</Button>
|
||||
</Tooltip>
|
||||
<Tooltip title={parseKeyCombinationsToString(CurrentKeyBindings[ScriptEditorAction.Run])}>
|
||||
<Button sx={{ mr: 1 }} onClick={onRun}>
|
||||
Run
|
||||
</Button>
|
||||
</Tooltip>
|
||||
<Typography>
|
||||
<NsApiDocumentationLink />
|
||||
</Typography>
|
||||
|
||||
@@ -232,6 +232,7 @@ export function SidebarRoot(props: { page: Page }): React.ReactElement {
|
||||
return canIPvGO;
|
||||
case ScriptEditorAction.Save:
|
||||
case ScriptEditorAction.GoToTerminal:
|
||||
case ScriptEditorAction.Run:
|
||||
return false;
|
||||
default:
|
||||
throwIfReachable(keyBindingType);
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
import { Terminal } from "../../Terminal";
|
||||
import { BaseServer } from "../../Server/BaseServer";
|
||||
import { LogBoxEvents } from "../../ui/React/LogBoxManager";
|
||||
import { startWorkerScript } from "../../NetscriptWorker";
|
||||
import { RunningScript } from "../../Script/RunningScript";
|
||||
import { createRunningScriptInstance, startWorkerScript } from "../../NetscriptWorker";
|
||||
import libarg from "arg";
|
||||
import { formatRam } from "../../ui/formatNumber";
|
||||
import { ScriptArg } from "@nsdefs";
|
||||
import { isPositiveInteger } from "../../types";
|
||||
import { ScriptFilePath, isLegacyScript } from "../../Paths/ScriptFilePath";
|
||||
@@ -13,11 +11,11 @@ import { roundToTwo } from "../../utils/helpers/roundToTwo";
|
||||
import { RamCostConstants } from "../../Netscript/RamCostGenerator";
|
||||
import { pluralize } from "../../utils/I18nUtils";
|
||||
|
||||
export function runScript(path: ScriptFilePath, commandArgs: (string | number | boolean)[], server: BaseServer): void {
|
||||
// This takes in the absolute filepath, see "run.ts"
|
||||
const script = server.scripts.get(path);
|
||||
if (!script) return Terminal.error(`Script ${path} does not exist on this server.`);
|
||||
|
||||
export function runScript(
|
||||
scriptPath: ScriptFilePath,
|
||||
commandArgs: (string | number | boolean)[],
|
||||
server: BaseServer,
|
||||
): void {
|
||||
const runArgs = { "--tail": Boolean, "-t": Number, "--ram-override": Number };
|
||||
let flags: {
|
||||
_: ScriptArg[];
|
||||
@@ -42,39 +40,32 @@ export function runScript(path: ScriptFilePath, commandArgs: (string | number |
|
||||
return Terminal.error("Invalid number of threads specified. Number of threads must be an integer greater than 0");
|
||||
}
|
||||
if (ramOverride != null && (isNaN(ramOverride) || ramOverride < RamCostConstants.Base)) {
|
||||
return Terminal.error(
|
||||
Terminal.error(
|
||||
`Invalid ram override specified. Ram override must be a number greater than ${RamCostConstants.Base}`,
|
||||
);
|
||||
return;
|
||||
}
|
||||
if (!server.hasAdminRights) return Terminal.error("Need root access to run script");
|
||||
|
||||
// Todo: Switch out arg for something with typescript support
|
||||
const args = flags._;
|
||||
|
||||
const singleRamUsage = ramOverride ?? script.getRamUsage(server.scripts);
|
||||
if (!singleRamUsage) {
|
||||
return Terminal.error(`Error while calculating ram usage for this script. ${script.ramCalculationError}`);
|
||||
}
|
||||
|
||||
const ramUsage = singleRamUsage * numThreads;
|
||||
const ramAvailable = server.maxRam - server.ramUsed;
|
||||
|
||||
if (ramUsage > ramAvailable + 0.001) {
|
||||
return Terminal.error(
|
||||
"This machine does not have enough RAM to run this script" +
|
||||
(numThreads === 1 ? "" : ` with ${numThreads} threads`) +
|
||||
`. Script requires ${formatRam(ramUsage)} of RAM`,
|
||||
);
|
||||
const result = createRunningScriptInstance(server, scriptPath, ramOverride, numThreads, args);
|
||||
if (!result.success) {
|
||||
Terminal.error(result.message);
|
||||
return;
|
||||
}
|
||||
|
||||
// Able to run script
|
||||
const runningScript = new RunningScript(script, singleRamUsage, args);
|
||||
const runningScript = result.runningScript;
|
||||
runningScript.threads = numThreads;
|
||||
|
||||
const success = startWorkerScript(runningScript, server);
|
||||
if (!success) return Terminal.error(`Failed to start script`);
|
||||
if (!success) {
|
||||
Terminal.error(`Failed to start script`);
|
||||
return;
|
||||
}
|
||||
|
||||
if (isLegacyScript(path)) {
|
||||
if (isLegacyScript(scriptPath)) {
|
||||
sendDeprecationNotice();
|
||||
}
|
||||
Terminal.print(
|
||||
|
||||
@@ -6,6 +6,7 @@ import { KEY } from "./KeyboardEventKey";
|
||||
export enum ScriptEditorAction {
|
||||
Save = "ScriptEditor-Save",
|
||||
GoToTerminal = "ScriptEditor-GoToTerminal",
|
||||
Run = "ScriptEditor-Run",
|
||||
}
|
||||
|
||||
export const SpoilerKeyBindingTypes = [
|
||||
@@ -220,7 +221,7 @@ export const DefaultKeyBindings: Record<KeyBindingType, [KeyCombination | null,
|
||||
},
|
||||
null,
|
||||
],
|
||||
"ScriptEditor-Save": [
|
||||
[ScriptEditorAction.Save]: [
|
||||
{
|
||||
control: true,
|
||||
alt: false,
|
||||
@@ -236,7 +237,7 @@ export const DefaultKeyBindings: Record<KeyBindingType, [KeyCombination | null,
|
||||
key: "S",
|
||||
},
|
||||
],
|
||||
"ScriptEditor-GoToTerminal": [
|
||||
[ScriptEditorAction.GoToTerminal]: [
|
||||
{
|
||||
control: true,
|
||||
alt: false,
|
||||
@@ -252,6 +253,16 @@ export const DefaultKeyBindings: Record<KeyBindingType, [KeyCombination | null,
|
||||
key: "B",
|
||||
},
|
||||
],
|
||||
[ScriptEditorAction.Run]: [
|
||||
{
|
||||
control: true,
|
||||
alt: false,
|
||||
shift: false,
|
||||
meta: false,
|
||||
key: "Q",
|
||||
},
|
||||
null,
|
||||
],
|
||||
};
|
||||
|
||||
// This is the set of key bindings merged from DefaultKeyBindings and Settings.KeyBindings.
|
||||
|
||||
@@ -1,12 +1,21 @@
|
||||
import type { Script } from "../../../src/Script/Script";
|
||||
import type { ScriptFilePath } from "../../../src/Paths/ScriptFilePath";
|
||||
import { startWorkerScript } from "../../../src/NetscriptWorker";
|
||||
import { runScriptFromScript, startWorkerScript } from "../../../src/NetscriptWorker";
|
||||
import { workerScripts } from "../../../src/Netscript/WorkerScripts";
|
||||
import { config as EvaluatorConfig } from "../../../src/NetscriptJSEvaluator";
|
||||
import { Server } from "../../../src/Server/Server";
|
||||
import { RunningScript } from "../../../src/Script/RunningScript";
|
||||
import { AddToAllServers, DeleteServer } from "../../../src/Server/AllServers";
|
||||
import { AddToAllServers, DeleteServer, GetServerOrThrow } from "../../../src/Server/AllServers";
|
||||
import { AlertEvents } from "../../../src/ui/React/AlertManager";
|
||||
import { initGameEnvironment, setupBasicTestingEnvironment } from "./Utilities";
|
||||
import { Terminal } from "../../../src/Terminal";
|
||||
import { runScript } from "../../../src/Terminal/commands/runScript";
|
||||
import { Player } from "@player";
|
||||
import { resetPidCounter } from "../../../src/Netscript/Pid";
|
||||
import { SpecialServers } from "../../../src/Server/data/SpecialServers";
|
||||
import { WorkerScript } from "../../../src/Netscript/WorkerScript";
|
||||
import { NetscriptFunctions } from "../../../src/NetscriptFunctions";
|
||||
import type { PositiveInteger } from "../../../src/types";
|
||||
|
||||
declare const importActual: (typeof EvaluatorConfig)["doImport"];
|
||||
|
||||
@@ -24,6 +33,12 @@ global.URL.revokeObjectURL = function () {};
|
||||
// and tends to crash even if you do.
|
||||
EvaluatorConfig.doImport = importActual;
|
||||
|
||||
global.URL.createObjectURL = function (blob) {
|
||||
return "data:text/javascript," + encodeURIComponent((blob as unknown as { code: string }).code);
|
||||
};
|
||||
|
||||
initGameEnvironment();
|
||||
|
||||
test.each([
|
||||
{
|
||||
name: "NS1 test /w import",
|
||||
@@ -76,10 +91,6 @@ test.each([
|
||||
],
|
||||
},
|
||||
])("Netscript execution: $name", async function ({ expected: expectedLog, scripts }) {
|
||||
global.URL.createObjectURL = function (blob) {
|
||||
return "data:text/javascript," + encodeURIComponent((blob as unknown as { code: string }).code);
|
||||
};
|
||||
|
||||
let server = {} as Server;
|
||||
const eventDelete = () => {};
|
||||
let alertDelete = () => {};
|
||||
@@ -119,3 +130,170 @@ test.each([
|
||||
alertDelete();
|
||||
}
|
||||
});
|
||||
|
||||
const testScriptPath = "test.js" as ScriptFilePath;
|
||||
const parentTestScriptPath = "parent_script.js" as ScriptFilePath;
|
||||
const runOptions = {
|
||||
threads: 1 as PositiveInteger,
|
||||
temporary: false,
|
||||
preventDuplicates: false,
|
||||
};
|
||||
|
||||
describe("runScript and runScriptFromScript", () => {
|
||||
let alertDelete: () => void;
|
||||
let alerted: Promise<unknown>;
|
||||
|
||||
beforeEach(() => {
|
||||
setupBasicTestingEnvironment();
|
||||
Terminal.clear();
|
||||
resetPidCounter();
|
||||
|
||||
alerted = new Promise((resolve) => {
|
||||
alertDelete = AlertEvents.subscribe((x) => resolve(x));
|
||||
});
|
||||
});
|
||||
afterEach(() => {
|
||||
alertDelete();
|
||||
});
|
||||
|
||||
describe("runScript", () => {
|
||||
describe("Success", () => {
|
||||
test("Normal", async () => {
|
||||
Player.getHomeComputer().writeToScriptFile(
|
||||
testScriptPath,
|
||||
`export async function main(ns) {
|
||||
const server = ns.getServer("home");
|
||||
ns.print(server.hostname);
|
||||
}`,
|
||||
);
|
||||
runScript(testScriptPath, [], Player.getHomeComputer());
|
||||
const workerScript = workerScripts.get(1);
|
||||
if (!workerScript) {
|
||||
throw new Error(`Invalid worker script`);
|
||||
}
|
||||
const result = await Promise.race([
|
||||
alerted,
|
||||
new Promise<void>((resolve) => (workerScript.atExit = new Map([["default", resolve]]))),
|
||||
]);
|
||||
expect(result).not.toBeDefined();
|
||||
expect(workerScript.scriptRef.logs[0]).toStrictEqual(SpecialServers.Home);
|
||||
});
|
||||
});
|
||||
describe("Failure", () => {
|
||||
test("Script does not exist", () => {
|
||||
runScript(testScriptPath, [], Player.getHomeComputer());
|
||||
expect((Terminal.outputHistory[1] as { text: string }).text).toContain(
|
||||
`Script ${testScriptPath} does not exist on home`,
|
||||
);
|
||||
});
|
||||
test("No root access", () => {
|
||||
const server = GetServerOrThrow("n00dles");
|
||||
server.writeToScriptFile(
|
||||
testScriptPath,
|
||||
`export async function main(ns) {
|
||||
}`,
|
||||
);
|
||||
runScript(testScriptPath, [], server);
|
||||
expect((Terminal.outputHistory[1] as { text: string }).text).toContain(
|
||||
`You do not have root access on ${server.hostname}`,
|
||||
);
|
||||
});
|
||||
test("Cannot calculate RAM", () => {
|
||||
Player.getHomeComputer().writeToScriptFile(
|
||||
testScriptPath,
|
||||
`export async function main(ns) {
|
||||
{
|
||||
}`,
|
||||
);
|
||||
runScript(testScriptPath, [], Player.getHomeComputer());
|
||||
expect((Terminal.outputHistory[1] as { text: string }).text).toContain(
|
||||
`Cannot calculate RAM usage of ${testScriptPath}`,
|
||||
);
|
||||
});
|
||||
test("Not enough RAM", () => {
|
||||
Player.getHomeComputer().writeToScriptFile(
|
||||
testScriptPath,
|
||||
`export async function main(ns) {
|
||||
ns.ramOverride(1024);
|
||||
}`,
|
||||
);
|
||||
runScript(testScriptPath, [], Player.getHomeComputer());
|
||||
expect((Terminal.outputHistory[1] as { text: string }).text).toContain("This script requires 1.02TB of RAM");
|
||||
});
|
||||
test("Thrown error in main function", async () => {
|
||||
/**
|
||||
* Suppress console.error(). When there is a thrown error in the player's script, we print it to the console. In
|
||||
* this test, we intentionally throw an error, so we can ignore it.
|
||||
*/
|
||||
jest.spyOn(console, "error").mockImplementation(jest.fn());
|
||||
const errorMessage = `Test error ${Date.now()}`;
|
||||
Player.getHomeComputer().writeToScriptFile(
|
||||
testScriptPath,
|
||||
`export async function main(ns) {
|
||||
throw new Error("${errorMessage}");
|
||||
}`,
|
||||
);
|
||||
runScript(testScriptPath, [], Player.getHomeComputer());
|
||||
const workerScript = workerScripts.get(1);
|
||||
if (!workerScript) {
|
||||
throw new Error(`Invalid worker script`);
|
||||
}
|
||||
const result = await Promise.race([
|
||||
alerted,
|
||||
new Promise<void>((resolve) => (workerScript.atExit = new Map([["default", resolve]]))),
|
||||
]);
|
||||
expect(result).toBeDefined();
|
||||
expect(workerScript.scriptRef.logs[0]).toContain(errorMessage);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("runScriptFromScript", () => {
|
||||
let parentWorkerScript: WorkerScript;
|
||||
beforeEach(() => {
|
||||
// Set up parentWorkerScript for passing to runScriptFromScript.
|
||||
const home = GetServerOrThrow(SpecialServers.Home);
|
||||
home.writeToScriptFile(parentTestScriptPath, "");
|
||||
const script = home.scripts.get(parentTestScriptPath);
|
||||
if (!script) {
|
||||
throw new Error("Invalid script");
|
||||
}
|
||||
const runningScript = new RunningScript(script, 4);
|
||||
parentWorkerScript = new WorkerScript(runningScript, 1, NetscriptFunctions);
|
||||
home.runScript(runningScript);
|
||||
});
|
||||
|
||||
describe("Success", () => {
|
||||
test("Normal", async () => {
|
||||
Player.getHomeComputer().writeToScriptFile(
|
||||
testScriptPath,
|
||||
`export async function main(ns) {
|
||||
const server = ns.getServer("home");
|
||||
ns.print(server.hostname);
|
||||
}`,
|
||||
);
|
||||
runScriptFromScript("run", Player.getHomeComputer(), testScriptPath, [], parentWorkerScript, runOptions);
|
||||
const workerScript = workerScripts.get(1);
|
||||
if (!workerScript) {
|
||||
throw new Error(`Invalid worker script`);
|
||||
}
|
||||
const result = await Promise.race([
|
||||
alerted,
|
||||
new Promise<void>((resolve) => (workerScript.atExit = new Map([["default", resolve]]))),
|
||||
]);
|
||||
|
||||
expect(result).not.toBeDefined();
|
||||
expect(workerScript.scriptRef.logs[0]).toStrictEqual(SpecialServers.Home);
|
||||
});
|
||||
});
|
||||
describe("Failure", () => {
|
||||
test("Prevent duplicates", () => {
|
||||
runScriptFromScript("run", Player.getHomeComputer(), parentTestScriptPath, [], parentWorkerScript, {
|
||||
...runOptions,
|
||||
preventDuplicates: true,
|
||||
});
|
||||
expect(parentWorkerScript.scriptRef.logs[0]).toContain("is already running");
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user