diff --git a/src/tests.rs b/src/tests.rs index e4d943b..89ef6dc 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -75,7 +75,12 @@ create_tests! { process_cwd: "process/cwd", process_env: "process/env", process_exit: "process/exit", - process_spawn: "process/spawn", + process_spawn_async: "process/spawn/async", + process_spawn_basic: "process/spawn/basic", + process_spawn_cwd: "process/spawn/cwd", + process_spawn_shell: "process/spawn/shell", + process_spawn_stdin: "process/spawn/stdin", + process_spawn_stdio: "process/spawn/stdio", require_async: "require/tests/async", require_async_background: "require/tests/async_background", diff --git a/tests/process/spawn.luau b/tests/process/spawn.luau deleted file mode 100644 index 1eda09d..0000000 --- a/tests/process/spawn.luau +++ /dev/null @@ -1,179 +0,0 @@ -local process = require("@lune/process") -local stdio = require("@lune/stdio") -local task = require("@lune/task") - --- Spawning a child process should work, with options - -local thread = task.delay(1, function() - stdio.ewrite("Spawning a process should take a reasonable amount of time\n") - task.wait(1) - process.exit(1) -end) - -local IS_WINDOWS = process.os == "windows" - -local result = process.spawn( - if IS_WINDOWS then "cmd" else "ls", - if IS_WINDOWS then { "/c", "dir" } else { "-a" } -) - -task.cancel(thread) - -assert(result.ok, "Failed to spawn child process") - -assert(result.stderr == "", "Stderr was not empty") -assert(result.stdout ~= "", "Stdout was empty") - -assert(string.find(result.stdout, "Cargo.toml") ~= nil, "Missing Cargo.toml in output") -assert(string.find(result.stdout, ".gitignore") ~= nil, "Missing .gitignore in output") - --- It should also work the same when spawned using a shell --- Note that the default on Windows is Powershell which has different flags / behavior - -local shellResult = process.spawn("ls", { - if IS_WINDOWS then "-Force" else "-a", -}, { - shell = true, -}) - -assert(shellResult.ok, "Failed to spawn child process (shell)") - -assert(shellResult.stderr == "", "Stderr was not empty (shell)") -assert(shellResult.stdout ~= "", "Stdout was empty (shell)") - -assert(string.find(shellResult.stdout, "Cargo.toml") ~= nil, "Missing Cargo.toml in output (shell)") -assert(string.find(shellResult.stdout, ".gitignore") ~= nil, "Missing .gitignore in output (shell)") - -local pwdCommand = if IS_WINDOWS then "cmd" else "pwd" -local pwdArgs = if IS_WINDOWS then { "/c", "cd" } else {} - --- Make sure the cwd option actually uses the directory we want -local rootPwd = process.spawn(pwdCommand, pwdArgs, { - cwd = "/", -}).stdout -rootPwd = string.gsub(rootPwd, "^%s+", "") -rootPwd = string.gsub(rootPwd, "%s+$", "") - --- Windows: :\, Unix: / -local expectedRootPwd = if IS_WINDOWS then string.sub(rootPwd, 1, 1) .. ":\\" else "/" -if rootPwd ~= expectedRootPwd then - error( - string.format( - "Current working directory for child process was not set correctly!" - .. "\nExpected '%s', got '%s'", - expectedRootPwd, - rootPwd - ) - ) -end - --- Setting cwd should not change the cwd of this process - -local pwdBefore = process.spawn(pwdCommand, pwdArgs).stdout -process.spawn("ls", {}, { - cwd = "/", - shell = true, -}) -local pwdAfter = process.spawn(pwdCommand, pwdArgs).stdout -assert(pwdBefore == pwdAfter, "Current working directory changed after running child process") - ---[[ - Setting the cwd on a child process should properly - replace any leading ~ with the users real home dir -]] - -local homeDir1 = process.spawn("echo $HOME", nil, { - shell = true, -}).stdout - --- Powershell for windows uses `$pwd.Path` instead of `pwd` as pwd would return a PathInfo object, --- using $pwd.Path gets the Path property of the PathInfo object. -local homeDir2 = process.spawn(if IS_WINDOWS then "$pwd.Path" else "pwd", nil, { - shell = true, - cwd = "~", -}).stdout - -assert(#homeDir1 > 0, "Home dir from echo was empty") -assert(#homeDir2 > 0, "Home dir from pwd was empty") -assert(homeDir1 == homeDir2, "Home dirs did not match when performing tilde substitution") - ---[[ - Spawning a process should not block any lua thread(s) - - We test this by sleeping more than once concurrently - and then ensuring that the total time slept is more - than a single sleep but also less than 1.5 sleeps -]] - -local SLEEP_DURATION = 1 / 4 -local SLEEP_SAMPLES = 2 - --- Unfortunately we -local thread2 = task.delay(30, function() - stdio.ewrite("Spawning a sleep process should take a reasonable amount of time\n") - task.wait(1) - process.exit(1) -end) - -local sleepStart = os.clock() -local sleepCounter = 0 -for i = 1, SLEEP_SAMPLES, 1 do - task.spawn(function() - local args = { - -- Sleep command on Windows in Seconds has some weird behavior with decimals ... - tostring(SLEEP_DURATION * (IS_WINDOWS and 1000 or 1)), - } - if IS_WINDOWS then - -- ... so we use milliseconds instead. - table.insert(args, 1, "-Milliseconds") - end - -- Windows does not have `sleep` as a process, so we use powershell instead. - process.spawn("sleep", args, if IS_WINDOWS then { shell = true } else nil) - sleepCounter += 1 - end) -end -while sleepCounter < SLEEP_SAMPLES do - task.wait() -end - -task.cancel(thread2) - -assert( - (os.clock() - sleepStart) >= SLEEP_DURATION, - "Spawning a process that does blocking sleep did not sleep enough" -) - --- Inheriting stdio & environment variables should work - -local echoMessage = "Hello from child process!" -local echoResult = process.spawn("echo", { - if IS_WINDOWS then '"$Env:TEST_VAR"' else '"$TEST_VAR"', -}, { - env = { TEST_VAR = echoMessage }, - shell = if IS_WINDOWS then "powershell" else "bash", - stdio = "inherit", -}) - --- Windows echo adds \r\n (CRLF) and unix adds \n (LF) -local trailingAddition = if IS_WINDOWS then "\r\n" else "\n" -assert( - echoResult.stdout == (echoMessage .. trailingAddition), - "Inheriting stdio did not return proper output" -) - --- Passing stdin strings should work - -local stdinChild = process.spawn(not IS_WINDOWS and "xargs" or "powershell", { - "echo", -}, { - stdin = echoMessage .. (IS_WINDOWS and "\n\n" or ""), -}) - -local stdinChildOut = stdinChild.stdout -if IS_WINDOWS then - stdinChildOut = stdinChildOut:sub(#stdinChildOut - #echoMessage - 1, #stdinChildOut) -end -assert( - stdinChildOut == echoMessage .. trailingAddition, - "Stdin passing did not return proper output" -) diff --git a/tests/process/spawn/async.luau b/tests/process/spawn/async.luau new file mode 100644 index 0000000..e0fdc68 --- /dev/null +++ b/tests/process/spawn/async.luau @@ -0,0 +1,44 @@ +local process = require("@lune/process") +local stdio = require("@lune/stdio") +local task = require("@lune/task") + +local IS_WINDOWS = process.os == "windows" + +-- Spawning a process should not block any lua thread(s) + +local SLEEP_DURATION = 1 / 4 +local SLEEP_SAMPLES = 2 + +local thread2 = task.delay(30, function() + stdio.ewrite("Spawning a sleep process should take a reasonable amount of time\n") + task.wait(1) + process.exit(1) +end) + +local sleepStart = os.clock() +local sleepCounter = 0 +for i = 1, SLEEP_SAMPLES, 1 do + task.spawn(function() + local args = { + -- Sleep command on Windows in Seconds has some weird behavior with decimals ... + tostring(SLEEP_DURATION * (IS_WINDOWS and 1000 or 1)), + } + if IS_WINDOWS then + -- ... so we use milliseconds instead. + table.insert(args, 1, "-Milliseconds") + end + -- Windows does not have `sleep` as a process, so we use powershell instead. + process.spawn("sleep", args, if IS_WINDOWS then { shell = true } else nil) + sleepCounter += 1 + end) +end +while sleepCounter < SLEEP_SAMPLES do + task.wait() +end + +task.cancel(thread2) + +assert( + (os.clock() - sleepStart) >= SLEEP_DURATION, + "Spawning a process that does blocking sleep did not sleep enough" +) diff --git a/tests/process/spawn/basic.luau b/tests/process/spawn/basic.luau new file mode 100644 index 0000000..012a8ee --- /dev/null +++ b/tests/process/spawn/basic.luau @@ -0,0 +1,28 @@ +local process = require("@lune/process") +local stdio = require("@lune/stdio") +local task = require("@lune/task") + +-- Spawning a child process should work, with options + +local thread = task.delay(1, function() + stdio.ewrite("Spawning a process should take a reasonable amount of time\n") + task.wait(1) + process.exit(1) +end) + +local IS_WINDOWS = process.os == "windows" + +local result = process.spawn( + if IS_WINDOWS then "cmd" else "ls", + if IS_WINDOWS then { "/c", "dir" } else { "-a" } +) + +task.cancel(thread) + +assert(result.ok, "Failed to spawn child process") + +assert(result.stderr == "", "Stderr was not empty") +assert(result.stdout ~= "", "Stdout was empty") + +assert(string.find(result.stdout, "Cargo.toml") ~= nil, "Missing Cargo.toml in output") +assert(string.find(result.stdout, ".gitignore") ~= nil, "Missing .gitignore in output") diff --git a/tests/process/spawn/cwd.luau b/tests/process/spawn/cwd.luau new file mode 100644 index 0000000..d9989df --- /dev/null +++ b/tests/process/spawn/cwd.luau @@ -0,0 +1,54 @@ +local process = require("@lune/process") + +local IS_WINDOWS = process.os == "windows" + +local pwdCommand = if IS_WINDOWS then "cmd" else "pwd" +local pwdArgs = if IS_WINDOWS then { "/c", "cd" } else {} + +-- Make sure the cwd option actually uses the directory we want +local rootPwd = process.spawn(pwdCommand, pwdArgs, { + cwd = "/", +}).stdout +rootPwd = string.gsub(rootPwd, "^%s+", "") +rootPwd = string.gsub(rootPwd, "%s+$", "") + +-- Windows: :\, Unix: / +local expectedRootPwd = if IS_WINDOWS then string.sub(rootPwd, 1, 1) .. ":\\" else "/" +if rootPwd ~= expectedRootPwd then + error( + string.format( + "Current working directory for child process was not set correctly!" + .. "\nExpected '%s', got '%s'", + expectedRootPwd, + rootPwd + ) + ) +end + +-- Setting cwd should not change the cwd of this process + +local pwdBefore = process.spawn(pwdCommand, pwdArgs).stdout +process.spawn("ls", {}, { + cwd = "/", + shell = true, +}) +local pwdAfter = process.spawn(pwdCommand, pwdArgs).stdout +assert(pwdBefore == pwdAfter, "Current working directory changed after running child process") + +-- Setting the cwd on a child process should properly +-- replace any leading ~ with the users real home dir + +local homeDir1 = process.spawn("echo $HOME", nil, { + shell = true, +}).stdout + +-- NOTE: Powershell for windows uses `$pwd.Path` instead of `pwd` as pwd would return +-- a PathInfo object, using $pwd.Path gets the Path property of the PathInfo object +local homeDir2 = process.spawn(if IS_WINDOWS then "$pwd.Path" else "pwd", nil, { + shell = true, + cwd = "~", +}).stdout + +assert(#homeDir1 > 0, "Home dir from echo was empty") +assert(#homeDir2 > 0, "Home dir from pwd was empty") +assert(homeDir1 == homeDir2, "Home dirs did not match when performing tilde substitution") diff --git a/tests/process/spawn/shell.luau b/tests/process/spawn/shell.luau new file mode 100644 index 0000000..6f64791 --- /dev/null +++ b/tests/process/spawn/shell.luau @@ -0,0 +1,20 @@ +local process = require("@lune/process") + +local IS_WINDOWS = process.os == "windows" + +-- Default shell should be /bin/sh on unix and powershell on Windows, +-- note that powershell needs slightly different command flags for ls + +local shellResult = process.spawn("ls", { + if IS_WINDOWS then "-Force" else "-a", +}, { + shell = true, +}) + +assert(shellResult.ok, "Failed to spawn child process (shell)") + +assert(shellResult.stderr == "", "Stderr was not empty (shell)") +assert(shellResult.stdout ~= "", "Stdout was empty (shell)") + +assert(string.find(shellResult.stdout, "Cargo.toml") ~= nil, "Missing Cargo.toml in output (shell)") +assert(string.find(shellResult.stdout, ".gitignore") ~= nil, "Missing .gitignore in output (shell)") diff --git a/tests/process/spawn/stdin.luau b/tests/process/spawn/stdin.luau new file mode 100644 index 0000000..56c77a5 --- /dev/null +++ b/tests/process/spawn/stdin.luau @@ -0,0 +1,19 @@ +local process = require("@lune/process") + +local IS_WINDOWS = process.os == "windows" + +-- Windows uses \r\n (CRLF) and unix uses \n (LF) + +local echoTrail = if IS_WINDOWS then "\r\n" else "\n" +local echoMessage = "Hello from child process!" + +-- When passing stdin to powershell on windows we must "accept" using the double newline + +local result = if IS_WINDOWS + then process.spawn("powershell", { "echo" }, { stdin = echoMessage .. "\n\n" }) + else process.spawn("xargs", { "echo" }, { stdin = echoMessage }) + +local resultStdout = if IS_WINDOWS + then string.sub(result.stdout, #result.stdout - #echoMessage - 1) + else result.stdout +assert(resultStdout == echoMessage .. echoTrail, "Stdin passing did not return proper output") diff --git a/tests/process/spawn/stdio.luau b/tests/process/spawn/stdio.luau new file mode 100644 index 0000000..0ea5b1c --- /dev/null +++ b/tests/process/spawn/stdio.luau @@ -0,0 +1,22 @@ +local process = require("@lune/process") + +local IS_WINDOWS = process.os == "windows" + +-- Inheriting stdio & environment variables should work + +local echoMessage = "Hello from child process!" +local echoResult = process.spawn("echo", { + if IS_WINDOWS then '"$Env:TEST_VAR"' else '"$TEST_VAR"', +}, { + env = { TEST_VAR = echoMessage }, + shell = if IS_WINDOWS then "powershell" else "bash", + stdio = "inherit", +}) + +-- Windows uses \r\n (CRLF) and unix uses \n (LF) + +local echoTrail = if IS_WINDOWS then "\r\n" else "\n" +assert( + echoResult.stdout == (echoMessage .. echoTrail), + "Inheriting stdio did not return proper output" +)