From 4f2f75762c5c8d3025df1d90ec4a6bdeff52afc2 Mon Sep 17 00:00:00 2001 From: danielyxie Date: Thu, 11 Jul 2019 19:37:17 -0700 Subject: [PATCH] Implemented 'kill by PID' functionality --- doc/source/basicgameplay/codingcontracts.rst | 8 +-- doc/source/basicgameplay/terminal.rst | 10 +-- doc/source/netscript/basicfunctions/exec.rst | 11 +++- doc/source/netscript/basicfunctions/kill.rst | 19 ++++++ doc/source/netscript/basicfunctions/run.rst | 11 +++- doc/source/netscript/netscriptjs.rst | 2 - src/Constants.ts | 7 +++ src/NetscriptFunctions.js | 66 ++++++++++++-------- src/NetscriptWorker.js | 65 +++++++++++-------- src/Server/BaseServer.ts | 27 ++++++-- src/Terminal.js | 20 ++++-- src/Terminal/DirectoryHelpers.ts | 6 +- src/Terminal/HelpText.ts | 17 ++--- test/Terminal/DirectoryTests.js | 8 +++ 14 files changed, 191 insertions(+), 86 deletions(-) diff --git a/doc/source/basicgameplay/codingcontracts.rst b/doc/source/basicgameplay/codingcontracts.rst index 42b948649..e7e8dc4d2 100644 --- a/doc/source/basicgameplay/codingcontracts.rst +++ b/doc/source/basicgameplay/codingcontracts.rst @@ -204,8 +204,8 @@ The list contains the name of (i.e. the value returned by | | | the string, the result should be an array with only an empty string. | | | | | | | | Examples: | -| | | ()())() -> [()()(), (())()] | -| | | (a)())() -> [(a)()(), (a())()] | +| | | ()())() -> [()()(), (())()] | +| | | (a)())() -> [(a)()(), (a())()] | | | | )( -> [""] | +------------------------------------+------------------------------------------------------------------------------------------+ | Find All Valid Math Expressions | | You are given a string which contains only digits between 0 and 9 as well as a target | @@ -218,8 +218,8 @@ The list contains the name of (i.e. the value returned by | | | | | | | Examples: | | | | Input: digits = "123", target = 6 | -| | | Output: [1+2+3, 1*2*3] | +| | | Output: [1+2+3, 1*2*3] | | | | | | | | Input: digits = "105", target = 5 | -| | | Output: [1*0+5, 10-5] | +| | | Output: [1*0+5, 10-5] | +------------------------------------+------------------------------------------------------------------------------------------+ diff --git a/doc/source/basicgameplay/terminal.rst b/doc/source/basicgameplay/terminal.rst index 6eeb42e5b..b1afb7a24 100644 --- a/doc/source/basicgameplay/terminal.rst +++ b/doc/source/basicgameplay/terminal.rst @@ -312,9 +312,12 @@ kill ^^^^ $ kill [script name] [args...] + $ kill [pid] -Kill the script specified by the script name and arguments. Each argument must -be separated by a space. Remember that a running script is uniquely identified +Kill the script specified by the script filename and arguments OR by its PID. + +If you are killing the script using its filename and arguments, then each argument +must be separated by a space. Remember that a running script is uniquely identified by both its name and the arguments that are used to start it. So, if a script was ran with the following arguments:: @@ -324,8 +327,7 @@ Then to kill this script the same arguments would have to be used:: $ kill foo.script 50e3 sigma-cosmetics -Note that after issuing the 'kill' command for a script, it may take a few seconds for -the script to actually stop running. +If you are killing the script using its PID, then the PID argument must be numeric. killall ^^^^^^^ diff --git a/doc/source/netscript/basicfunctions/exec.rst b/doc/source/netscript/basicfunctions/exec.rst index 95967d933..9ded031b2 100644 --- a/doc/source/netscript/basicfunctions/exec.rst +++ b/doc/source/netscript/basicfunctions/exec.rst @@ -14,10 +14,15 @@ exec() Netscript Function Run a script as a separate process on a specified server. This is similar to the *run* function except that it can be used to run a script on any server, instead of just the current server. - Returns true if the script is successfully started, and false otherwise. + If the script was successfully started, then this functions returns the PID + of that script. Otherwise, it returns 0. - Running this function with a *numThreads* argument of 0 will return false without running the script. - However, running this function with a negative *numThreads* argument will cause a runtime error. + .. note:: PID stands for Process ID. The PID is a unique identifier for each script. + The PID will always be a positive integer. + + .. warning:: Running this function with a *numThreads* argument of 0 will return 0 without + running the script. However, running this function with a negative *numThreads* + argument will cause a runtime error. The simplest way to use the *exec* command is to call it with just the script name and the target server. The following example will try to run *generic-hack.script* on the *foodnstuff* server:: diff --git a/doc/source/netscript/basicfunctions/kill.rst b/doc/source/netscript/basicfunctions/kill.rst index 3c3579836..7d695628a 100644 --- a/doc/source/netscript/basicfunctions/kill.rst +++ b/doc/source/netscript/basicfunctions/kill.rst @@ -27,3 +27,22 @@ kill() Netscript Function The following will try to kill a script named *foo.script* on the current server that was ran with the arguments 1 and "foodnstuff":: kill("foo.script", getHostname(), 1, "foodnstuff"); + +.. js:function:: kill(scriptPid) + + :param number scriptPid: PID of the script to kill + :RAM cost: 0.5 GB + + Kills the script with the specified PID. Killing a script by its PID will typically + have better performance, especially if you have many scripts running. + + If this function successfully kills the specified script, then it will return true. + Otherwise, it will return false. + + *Examples:* + + The following example will try to kill the script with the PID 10:: + + if (kill(10)) { + print("Killed script with PID 10!"); + } diff --git a/doc/source/netscript/basicfunctions/run.rst b/doc/source/netscript/basicfunctions/run.rst index b4b9a414e..21f7fbe91 100644 --- a/doc/source/netscript/basicfunctions/run.rst +++ b/doc/source/netscript/basicfunctions/run.rst @@ -13,10 +13,15 @@ run() Netscript Function Run a script as a separate process. This function can only be used to run scripts located on the current server (the server running the script that calls this function). - Returns true if the script is successfully started, and false otherwise. + If the script was successfully started, then this functions returns the PID + of that script. Otherwise, it returns 0. - Running this function with a *numThreads* argument of 0 will return false without running the script. - However, running this function with a negative *numThreads* argument will cause a runtime error. + .. note:: PID stands for Process ID. The PID is a unique identifier for each script. + The PID will always be a positive integer. + + .. warning:: Running this function with a *numThreads* argument of 0 will return 0 without + running the script. However, running this function with a negative *numThreads* + argument will cause a runtime error. The simplest way to use the *run* command is to call it with just the script name. The following example will run 'foo.script' single-threaded with no arguments:: diff --git a/doc/source/netscript/netscriptjs.rst b/doc/source/netscript/netscriptjs.rst index e09c88e90..4ddea3a1c 100644 --- a/doc/source/netscript/netscriptjs.rst +++ b/doc/source/netscript/netscriptjs.rst @@ -51,8 +51,6 @@ Here is a summary of all rules you need to follow when writing Netscript JS code * grow * weaken * sleep - * run - * exec * prompt * wget diff --git a/src/Constants.ts b/src/Constants.ts index 1f9c95138..ede50f91a 100644 --- a/src/Constants.ts +++ b/src/Constants.ts @@ -230,10 +230,17 @@ export let CONSTANTS: IMap = { * Bug fix: workForFaction() function now properly accounts for disabled logs * When writing to a file, the write() function now casts the data being written to a string (using String()) * BitNode-selection page now shows what Source-File level you have for each BitNode + * Overloaded kill() function so that you can kill a script by its PID + * spawn() now only takes 10 seconds to run (decreased from 20 seconds) + * run() and exec() now return the PID of the newly-executed scripts, rather than a boolean + ** (A PID is just a positive integer) + * run(), exec(), and spawn() no longer need to be await-ed in NetscriptJS Misc Changes + * The 'kill' Terminal command can now kill a script by its PID * Added 'Solarized Dark' theme to CodeMirror editor * After Infiltration, you will now return to the company page rather than the city page * Bug fix: Stock Market UI should no longer crash for certain locale settings + * Bug fix: You can now properly remove unfinished programs (the *.exe-N%-INC files) ` } diff --git a/src/NetscriptFunctions.js b/src/NetscriptFunctions.js index 7d4deb32c..b6f55f64c 100644 --- a/src/NetscriptFunctions.js +++ b/src/NetscriptFunctions.js @@ -293,7 +293,7 @@ function NetscriptFunctions(workerScript) { if (fn != null && typeof fn === "string") { // Get Logs of another script if (ip == null) { ip = workerScript.serverIp; } - const server = getServer(ip, callingFnName); + const server = safeGetServer(ip, callingFnName); return findRunningScript(fn, scriptArgs, server); } @@ -973,6 +973,8 @@ function NetscriptFunctions(workerScript) { if (scriptname == null || threads == null) { throw makeRuntimeRejectMsg(workerScript, "Invalid scriptname or numThreads argument passed to spawn()"); } + + const spawnDelay = 10; setTimeoutRef(() => { if (scriptname === undefined) { throw makeRuntimeRejectMsg(workerScript, "spawn() call has incorrect number of arguments. Usage: spawn(scriptname, numThreads, [arg1], [arg2]...)"); @@ -990,40 +992,52 @@ function NetscriptFunctions(workerScript) { } return runScriptFromScript(scriptServer, scriptname, argsForNewScript, workerScript, threads); - }, 20e3); - if (workerScript.disableLogs.ALL == null && workerScript.disableLogs.spawn == null) { - workerScript.scriptRef.log("spawn() will execute " + scriptname + " in 20 seconds"); + }, spawnDelay * 1e3); + if (workerScript.shouldLog("spawn")) { + workerScript.scriptRef.log(`spawn() will execute ${scriptname} in ${spawnDelay} seconds`); } NetscriptFunctions(workerScript).exit(); }, - kill: function(filename, ip) { + kill: function(filename, ip, ...scriptArgs) { updateDynamicRam("kill", getRamCost("kill")); - if (filename === undefined || ip === undefined) { - throw makeRuntimeRejectMsg(workerScript, "kill() call has incorrect number of arguments. Usage: kill(scriptname, server, [arg1], [arg2]...)"); + + let res; + const killByPid = (typeof filename === "number"); + if (killByPid) { + // Kill by pid + res = killWorkerScript(filename); + } else { + // Kill by filename/ip + if (filename === undefined || ip === undefined) { + throw makeRuntimeRejectMsg(workerScript, "kill() call has incorrect number of arguments. Usage: kill(scriptname, server, [arg1], [arg2]...)"); + } + + const server = safeGetServer(ip); + const runningScriptObj = getRunningScript(filename, ip, "kill", scriptArgs); + if (runningScriptObj == null) { + workerScript.log(`tail() failed. ${getCannotFindRunningScriptErrorMessage(filename, ip, scriptArgs)}`) + return false; + } + + res = killWorkerScript(runningScriptObj, server.ip); } - var server = getServer(ip); - if (server == null) { - workerScript.scriptRef.log("kill() failed. Invalid IP or hostname passed in: " + ip); - throw makeRuntimeRejectMsg(workerScript, "kill() failed. Invalid IP or hostname passed in: " + ip); - } - var argsForKillTarget = []; - for (var i = 2; i < arguments.length; ++i) { - argsForKillTarget.push(arguments[i]); - } - var runningScriptObj = findRunningScript(filename, argsForKillTarget, server); - if (runningScriptObj == null) { - workerScript.scriptRef.log("kill() failed. No such script "+ filename + " on " + server.hostname + " with args: " + arrayToString(argsForKillTarget)); - return false; - } - var res = killWorkerScript(runningScriptObj, server.ip); + if (res) { - if (workerScript.disableLogs.ALL == null && workerScript.disableLogs.kill == null) { - workerScript.scriptRef.log("Killing " + filename + " on " + server.hostname + " with args: " + arrayToString(argsForKillTarget) + ". May take up to a few minutes for the scripts to die..."); + if (workerScript.shouldLog("kill")) { + if (killByPid) { + workerScript.log(`Killing script with PID ${filename}`); + } else { + workerScript.log(`Killing ${filename} on ${ip} with args: ${arrayToString(scriptArgs)}.`); + } } return true; } else { - if (workerScript.disableLogs.ALL == null && workerScript.disableLogs.kill == null) { - workerScript.scriptRef.log("kill() failed. No such script "+ filename + " on " + server.hostname + " with args: " + arrayToString(argsForKillTarget)); + if (workerScript.shouldLog("kill")) { + if (killByPid) { + workerScript.log(`kill() failed. No such script with PID ${filename}`); + } else { + workerScript.log(`kill() failed. No such script ${filename} on ${ip} with args: ${arrayToString(scriptArgs)}`); + } } return false; } diff --git a/src/NetscriptWorker.js b/src/NetscriptWorker.js index 66f709870..f188d7188 100644 --- a/src/NetscriptWorker.js +++ b/src/NetscriptWorker.js @@ -605,42 +605,53 @@ export function loadAllRunningScripts() { * Run a script from inside another script (run(), exec(), spawn(), etc.) */ export function runScriptFromScript(server, scriptname, args, workerScript, threads=1) { - //Check if the script is already running - let runningScriptObj = findRunningScript(scriptname, args, server); - if (runningScriptObj != null) { - workerScript.scriptRef.log(scriptname + " is already running on " + server.hostname); - return Promise.resolve(false); + // Sanitize arguments + if (!(workerScript instanceof WorkerScript)) { + return 0; } - //'null/undefined' arguments are not allowed + if (typeof scriptname !== "string" || !Array.isArray(args)) { + workerScript.log(`ERROR: runScriptFromScript() failed due to invalid arguments`); + console.error(`runScriptFromScript() failed due to invalid arguments`); + return 0; + } + + // Check if the script is already running + let runningScriptObj = server.getRunningScript(scriptname, args); + if (runningScriptObj != null) { + workerScript.log(`${scriptname} is already running on ${server.hostname}`); + return 0; + } + + // 'null/undefined' arguments are not allowed for (let i = 0; i < args.length; ++i) { if (args[i] == null) { - workerScript.scriptRef.log("ERROR: Cannot execute a script with null/undefined as an argument"); - return Promise.resolve(false); + workerScript.log("ERROR: Cannot execute a script with null/undefined as an argument"); + return 0; } } - //Check if the script exists and if it does run it + // Check if the script exists and if it does run it for (let i = 0; i < server.scripts.length; ++i) { - if (server.scripts[i].filename == scriptname) { - //Check for admin rights and that there is enough RAM availble to run - var script = server.scripts[i]; - var ramUsage = script.ramUsage; - threads = Math.round(Number(threads)); //Convert to number and round - if (threads === 0) { return Promise.resolve(false); } + if (server.scripts[i].filename === scriptname) { + // Check for admin rights and that there is enough RAM availble to run + const script = server.scripts[i]; + let ramUsage = script.ramUsage; + threads = Math.round(Number(threads)); + if (threads === 0) { return 0; } ramUsage = ramUsage * threads; - var ramAvailable = server.maxRam - server.ramUsed; + const ramAvailable = server.maxRam - server.ramUsed; if (server.hasAdminRights == false) { - workerScript.scriptRef.log("Cannot run script " + scriptname + " on " + server.hostname + " because you do not have root access!"); - return Promise.resolve(false); + workerScript.log(`Cannot run script ${scriptname} on ${server.hostname} because you do not have root access!`); + return 0; } else if (ramUsage > ramAvailable){ - workerScript.scriptRef.log("Cannot run script " + scriptname + "(t=" + threads + ") on " + server.hostname + " because there is not enough available RAM!"); - return Promise.resolve(false); + workerScript.log(`Cannot run script ${scriptname} (t=${threads}) on ${server.hostname} because there is not enough available RAM!`); + return 0; } else { - //Able to run script + // Able to run script if (workerScript.disableLogs.ALL == null && workerScript.disableLogs.exec == null && workerScript.disableLogs.run == null && workerScript.disableLogs.spawn == null) { - workerScript.scriptRef.log(`Running script: ${scriptname} on ${server.hostname} with ${threads} threads and args: ${arrayToString(args)}. May take a few seconds to start up...`); + workerScript.log(`Running script: ${scriptname} on ${server.hostname} with ${threads} threads and args: ${arrayToString(args)}.`); } let runningScriptObj = new RunningScript(script, args); runningScriptObj.threads = threads; @@ -649,10 +660,14 @@ export function runScriptFromScript(server, scriptname, args, workerScript, thre // Push onto runningScripts. // This has to come after addWorkerScript() because that fn updates RAM usage server.runScript(runningScriptObj, Player.hacknet_node_money_mult); - return Promise.resolve(true); + + // Once the WorkerScript is constructed in addWorkerScript(), the RunningScript + // object should have a PID assigned to it, so we return that + return runningScriptObj.pid; } } } - workerScript.scriptRef.log("Could not find script " + scriptname + " on " + server.hostname); - return Promise.resolve(false); + + workerScript.log(`Could not find script ${scriptname} on ${server.hostname}`); + return 0; } diff --git a/src/Server/BaseServer.ts b/src/Server/BaseServer.ts index 143570abf..f5351787f 100644 --- a/src/Server/BaseServer.ts +++ b/src/Server/BaseServer.ts @@ -12,6 +12,7 @@ import { IReturnStatus } from "../types"; import { isScriptFilename } from "../Script/ScriptHelpersTS"; import { createRandomIp } from "../../utils/IPAddress"; +import { compareArrays } from "../../utils/helpers/compareArrays"; interface IConstructorParams { adminRights?: boolean; @@ -113,8 +114,27 @@ export class BaseServer { return null; } - // Given the name of the script, returns the corresponding - // script object on the server (if it exists) + /** + * Find an actively running script on this server + * @param scriptName - Filename of script to search for + * @param scriptArgs - Arguments that script is being run with + * @returns RunningScript for the specified active script + * Returns null if no such script can be found + */ + getRunningScript(scriptName: string, scriptArgs: any[]): RunningScript | null { + for (let rs of this.runningScripts) { + if (rs.filename === scriptName && compareArrays(rs.args, scriptArgs)) { + return rs; + } + } + + return null; + } + + /** + * Given the name of the script, returns the corresponding + * Script object on the server (if it exists) + */ getScript(scriptName: string): Script | null { for (let i = 0; i < this.scripts.length; i++) { if (this.scripts[i].filename === scriptName) { @@ -129,7 +149,6 @@ export class BaseServer { * Returns boolean indicating whether the given script is running on this server */ isRunning(fn: string): boolean { - // Check that the script isnt currently running for (const runningScriptObj of this.runningScripts) { if (runningScriptObj.filename === fn) { return true; @@ -157,7 +176,7 @@ export class BaseServer { * @returns {IReturnStatus} Return status object indicating whether or not file was deleted */ removeFile(fn: string): IReturnStatus { - if (fn.endsWith(".exe")) { + if (fn.endsWith(".exe") || fn.match(/^.+\.exe-\d+(?:\.\d*)?%-INC$/) != null) { for (let i = 0; i < this.programs.length; ++i) { if (this.programs[i] === fn) { this.programs.splice(i, 1); diff --git a/src/Terminal.js b/src/Terminal.js index ae05244bb..0bca33d4c 100644 --- a/src/Terminal.js +++ b/src/Terminal.js @@ -1150,7 +1150,7 @@ let Terminal = { killWorkerScript(s.runningScripts[i], s.ip, false); } WorkerScriptStartStopEventEmitter.emitEvent(); - post("Killing all running scripts. May take up to a few minutes for the scripts to die..."); + post("Killing all running scripts"); break; } case "ls": { @@ -1573,19 +1573,32 @@ let Terminal = { return; } + // Kill by PID + if (typeof commandArray[1] === "number") { + const pid = commandArray[1]; + const res = killWorkerScript(pid); + if (res) { + post(`Killing script with PID ${pid}`); + } else { + post(`Failed to kill script with PID ${pid}. No such script exists`); + } + + return; + } + const s = Player.getCurrentServer(); const scriptName = Terminal.getFilepath(commandArray[1]); const args = []; for (let i = 2; i < commandArray.length; ++i) { args.push(commandArray[i]); } - const runningScript = findRunningScript(scriptName, args, s); + const runningScript = s.getRunningScript(scriptName, args); if (runningScript == null) { postError("No such script is running. Nothing to kill"); return; } killWorkerScript(runningScript, s.ip); - post(`Killing ${scriptName}. May take up to a few seconds for the scripts to die...`); + post(`Killing ${scriptName}`); } catch(e) { Terminal.postThrownError(e); } @@ -2291,7 +2304,6 @@ let Terminal = { } else { // Able to run script post("Running script with " + numThreads + " thread(s) and args: " + arrayToString(args) + "."); - post("May take a few seconds to start up the process..."); var runningScriptObj = new RunningScript(script, args); runningScriptObj.threads = numThreads; diff --git a/src/Terminal/DirectoryHelpers.ts b/src/Terminal/DirectoryHelpers.ts index 0957b18d0..0a3692e54 100644 --- a/src/Terminal/DirectoryHelpers.ts +++ b/src/Terminal/DirectoryHelpers.ts @@ -36,9 +36,9 @@ export function removeTrailingSlash(s: string): string { * not the entire filepath */ export function isValidFilename(filename: string): boolean { - // Allows alphanumerics, hyphens, underscores. + // Allows alphanumerics, hyphens, underscores, and percentage signs // Must have a file extension - const regex = /^[.a-zA-Z0-9_-]+[.][.a-zA-Z0-9]+$/; + const regex = /^[.a-zA-Z0-9_-]+[.][a-zA-Z0-9]+(?:-\d+(?:\.\d*)?%-INC)?$/; // match() returns null if no match is found return filename.match(regex) != null; @@ -49,7 +49,7 @@ export function isValidFilename(filename: string): boolean { * not an entire path */ export function isValidDirectoryName(name: string): boolean { - // Allows alphanumerics, hyphens and underscores. + // Allows alphanumerics, hyphens, underscores, and percentage signs. // Name can begin with a single period, but otherwise cannot have any const regex = /^.?[a-zA-Z0-9_-]+$/; diff --git a/src/Terminal/HelpText.ts b/src/Terminal/HelpText.ts index 70bb96756..b006a7296 100644 --- a/src/Terminal/HelpText.ts +++ b/src/Terminal/HelpText.ts @@ -20,7 +20,7 @@ export const TerminalHelpText: string = "home Connect to home computer
" + "hostname Displays the hostname of the machine
" + "ifconfig Displays the IP address of the machine
" + - "kill [script] [args...] Stops the specified script on the current server
" + + "kill [script/pid] [args...] Stops the specified script on the current server
" + "killall Stops all running scripts on the current machine
" + "ls [dir] [| grep pattern] Displays all files on the machine
" + "lscpu Displays the number of CPU cores on the machine
" + @@ -128,15 +128,16 @@ export const HelpTexts: IMap = { ifconfig: "ipconfig
" + "Prints the IP address of the current server", kill: "kill [script name] [args...]
" + - "Kill the script specified by the script name and arguments. Each argument must be separated by " + - "a space. Remember that a running script is uniquely identified by " + - "both its name and the arguments that are used to start it. So, if a script was ran with the following arguments:

" + + "kill [pid]
" + + "Kill the script specified by the script name and arguments OR by its PID.

" + + "If you are killing the script using its filename and arguments, then each " + + "argument must be separated by a space. Remember that a running script is " + + "uniquely identified by both its name and the arguments that are used to start " + + "it. So, if a script was ran with the following arguments:

" + "run foo.script 1 sigma-cosmetics

" + "Then to kill this script the same arguments would have to be used:

" + "kill foo.script 1 sigma-cosmetics

" + - "Note that after issuing the 'kill' command for a script, it may take a while for the script to actually stop running. " + - "This will happen if the script is in the middle of a command such as grow() or weaken() that takes time to execute. " + - "The script will not be stopped/killed until after that time has elapsed.", + "If you are killing the script using its PID, then the PID argument must be numeric", killall: "killall
" + "Kills all scripts on the current server. " + "Note that after the 'kill' command is issued for a script, it may take a while for the script to actually stop running. " + @@ -168,7 +169,7 @@ export const HelpTexts: IMap = { "Move the source file to the specified destination. This can also be used to rename files. " + "This command only works for scripts and text files (.txt). This command CANNOT be used to " + "convert to different file types

" + - "Note that, unlike the Linux 'mv' command, the destination argument must be the " + + "Note that, unlike the Linux 'mv' command, the destination argument must be the " + "full filepath. " + "Examples:

" + "mv hacking-controller.script scripts/hacking-controller.script
" + diff --git a/test/Terminal/DirectoryTests.js b/test/Terminal/DirectoryTests.js index c62b28f14..e8516ca18 100644 --- a/test/Terminal/DirectoryTests.js +++ b/test/Terminal/DirectoryTests.js @@ -67,6 +67,10 @@ describe("Terminal Directory Tests", function() { expect(isValidFilename("_foo.lit")).to.equal(true); expect(isValidFilename("mult.periods.script")).to.equal(true); expect(isValidFilename("mult.per-iods.again.script")).to.equal(true); + expect(isValidFilename("BruteSSH.exe-50%-INC")).to.equal(true); + expect(isValidFilename("DeepscanV1.exe-1.01%-INC")).to.equal(true); + expect(isValidFilename("DeepscanV2.exe-1.00%-INC")).to.equal(true); + expect(isValidFilename("AutoLink.exe-1.%-INC")).to.equal(true); }); it("should return false for invalid filenames", function() { @@ -79,6 +83,10 @@ describe("Terminal Directory Tests", function() { expect(isValidFilename("foo._script")).to.equal(false); expect(isValidFilename("foo.hyphened-ext")).to.equal(false); expect(isValidFilename("")).to.equal(false); + expect(isValidFilename("AutoLink-1.%-INC.exe")).to.equal(false); + expect(isValidFilename("AutoLink.exe-1.%-INC.exe")).to.equal(false); + expect(isValidFilename("foo%.exe")).to.equal(false); + expect(isValidFilename("-1.00%-INC")).to.equal(false); }); });