Commit 0ae9c06a authored by Karel Koci's avatar Karel Koci 🤘

Drop environment setting and add callback instead

We want to do sometimes more than just edit environment for subprocess.
Defining full fledge callback that is called in subprocess is better
alternative. It allows even more changes to be done before execution of
command.
parent 8ebc0ab6
...@@ -454,44 +454,53 @@ static int lua_cleanup_unregister_handle(lua_State *L) { ...@@ -454,44 +454,53 @@ static int lua_cleanup_unregister_handle(lua_State *L) {
return 0; return 0;
} }
struct subprocess_callback_data {
lua_State *L;
char *callback;
};
static void subprocess_callback(void *vdata) {
struct subprocess_callback_data *dt = (struct subprocess_callback_data*)vdata;
if (!dt->callback)
return;
lua_State *L = dt->L;
// This may be called from C code with a dirty stack
luaL_checkstack(L, 4, "Not enough stack space to call subprocess callback");
int handler = push_err_handler(L);
extract_registry_value(L, dt->callback);
int result = lua_pcall(L, 0, 0, handler);
ASSERT_MSG(!result, "%s", interpreter_error_result(L));
}
static int lua_subprocess(lua_State *L) { static int lua_subprocess(lua_State *L) {
enum log_subproc_type type = (enum log_subproc_type)luaL_checkinteger(L, 1); enum log_subproc_type type = (enum log_subproc_type)luaL_checkinteger(L, 1);
// TODO verify type? // TODO verify type?
const char *message = luaL_checkstring(L, 2); const char *message = luaL_checkstring(L, 2);
int timeout = luaL_checkinteger(L, 3); int timeout = luaL_checkinteger(L, 3);
const char *command = luaL_checkstring(L, 5); struct subprocess_callback_data callback_data = {
.L = L,
// Environment .callback = NULL
luaL_checktype(L, 4, LUA_TTABLE); }; // This can be on stack as we won't return while using this
int env_size = 1; // For terminating NULL int cmd_index = 4;
lua_pushnil(L); if (lua_isfunction(L, 4)) {
while (lua_next(L, 4) != 0) { callback_data.callback = register_value(L, 4);
env_size++; cmd_index++;
lua_pop(L, 1); // pop pushed key
} }
struct env_change env[env_size]; const char *command = luaL_checkstring(L, cmd_index);
lua_pushnil(L); const char *args[lua_gettop(L) - cmd_index - 1];
int i = 0; for (int i = cmd_index + 1; i <= lua_gettop(L); i++) {
while (lua_next(L, 4) != 0) { args[i - cmd_index - 1] = luaL_checkstring(L, i);
env[i].name = strdup(luaL_checkstring(L, -2));
env[i].value = strdup(luaL_checkstring(L, -1));
lua_pop(L, 1); // pop pushed key
i++;
} }
env[env_size - 1] = (struct env_change){NULL}; args[lua_gettop(L) - cmd_index] = NULL;
int ec;
char *output; char *output;
if (lua_gettop(L) > 5) { int ec = lsubproclc(type, message, &output, timeout, subprocess_callback, &callback_data, command, args);
const char *args[lua_gettop(L) - 4];
for (int i = 6; i <= lua_gettop(L); i++) { // Free callback data callback name
args[i - 6] = luaL_checkstring(L, i); if (callback_data.callback)
} free(callback_data.callback);
args[lua_gettop(L) - 5] = NULL;
ec = lsubprocle(type, message, &output, timeout, env, command, args);
} else {
ec = lsubprocve(type, message, &output, timeout, env, command, NULL);
}
lua_pushinteger(L, ec); lua_pushinteger(L, ec);
lua_pushstring(L, output); lua_pushstring(L, output);
......
...@@ -93,7 +93,7 @@ Family of functions ``subproc*`` defined in ``subprocess.h`` are exported to lua ...@@ -93,7 +93,7 @@ Family of functions ``subproc*`` defined in ``subprocess.h`` are exported to lua
in form of functions `subprocess`. in form of functions `subprocess`.
Function `subprocess` is defined as follows: Function `subprocess` is defined as follows:
`subprocess(type, message, timeout, environment, command ...)` `subprocess(type, message, timeout, callback, command ...)`
`type` is identification used to specify what type of subprocess it's. Allowed `type` is identification used to specify what type of subprocess it's. Allowed
predefined constants are as follows: predefined constants are as follows:
...@@ -106,9 +106,10 @@ Function `subprocess` is defined as follows: ...@@ -106,9 +106,10 @@ Function `subprocess` is defined as follows:
`timeout` is time in seconds after which subprocess will be automatically `timeout` is time in seconds after which subprocess will be automatically
killed. killed.
`environment` is (hash) table containing changes to be done to environment `callback` is optional function that would be called in subprocess just before
variables. Key has to be only strings but values can be anything that can be it executes given command. If you don't want to specify it then you can pass nil
later converted to string using `tostring` function. or you can just drop it out (in that case command is expeted on this argument
place). This functions should has no arguments and shouldn't return anything.
`command` is any arbitrary number of string arguments that are passed as command `command` is any arbitrary number of string arguments that are passed as command
and its additional arguments. and its additional arguments.
...@@ -117,6 +118,10 @@ This function returns exit code of executed subprocess as first argument. And ...@@ -117,6 +118,10 @@ This function returns exit code of executed subprocess as first argument. And
output of this process as second argument. (Output includes both stdout and output of this process as second argument. (Output includes both stdout and
stderr). stderr).
Note::
There is some problem with printing from lua to stdout in callback on some
platforms.
Asynchronous events Asynchronous events
------------------- -------------------
......
...@@ -36,32 +36,31 @@ void subproc_kill_t(int timeout) { ...@@ -36,32 +36,31 @@ void subproc_kill_t(int timeout) {
kill_timeout = timeout; kill_timeout = timeout;
} }
static void run_child(const char *cmd, const char *args[], struct env_change env[], int p_out[2], int p_err[2]) { static void run_child(const char *cmd, const char *args[], subproc_callback callback, void *data, int p_out[2], int p_err[2]) {
// Close unneded FDs // Close unneded FDs
ASSERT(close(0) != -1); ASSERT(close(0) != -1);
ASSERT(close(p_out[0]) != -1); ASSERT(close(p_out[0]) != -1);
ASSERT(dup2(p_out[1], 1) != -1 && close(p_out[1]) != -1); ASSERT(dup2(p_out[1], 1) != -1 && close(p_out[1]) != -1);
ASSERT(close(p_err[0]) != -1); ASSERT(close(p_err[0]) != -1);
ASSERT(dup2(p_err[1], 2) != -1 && close(p_err[1]) != -1); ASSERT(dup2(p_err[1], 2) != -1 && close(p_err[1]) != -1);
// Set environment // Callback
for (int i = 0; env[i].name != NULL; i++) { if (callback)
if (env[i].value != NULL) callback(data);
setenv(env[i].name, env[i].value, true);
else
unsetenv(env[i].name);
}
// Exec // Exec
size_t arg_c = 2; // cmd and NULL terminator if (cmd) {
for (const char **p = args; *p; p++) size_t arg_c = 2; // cmd and NULL terminator
arg_c++; for (const char **p = args; *p; p++)
char *argv[arg_c]; arg_c++;
size_t i = 1; char *argv[arg_c];
for (const char **p = args; *p; p++) size_t i = 1;
argv[i++] = strdup(*p); for (const char **p = args; *p; p++)
argv[i] = NULL; argv[i++] = strdup(*p);
argv[0] = strdup(cmd); argv[i] = NULL;
execvp(cmd, argv); argv[0] = strdup(cmd);
DIE("Failed to exec %s: %s", cmd, strerror(errno)); execvp(cmd, argv);
DIE("Failed to exec %s: %s", cmd, strerror(errno));
} else
exit(0); // We just exit child
} }
int subprocv(int timeout, const char *cmd, ...) { int subprocv(int timeout, const char *cmd, ...) {
...@@ -80,10 +79,10 @@ int subprocvo(int timeout, FILE *fd[2], const char *cmd, ...) { ...@@ -80,10 +79,10 @@ int subprocvo(int timeout, FILE *fd[2], const char *cmd, ...) {
return res; return res;
} }
int subprocveo(int timeout, FILE *fd[2], struct env_change env[], const char *cmd, ...) { int subprocvoc(int timeout, FILE *fd[2], subproc_callback callback, void *data, const char *cmd, ...) {
va_list va_args; va_list va_args;
va_start(va_args, cmd); va_start(va_args, cmd);
int res = vsubprocveo(timeout, fd, env, cmd, va_args); int res = vsubprocvoc(timeout, fd, callback, data, cmd, va_args);
va_end(va_args); va_end(va_args);
return res; return res;
} }
...@@ -94,11 +93,10 @@ int subprocl(int timeout, const char *cmd, const char *args[]) { ...@@ -94,11 +93,10 @@ int subprocl(int timeout, const char *cmd, const char *args[]) {
} }
int subproclo(int timeout, FILE *fd[2], const char *cmd, const char *args[]) { int subproclo(int timeout, FILE *fd[2], const char *cmd, const char *args[]) {
struct env_change env[] = {{NULL}}; return subprocloc(timeout, fd, NULL, NULL, cmd, args);
return subprocleo(timeout, fd, env, cmd, args);
} }
int subprocleo(int timeout, FILE *fd[2], struct env_change env[], const char *cmd, const char *args[]) { int subprocloc(int timeout, FILE *fd[2], subproc_callback callback, void *data, const char *cmd, const char *args[]) {
struct log_buffer log; struct log_buffer log;
log_buffer_init(&log, LL_TRACE); log_buffer_init(&log, LL_TRACE);
if (log.f) { if (log.f) {
...@@ -119,7 +117,7 @@ int subprocleo(int timeout, FILE *fd[2], struct env_change env[], const char *cm ...@@ -119,7 +117,7 @@ int subprocleo(int timeout, FILE *fd[2], struct env_change env[], const char *cm
if (pid == -1) if (pid == -1)
DIE("Failed to fork command %s: %s", cmd, strerror(errno)); DIE("Failed to fork command %s: %s", cmd, strerror(errno));
else if (pid == 0) else if (pid == 0)
run_child(cmd, args, env, p_out, p_err); run_child(cmd, args, callback, data, p_out, p_err);
ASSERT(close(p_out[1]) != -1); ASSERT(close(p_out[1]) != -1);
ASSERT(close(p_err[1]) != -1); ASSERT(close(p_err[1]) != -1);
...@@ -130,12 +128,14 @@ int subprocleo(int timeout, FILE *fd[2], struct env_change env[], const char *cm ...@@ -130,12 +128,14 @@ int subprocleo(int timeout, FILE *fd[2], struct env_change env[], const char *cm
}; };
time_t t_start = time(NULL); time_t t_start = time(NULL);
bool term_sent = false; bool term_sent = false;
int dead = 0, i; while (true) {
while (dead < 2) {
time_t rem_t = timeout - time(NULL) + t_start; time_t rem_t = timeout - time(NULL) + t_start;
ASSERT(poll(pfds, 2, rem_t < 0 ? 0 : rem_t) != -1); // We ignore interrupt errors as those are really not an errors
dead = 0; // TODO what if timeout is negative?
for (i = 0; i < 2; i++) { // TODO also this timeout is in ms not in s!!!
ASSERT_MSG(poll(pfds, 2, rem_t < 0 ? 0 : rem_t) != -1 || errno == EINTR, "Subprocess poll failed with error: %s", strerror(errno));
int dead = 0;
for (int i = 0; i < 2; i++) {
if (pfds[i].revents & POLLIN) { if (pfds[i].revents & POLLIN) {
char *buff[64]; char *buff[64];
ssize_t loaded; ssize_t loaded;
...@@ -174,11 +174,10 @@ int vsubprocv(int timeout, const char *cmd, va_list args) { ...@@ -174,11 +174,10 @@ int vsubprocv(int timeout, const char *cmd, va_list args) {
} }
int vsubprocvo(int timeout, FILE *fd[2], const char *cmd, va_list args) { int vsubprocvo(int timeout, FILE *fd[2], const char *cmd, va_list args) {
struct env_change env[] = {{NULL}}; return vsubprocvoc(timeout, fd, NULL, NULL, cmd, args);
return vsubprocveo(timeout, fd, env, cmd, args);
} }
int vsubprocveo(int timeout, FILE *fd[2], struct env_change env[], const char *cmd, va_list args) { int vsubprocvoc(int timeout, FILE *fd[2], subproc_callback callback, void *data, const char *cmd, va_list args) {
size_t argc = 1; // For NULL terminator size_t argc = 1; // For NULL terminator
// Count (use copy for that) // Count (use copy for that)
va_list va_copy; va_list va_copy;
...@@ -191,7 +190,7 @@ int vsubprocveo(int timeout, FILE *fd[2], struct env_change env[], const char *c ...@@ -191,7 +190,7 @@ int vsubprocveo(int timeout, FILE *fd[2], struct env_change env[], const char *c
size_t i = 0; size_t i = 0;
while((argv[i++] = va_arg(args, const char *)) != NULL); while((argv[i++] = va_arg(args, const char *)) != NULL);
argv[argc - 1] = NULL; argv[argc - 1] = NULL;
return subprocleo(timeout, fd, env, cmd, argv); return subprocloc(timeout, fd, callback, data, cmd, argv);
} }
int lsubprocv(enum log_subproc_type type, const char *message, char **output, int timeout, const char *cmd, ...) { int lsubprocv(enum log_subproc_type type, const char *message, char **output, int timeout, const char *cmd, ...) {
...@@ -202,38 +201,36 @@ int lsubprocv(enum log_subproc_type type, const char *message, char **output, in ...@@ -202,38 +201,36 @@ int lsubprocv(enum log_subproc_type type, const char *message, char **output, in
return ec; return ec;
} }
int lsubprocve(enum log_subproc_type type, const char *message, char **output, int timeout, struct env_change env[], const char *cmd, ...) { int lsubprocvc(enum log_subproc_type type, const char *message, char **output, int timeout, subproc_callback callback, void *data, const char *cmd, ...) {
va_list va_args; va_list va_args;
va_start(va_args, cmd); va_start(va_args, cmd);
int ec = lvsubprocve(type, message, output, timeout, env, cmd, va_args); int ec = lvsubprocvc(type, message, output, timeout, callback, data, cmd, va_args);
va_end(va_args); va_end(va_args);
return ec; return ec;
} }
int lsubprocl(enum log_subproc_type type, const char *message, char **output, int timeout, const char *cmd, const char *args[]) { int lsubprocl(enum log_subproc_type type, const char *message, char **output, int timeout, const char *cmd, const char *args[]) {
struct env_change env[] = {{NULL}}; return lsubproclc(type, message, output, timeout, NULL, NULL, cmd, args);
return lsubprocle(type, message, output, timeout, env, cmd, args);
} }
int lsubprocle(enum log_subproc_type type, const char *message, char **output, int timeout, struct env_change env[], const char *cmd, const char *args[]) { int lsubproclc(enum log_subproc_type type, const char *message, char **output, int timeout, subproc_callback callback, void *data, const char *cmd, const char *args[]) {
struct log_subproc lsp; struct log_subproc lsp;
log_subproc_open(&lsp, type, message); log_subproc_open(&lsp, type, message);
FILE *fds[] = {lsp.out, lsp.err}; FILE *fds[] = {lsp.out, lsp.err};
int ec = subprocleo(timeout, fds, env, cmd, args); int ec = subprocloc(timeout, fds, callback, data, cmd, args);
log_subproc_close(&lsp, ec, output); log_subproc_close(&lsp, ec, output);
return ec; return ec;
} }
int lvsubprocv(enum log_subproc_type type, const char *message, char **output, int timeout, const char *cmd, va_list args) { int lvsubprocv(enum log_subproc_type type, const char *message, char **output, int timeout, const char *cmd, va_list args) {
struct env_change env[] = {{NULL}}; return lsubprocvc(type, message, output, timeout, NULL, NULL, cmd, args);
return lsubprocve(type, message, output, timeout, env, cmd, args);
} }
int lvsubprocve(enum log_subproc_type type, const char *message, char **output, int timeout, struct env_change env[], const char *cmd, va_list args) { int lvsubprocvc(enum log_subproc_type type, const char *message, char **output, int timeout, subproc_callback callback, void *data, const char *cmd, va_list args) {
struct log_subproc lsp; struct log_subproc lsp;
log_subproc_open(&lsp, type, message); log_subproc_open(&lsp, type, message);
FILE *fds[] = {lsp.out, lsp.err}; FILE *fds[] = {lsp.out, lsp.err};
int ec = vsubprocveo(timeout, fds, env, cmd, args); int ec = vsubprocvoc(timeout, fds, callback, data, cmd, args);
log_subproc_close(&lsp, ec, output); log_subproc_close(&lsp, ec, output);
return ec; return ec;
} }
...@@ -24,15 +24,12 @@ ...@@ -24,15 +24,12 @@
#include <stdio.h> #include <stdio.h>
#include "logging.h" #include "logging.h"
// Structure used for passing changes to environment
struct env_change {
const char *name, *value;
};
// Set subproc kill timeout. This is timeout used when primary timeout runs out // Set subproc kill timeout. This is timeout used when primary timeout runs out
// and SIGTERM is send but process still doesn't dies. // and SIGTERM is send but process still doesn't dies.
void subproc_kill_t(int timeout); void subproc_kill_t(int timeout);
typedef void (*subproc_callback)(void *data);
/* /*
This runs non-interactive programs as subprocess. It closes stdin and pipes stdout This runs non-interactive programs as subprocess. It closes stdin and pipes stdout
and stderr trough logging system. and stderr trough logging system.
...@@ -50,20 +47,20 @@ Returned status field from wait call. See manual for wait on how to decode it. ...@@ -50,20 +47,20 @@ Returned status field from wait call. See manual for wait on how to decode it.
*/ */
int subprocv(int timeout, const char *command, ...); // (char *) NULL int subprocv(int timeout, const char *command, ...); // (char *) NULL
int subprocvo(int timeout, FILE *fd[2], const char *command, ...); // (char *) NULL int subprocvo(int timeout, FILE *fd[2], const char *command, ...); // (char *) NULL
int subprocveo(int timeout, FILE *fd[2], struct env_change env[], const char *command, ...); // (char *) NULL int subprocvoc(int timeout, FILE *fd[2], subproc_callback callback, void *data, const char *command, ...); // (char *) NULL
int subprocl(int timeout, const char *command, const char *args[]); int subprocl(int timeout, const char *command, const char *args[]);
int subproclo(int timeout, FILE *fd[2], const char *command, const char *args[]); int subproclo(int timeout, FILE *fd[2], const char *command, const char *args[]);
int subprocleo(int timeout, FILE *fd[2], struct env_change env[], const char *command, const char *args[]); int subprocloc(int timeout, FILE *fd[2], subproc_callback callback, void *data, const char *command, const char *args[]);
int vsubprocv(int timeout, const char *command, va_list args); int vsubprocv(int timeout, const char *command, va_list args);
int vsubprocvo(int timeout, FILE *fd[2], const char *command, va_list args); int vsubprocvo(int timeout, FILE *fd[2], const char *command, va_list args);
int vsubprocveo(int timeout, FILE *fd[2], struct env_change env[], const char *command, va_list args); int vsubprocvoc(int timeout, FILE *fd[2], subproc_callback callback, void *data, const char *command, va_list args);
// Following functions integrate log_subproc with subproc to enable logging of subprocess output. // Following functions integrate log_subproc with subproc to enable logging of subprocess output.
int lsubprocv(enum log_subproc_type type, const char *message, char **output, int timeout, const char *command, ...); int lsubprocv(enum log_subproc_type type, const char *message, char **output, int timeout, const char *command, ...);
int lsubprocve(enum log_subproc_type type, const char *message, char **output, int timeout, struct env_change env[], const char *command, ...); int lsubprocvc(enum log_subproc_type type, const char *message, char **output, int timeout, subproc_callback callback, void *data, const char *command, ...);
int lsubprocl(enum log_subproc_type type, const char *message, char **output, int timeout, const char *command, const char *args[]); int lsubprocl(enum log_subproc_type type, const char *message, char **output, int timeout, const char *command, const char *args[]);
int lsubprocle(enum log_subproc_type type, const char *message, char **output, int timeout, struct env_change env[], const char *command, const char *args[]); int lsubproclc(enum log_subproc_type type, const char *message, char **output, int timeout, subproc_callback callback, void *data, const char *command, const char *args[]);
int lvsubprocv(enum log_subproc_type type, const char *message, char **output, int timeout, const char *command, va_list args); int lvsubprocv(enum log_subproc_type type, const char *message, char **output, int timeout, const char *command, va_list args);
int lvsubprocve(enum log_subproc_type type, const char *message, char **output, int timeout, struct env_change env[], const char *command, va_list args); int lvsubprocvc(enum log_subproc_type type, const char *message, char **output, int timeout, subproc_callback callback, void *data, const char *command, va_list args);
#endif #endif
...@@ -49,61 +49,76 @@ START_TEST(timeout) { ...@@ -49,61 +49,76 @@ START_TEST(timeout) {
} }
END_TEST END_TEST
struct buffs {
FILE *fds[2];
char *b_out, *b_err;
size_t s_out, s_err;
};
static struct buffs *buffs_init() {
struct buffs *bfs = malloc(sizeof *bfs);
bfs->fds[0] = open_memstream(&bfs->b_out, &bfs->s_out);
bfs->fds[1] = open_memstream(&bfs->b_err, &bfs->s_err);
return bfs;
}
static void buffs_assert(struct buffs *bfs, const char *out, const char *err) {
fflush(bfs->fds[0]);
fflush(bfs->fds[1]);
ck_assert(strcmp(out, bfs->b_out) == 0);
ck_assert(strcmp(err, bfs->b_err) == 0);
rewind(bfs->fds[0]);
rewind(bfs->fds[1]);
bfs->b_out[0] = '\0';
bfs->b_err[0] = '\0';
}
static void buffs_free(struct buffs *bfs) {
fclose(bfs->fds[0]);
fclose(bfs->fds[1]);
free(bfs->b_out);
free(bfs->b_err);
free(bfs);
}
START_TEST(output) { START_TEST(output) {
subproc_kill_t(0); subproc_kill_t(0);
char *buff_out, *buff_err; struct buffs *bfs = buffs_init();
size_t size_out, size_err;
FILE *ff_out = open_memstream(&buff_out, &size_out);
FILE *ff_err = open_memstream(&buff_err, &size_err);
FILE *fds[] = {ff_out, ff_err};
#define BUFF_ASSERT(STDOUT, STDERR) do { \
fflush(ff_out); \
fflush(ff_err); \
ck_assert(strcmp(STDOUT, buff_out) == 0); \
ck_assert(strcmp(STDERR, buff_err) == 0); \
rewind(ff_out); \
rewind(ff_err); \
buff_out[0] = '\0'; \
buff_err[0] = '\0'; \
} while(0)
// Echo to stdout // Echo to stdout
ck_assert(subprocvo(1, fds, "echo", "hello", NULL) == 0); ck_assert(subprocvo(1, bfs->fds, "echo", "hello", NULL) == 0);
BUFF_ASSERT("hello\n", ""); buffs_assert(bfs, "hello\n", "");
// Echo to stderr // Echo to stderr
ck_assert(subprocvo(1, fds, "sh", "-c", "echo hello >&2", NULL) == 0); ck_assert(subprocvo(1, bfs->fds, "sh", "-c", "echo hello >&2", NULL) == 0);
BUFF_ASSERT("", "hello\n"); buffs_assert(bfs, "", "hello\n");
#undef BUFF_ASSERT
fclose(ff_out); buffs_free(bfs);
fclose(ff_err);
free(buff_out);
free(buff_err);
} }
END_TEST END_TEST
START_TEST(environment) { static void callback_test(void *data) {
if (data)
printf("%s", (const char *)data);
else
printf("hello");
}
START_TEST(callback) {
subproc_kill_t(0); subproc_kill_t(0);
char *buff; struct buffs *bfs = buffs_init();
size_t size;
FILE *ff = open_memstream(&buff, &size); // Without data
FILE *devnull = fopen("/dev/null", "w"); ck_assert(subprocloc(1, bfs->fds, callback_test, NULL, NULL, NULL) == 0);
FILE *fds[] = {ff, devnull}; buffs_assert(bfs, "hello", "");
struct env_change env[] = { // With data
{.name = "TESTME", .value = "Hello" }, ck_assert(subprocvoc(1, bfs->fds, callback_test, "Hello again", NULL, NULL) == 0);
{NULL} buffs_assert(bfs, "Hello again", "");
};
ck_assert(subprocveo(1, fds, env, "sh", "-c", "echo $TESTME", NULL) == 0); buffs_free(bfs);
fflush(ff);
ck_assert(strcmp("Hello\n", buff) == 0);
fclose(ff);
fclose(devnull);
free(buff);
} }
END_TEST END_TEST
...@@ -114,7 +129,8 @@ Suite *gen_test_suite(void) { ...@@ -114,7 +129,8 @@ Suite *gen_test_suite(void) {
tcase_add_test(subproc, exit_code); tcase_add_test(subproc, exit_code);
tcase_add_test(subproc, timeout); tcase_add_test(subproc, timeout);
tcase_add_test(subproc, output); tcase_add_test(subproc, output);
tcase_add_test(subproc, environment); tcase_add_test(subproc, callback);
suite_add_tcase(result, subproc); suite_add_tcase(
result, subproc);
return result; return result;
} }
...@@ -22,33 +22,46 @@ require 'lunit' ...@@ -22,33 +22,46 @@ require 'lunit'
module("subproc", package.seeall, lunit.testcase) module("subproc", package.seeall, lunit.testcase)
function test_exit_code() function test_exit_code()
local ok, out = subprocess(LST_HOOK, "Test: true", 1, {}, "true") local ok, out = subprocess(LST_HOOK, "Test: true", 1, "true")
assert_equal(0, ok) assert_equal(0, ok)
assert_equal("", out) assert_equal("", out)
local ok, out = subprocess(LST_HOOK, "Test: false", 1, {}, "false") local ok, out = subprocess(LST_HOOK, "Test: false", 1, "false")
assert_not_equal(0, ok) assert_not_equal(0, ok)
assert_equal("", out) assert_equal("", out)
end end
function test_output() function test_output()
local ok, out = subprocess(LST_HOOK, "Test: echo", 1, {}, "echo", "hello") local ok, out = subprocess(LST_HOOK, "Test: echo", 1, "echo", "hello")
assert_equal(0, ok) assert_equal(0, ok)
assert_equal("hello\n", out) assert_equal("hello\n", out)
local ok, out = subprocess(LST_HOOK, "Test: echo stderr", 1, {}, "sh", "-c", "echo hello >&2") local ok, out = subprocess(LST_HOOK, "Test: echo stderr", 1, "sh", "-c", "echo hello >&2")
assert_equal(0, ok) assert_equal(0, ok)
assert_equal("hello\n", out) assert_equal("hello\n", out)
end end
function test_timeout() function test_timeout()
local ok, out = subprocess(LST_HOOK, "Test: sleep", 1, {}, "sleep", "2") local ok, out = subprocess(LST_HOOK, "Test: sleep", 1, "sleep", "2")
assert_not_equal(0, ok) assert_not_equal(0, ok)
assert_equal("", out) assert_equal("", out)
end end
function test_env() function test_callback()
local ok, out = subprocess(LST_HOOK, "Test: env", 1, {['TESTMSG'] = 'hello'}, "sh", "-c", "echo $TESTMSG") subprocess_kill_timeout(0)
local ok, out = subprocess(LST_HOOK, "Test: env", 1, function () io.stderr:write("Hello callback\n") end, "true")
assert_equal(0, ok) assert_equal(0, ok)
assert_equal("hello\n", out) assert_equal("Hello callback\n", out)
--[[
We are testing here with stderr for a reason. There seems to be some problem
with lua's stdout and dup. No lua stdout works after dup2 on f.d. 1 on some
testing platforms. We are not planning to print messages from lua from
subprocess on daily base so let's not care.
]]
local ok, out = subprocess(LST_HOOK, "Test: env", 1, function () setenv("TESTENV", "Hello env") end, "sh", "-c", "echo $TESTENV")
assert_equal(0, ok)
assert_equal("Hello env\n", out)
end end
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment