bitburner-src/test/jest/Netscript/StaticRamParsingCalculation.test.js
JP Sugarbroad 01529a8347 fix typing conflict between jest and cypress
Cypress and Jest both define "expect", but they come from different
libraries (Cypress uses Chai, Jest uses its own thing). So we can't
include both of them in the tsconfig.json. Conveniently, however, we
don't need any of the test code to be part of the main project, so
it suffices to give these things their own tsconfig.json files.

That being done, adding "jest" to the global types lets us remove all
those imports.
2022-05-10 22:26:54 -07:00

328 lines
11 KiB
JavaScript

// Player is needed for calculating costs like Singularity functions, that depend on acquired source files
import { Player } from "../../../src/Player";
import { RamCostConstants } from "../../../src/Netscript/RamCostGenerator";
import { calculateRamUsage } from "../../../src/Script/RamCalculations";
import { Script } from "../../../src/Script/Script";
jest.mock(`!!raw-loader!../NetscriptDefinitions.d.ts`, () => "", {
virtual: true,
});
const ScriptBaseCost = RamCostConstants.ScriptBaseRamCost;
const HackCost = 0.1;
const GrowCost = 0.15;
const SleeveGetTaskCost = 4;
const HacknetCost = 4;
const CorpCost = 1024 - ScriptBaseCost;
describe("Parsing NetScript code to work out static RAM costs", function () {
// Tests numeric equality, allowing for floating point imprecision - and includes script base cost
function expectCost(val, expected) {
const expectedWithBase = expected + ScriptBaseCost;
expect(val).toBeGreaterThanOrEqual(expectedWithBase - 100 * Number.EPSILON);
expect(val).toBeLessThanOrEqual(expectedWithBase + 100 * Number.EPSILON);
}
describe("Single files with basic NS functions", function () {
it("Empty main function", async function () {
const code = `
export async function main(ns) { }
`;
const calculated = (await calculateRamUsage(Player, code, [])).cost;
expectCost(calculated, 0);
});
it("Free NS function directly in main", async function () {
const code = `
export async function main(ns) {
ns.print("Slum snakes r00l!");
}
`;
const calculated = (await calculateRamUsage(Player, code, [])).cost;
expectCost(calculated, 0);
});
it("Single simple base NS function directly in main", async function () {
const code = `
export async function main(ns) {
await ns.hack("joesguns");
}
`;
const calculated = (await calculateRamUsage(Player, code, [])).cost;
expectCost(calculated, HackCost);
});
it("Single simple base NS function directly in main with differing arg name", async function () {
const code = `
export async function main(X) {
await X.hack("joesguns");
}
`;
const calculated = (await calculateRamUsage(Player, code, [])).cost;
expectCost(calculated, HackCost);
});
it("Repeated simple base NS function directly in main", async function () {
const code = `
export async function main(ns) {
await ns.hack("joesguns");
await ns.hack("joesguns");
}
`;
const calculated = (await calculateRamUsage(Player, code, [])).cost;
expectCost(calculated, HackCost);
});
it("Multiple simple base NS functions directly in main", async function () {
const code = `
export async function main(ns) {
await ns.hack("joesguns");
await ns.grow("joesguns");
}
`;
const calculated = (await calculateRamUsage(Player, code, [])).cost;
expectCost(calculated, HackCost + GrowCost);
});
it("Simple base NS functions in a referenced function", async function () {
const code = `
export async function main(ns) {
doHacking(ns);
}
async function doHacking(ns) {
await ns.hack("joesguns");
}
`;
const calculated = (await calculateRamUsage(Player, code, [])).cost;
expectCost(calculated, HackCost);
});
it("Simple base NS functions in a referenced class", async function () {
const code = `
export async function main(ns) {
await new Hacker(ns).doHacking();
}
class Hacker {
ns;
constructor(ns) { this.ns = ns; }
async doHacking() { await this.ns.hack("joesguns"); }
}
`;
const calculated = (await calculateRamUsage(Player, code, [])).cost;
expectCost(calculated, HackCost);
});
it("Simple base NS functions in a referenced class", async function () {
const code = `
export async function main(ns) {
await new Hacker(ns).doHacking();
}
class Hacker {
#ns;
constructor(ns) { this.#ns = ns; }
async doHacking() { await this.#ns.hack("joesguns"); }
}
`;
const calculated = (await calculateRamUsage(Player, code, [])).cost;
expectCost(calculated, HackCost);
});
});
describe("Functions that can be confused with NS functions", function () {
it("Function 'get' that can be confused with Stanek.get", async function () {
const code = `
export async function main(ns) {
get();
}
function get() { return 0; }
`;
const calculated = (await calculateRamUsage(Player, code, [])).cost;
expectCost(calculated, 0);
});
it("Function 'purchaseNode' that can be confused with Hacknet.purchaseNode", async function () {
const code = `
export async function main(ns) {
purchaseNode();
}
function purchaseNode() { return 0; }
`;
const calculated = (await calculateRamUsage(Player, code, [])).cost;
// Works at present, because the parser checks the namespace only, not the function name
expectCost(calculated, 0);
});
// TODO: once we fix static parsing this should pass
it.skip("Function 'getTask' that can be confused with Sleeve.getTask", async function () {
const code = `
export async function main(ns) {
getTask();
}
function getTask() { return 0; }
`;
const calculated = (await calculateRamUsage(Player, code, [])).cost;
expectCost(calculated, 0);
});
});
describe("Single files with non-core NS functions", function () {
it("Hacknet NS function with a cost from namespace", async function () {
const code = `
export async function main(ns) {
ns.hacknet.purchaseNode(0);
}
`;
const calculated = (await calculateRamUsage(Player, code, [])).cost;
expectCost(calculated, HacknetCost);
});
it("Corporation NS function with a cost from namespace", async function () {
const code = `
export async function main(ns) {
ns.corporation.getCorporation();
}
`;
const calculated = (await calculateRamUsage(Player, code, [])).cost;
expectCost(calculated, CorpCost);
});
it("Both Corporation and Hacknet functions", async function () {
const code = `
export async function main(ns) {
ns.corporation.getCorporation();
ns.hacknet.purchaseNode(0);
}
`;
const calculated = (await calculateRamUsage(Player, code, [])).cost;
expectCost(calculated, CorpCost + HacknetCost);
});
it("Sleeve functions with an individual cost", async function () {
const code = `
export async function main(ns) {
ns.sleeve.getTask(3);
}
`;
const calculated = (await calculateRamUsage(Player, code, [])).cost;
expectCost(calculated, SleeveGetTaskCost);
});
});
describe("Imported files", function () {
it("Simple imported function with no cost", async function () {
const libCode = `
export function dummy() { return 0; }
`;
const lib = new Script(Player, "libTest.js", libCode, []);
const code = `
import { dummy } from "libTest";
export async function main(ns) {
dummy();
}
`;
const calculated = (await calculateRamUsage(Player, code, [lib])).cost;
expectCost(calculated, 0);
});
it("Imported ns function", async function () {
const libCode = `
export async function doHack(ns) { return await ns.hack("joesguns"); }
`;
const lib = new Script(Player, "libTest.js", libCode, []);
const code = `
import { doHack } from "libTest";
export async function main(ns) {
await doHack(ns);
}
`;
const calculated = (await calculateRamUsage(Player, code, [lib])).cost;
expectCost(calculated, HackCost);
});
it("Importing a single function from a library that exports multiple", async function () {
const libCode = `
export async function doHack(ns) { return await ns.hack("joesguns"); }
export async function doGrow(ns) { return await ns.grow("joesguns"); }
`;
const lib = new Script(Player, "libTest.js", libCode, []);
const code = `
import { doHack } from "libTest";
export async function main(ns) {
await doHack(ns);
}
`;
const calculated = (await calculateRamUsage(Player, code, [lib])).cost;
expectCost(calculated, HackCost);
});
it("Importing all functions from a library that exports multiple", async function () {
const libCode = `
export async function doHack(ns) { return await ns.hack("joesguns"); }
export async function doGrow(ns) { return await ns.grow("joesguns"); }
`;
const lib = new Script(Player, "libTest.js", libCode, []);
const code = `
import * as test from "libTest";
export async function main(ns) {
await test.doHack(ns);
}
`;
const calculated = (await calculateRamUsage(Player, code, [lib])).cost;
expectCost(calculated, HackCost + GrowCost);
});
// TODO: once we fix static parsing this should pass
it.skip("Importing a function from a library that contains a class", async function () {
const libCode = `
export async function doHack(ns) { return await ns.hack("joesguns"); }
class Grower {
ns;
constructor(ns) { this.ns = ns; }
async doGrow() { return await this.ns.grow("joesguns"); }
}
`;
const lib = new Script(Player, "libTest.js", libCode, []);
const code = `
import * as test from "libTest";
export async function main(ns) {
await test.doHack(ns);
}
`;
const calculated = (await calculateRamUsage(Player, code, [lib])).cost;
expectCost(calculated, HackCost);
});
it("Importing a function from a library that creates a class in a function", async function () {
const libCode = `
export function createClass() {
class Grower {
ns;
constructor(ns) { this.ns = ns; }
async doGrow() { return await this.ns.grow("joesguns"); }
}
return Grower;
}
`;
const lib = new Script(Player, "libTest.js", libCode, []);
const code = `
import { createClass } from "libTest";
export async function main(ns) {
const grower = createClass();
const growerInstance = new grower(ns);
await growerInstance.doGrow();
}
`;
const calculated = (await calculateRamUsage(Player, code, [lib])).cost;
expectCost(calculated, GrowCost);
});
});
});