aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJoseph Hunkeler <jhunkeler@users.noreply.github.com>2024-10-22 11:04:17 -0400
committerGitHub <noreply@github.com>2024-10-22 11:04:17 -0400
commit7729d546d2dbda85ca1d86a913e97b51487355ba (patch)
treee9a0e7f9f2069ecd9e718dd66d3e11fa7a80722d
parent8edc87d51900ccf7d1d67ad3647a4b8fa2d9b7ae (diff)
parent30f48145d1a1c747c40f94e2a7314d4bdf61cf55 (diff)
downloadstasis-7729d546d2dbda85ca1d86a913e97b51487355ba.tar.gz
Merge pull request #63 from jhunkeler/update-tests
Update tests / Bug fixes
-rw-r--r--CMakeLists.txt16
-rw-r--r--include/delivery.h2
-rw-r--r--include/multiprocessing.h3
-rw-r--r--src/cli/stasis/stasis_main.c17
-rw-r--r--src/lib/core/conda.c15
-rw-r--r--src/lib/core/delivery.c14
-rw-r--r--src/lib/core/delivery_init.c2
-rw-r--r--src/lib/core/multiprocessing.c12
-rw-r--r--tests/CMakeLists.txt28
-rw-r--r--tests/test_conda.c61
-rw-r--r--tests/test_envctl.c72
-rw-r--r--tests/test_multiprocessing.c92
12 files changed, 298 insertions, 36 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index caed929..6558205 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -26,14 +26,30 @@ endif()
add_subdirectory(src)
+# Toggle extremely verbose output
option(BUILD_TESTING_DEBUG OFF)
if (BUILD_TESTING_DEBUG)
add_compile_options(-DDEBUG)
endif()
+
+# Toggle regression testing on/off
+option(BUILD_TESTING_RT ON)
+
+# Toggle testing
option(BUILD_TESTING OFF)
+message(CHECK_START "Run unit tests")
if (BUILD_TESTING)
+ message(CHECK_PASS "yes")
enable_testing()
+ message(CHECK_START "Run regression tests")
+ if (BUILD_TESTING_RT)
+ message(CHECK_PASS "yes")
+ else()
+ message(CHECK_PASS "no")
+ endif()
add_subdirectory(tests)
+else()
+ message(CHECK_PASS "no")
endif()
set(SYSCONFDIR "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_SYSCONFDIR}")
diff --git a/include/delivery.h b/include/delivery.h
index bd5137c..345cd13 100644
--- a/include/delivery.h
+++ b/include/delivery.h
@@ -385,7 +385,7 @@ void delivery_install_conda(char *install_script, char *conda_install_dir);
int delivery_format_str(struct Delivery *ctx, char **dest, const char *fmt);
// helper function
-void delivery_gather_tool_versions(struct Delivery *ctx);
+int delivery_gather_tool_versions(struct Delivery *ctx);
// helper function
int delivery_init_tmpdir(struct Delivery *ctx);
diff --git a/include/multiprocessing.h b/include/multiprocessing.h
index 5919462..ec7c1ad 100644
--- a/include/multiprocessing.h
+++ b/include/multiprocessing.h
@@ -38,6 +38,9 @@ struct MultiProcessingPool {
int status_interval; ///< Report a pooled task is "running" every n seconds
};
+/// A multiprocessing task's initial state (i.e. "FAIL")
+#define MP_POOL_TASK_STATUS_INITIAL (-1)
+
/// Maximum number of multiprocessing tasks STASIS can execute
#define MP_POOL_TASK_MAX 1000
diff --git a/src/cli/stasis/stasis_main.c b/src/cli/stasis/stasis_main.c
index 5325892..c2443d7 100644
--- a/src/cli/stasis/stasis_main.c
+++ b/src/cli/stasis/stasis_main.c
@@ -332,14 +332,15 @@ int main(int argc, char *argv[]) {
exit(1);
}
- delivery_gather_tool_versions(&ctx);
- if (!ctx.conda.tool_version) {
- msg(STASIS_MSG_ERROR | STASIS_MSG_L2, "Could not determine conda version\n");
- exit(1);
- }
- if (!ctx.conda.tool_build_version) {
- msg(STASIS_MSG_ERROR | STASIS_MSG_L2, "Could not determine conda-build version\n");
- exit(1);
+ if (delivery_gather_tool_versions(&ctx)) {
+ if (!ctx.conda.tool_version) {
+ msg(STASIS_MSG_ERROR | STASIS_MSG_L2, "Could not determine conda version\n");
+ exit(1);
+ }
+ if (!ctx.conda.tool_build_version) {
+ msg(STASIS_MSG_ERROR | STASIS_MSG_L2, "Could not determine conda-build version\n");
+ exit(1);
+ }
}
if (pip_exec("install build")) {
diff --git a/src/lib/core/conda.c b/src/lib/core/conda.c
index 35caf02..25069f8 100644
--- a/src/lib/core/conda.c
+++ b/src/lib/core/conda.c
@@ -109,7 +109,7 @@ int pip_index_provides(const char *index_url, const char *spec) {
strcpy(proc.f_stdout, logfile);
// Do an installation in dry-run mode to see if the package exists in the given index.
- snprintf(cmd, sizeof(cmd) - 1, "python -m pip install --dry-run --no-deps --index-url=%s %s", index_url, spec_local);
+ snprintf(cmd, sizeof(cmd) - 1, "python -m pip install --dry-run --no-cache --no-deps --index-url=%s '%s'", index_url, spec_local);
status = shell(&proc, cmd);
// Print errors only when shell() itself throws one
@@ -443,6 +443,15 @@ char *conda_get_active_environment() {
int conda_provides(const char *spec) {
struct Process proc;
memset(&proc, 0, sizeof(proc));
+
+ // Short circuit:
+ // Running "mamba search" without an argument will print every package in
+ // all channels, then return "found". Prevent this.
+ // No data implies "not found".
+ if (isempty((char *) spec)) {
+ return 0;
+ }
+
strcpy(proc.f_stdout, "/dev/null");
strcpy(proc.f_stderr, "/dev/null");
@@ -450,12 +459,12 @@ int conda_provides(const char *spec) {
// conda_exec() expects the program output to be visible to the user.
// For this operation we only need the exit value.
char cmd[PATH_MAX] = {0};
- snprintf(cmd, sizeof(cmd) - 1, "mamba search --use-index-cache %s", spec);
+ snprintf(cmd, sizeof(cmd) - 1, "mamba search %s", spec);
if (shell(&proc, cmd) < 0) {
fprintf(stderr, "shell: %s", strerror(errno));
return -1;
}
- return proc.returncode == 0;
+ return proc.returncode == 0; // 0=not_found, 1=found
}
int conda_index(const char *path) {
diff --git a/src/lib/core/delivery.c b/src/lib/core/delivery.c
index e32ed4c..5645dcc 100644
--- a/src/lib/core/delivery.c
+++ b/src/lib/core/delivery.c
@@ -302,16 +302,22 @@ void delivery_defer_packages(struct Delivery *ctx, int type) {
}
}
-void delivery_gather_tool_versions(struct Delivery *ctx) {
- int status = 0;
+int delivery_gather_tool_versions(struct Delivery *ctx) {
+ int status_tool_version = 0;
+ int status_tool_build_version = 0;
// Extract version from tool output
- ctx->conda.tool_version = shell_output("conda --version", &status);
+ ctx->conda.tool_version = shell_output("conda --version", &status_tool_version);
if (ctx->conda.tool_version)
strip(ctx->conda.tool_version);
- ctx->conda.tool_build_version = shell_output("conda build --version", &status);
+ ctx->conda.tool_build_version = shell_output("conda build --version", &status_tool_build_version);
if (ctx->conda.tool_build_version)
strip(ctx->conda.tool_version);
+
+ if (status_tool_version || status_tool_build_version) {
+ return 1;
+ }
+ return 0;
}
diff --git a/src/lib/core/delivery_init.c b/src/lib/core/delivery_init.c
index e914f99..2333628 100644
--- a/src/lib/core/delivery_init.c
+++ b/src/lib/core/delivery_init.c
@@ -257,7 +257,7 @@ int delivery_init(struct Delivery *ctx, int render_mode) {
char cache_local[PATH_MAX];
sprintf(cache_local, "%s/%s", ctx->storage.tmpdir, "cache");
- setenv("XDG_CACHE_HOME", ctx->storage.tmpdir, 1);
+ setenv("XDG_CACHE_HOME", cache_local, 1);
// add tools to PATH
char pathvar_tmp[STASIS_BUFSIZ];
diff --git a/src/lib/core/multiprocessing.c b/src/lib/core/multiprocessing.c
index 484c566..252bab9 100644
--- a/src/lib/core/multiprocessing.c
+++ b/src/lib/core/multiprocessing.c
@@ -98,7 +98,7 @@ struct MultiProcessingTask *mp_pool_task(struct MultiProcessingPool *pool, const
}
// Set default status to "error"
- slot->status = -1;
+ slot->status = MP_POOL_TASK_STATUS_INITIAL;
// Set task identifier string
memset(slot->ident, 0, sizeof(slot->ident));
@@ -169,7 +169,13 @@ void mp_pool_show_summary(struct MultiProcessingPool *pool) {
for (size_t i = 0; i < pool->num_used; i++) {
struct MultiProcessingTask *task = &pool->task[i];
char status_str[10] = {0};
- if (!task->status && !task->signaled_by) {
+
+ if (task->status == MP_POOL_TASK_STATUS_INITIAL && task->pid == MP_POOL_PID_UNUSED) {
+ // You will only see this label if the task pool is killed by
+ // MP_POOL_FAIL_FAST and tasks are still queued for execution
+ strcpy(status_str, "HOLD");
+ } else if (!task->status && !task->signaled_by) {
+
strcpy(status_str, "DONE");
} else if (task->signaled_by) {
strcpy(status_str, "TERM");
@@ -254,7 +260,7 @@ int mp_pool_join(struct MultiProcessingPool *pool, size_t jobs, size_t flags) {
for (size_t i = lower_i; i < upper_i; i++) {
struct MultiProcessingTask *slot = &pool->task[i];
- if (slot->status == -1) {
+ if (slot->status == MP_POOL_TASK_STATUS_INITIAL) {
if (mp_task_fork(pool, slot)) {
fprintf(stderr, "%s: mp_task_fork failed\n", slot->ident);
kill(0, SIGTERM);
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
index f4380e0..0da290f 100644
--- a/tests/CMakeLists.txt
+++ b/tests/CMakeLists.txt
@@ -12,30 +12,30 @@ set(win_msvc_cflags /Wall)
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/data/generic.ini ${CMAKE_CURRENT_BINARY_DIR} COPYONLY)
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/data/result.xml ${CMAKE_CURRENT_BINARY_DIR} COPYONLY)
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/data/result_error.xml ${CMAKE_CURRENT_BINARY_DIR} COPYONLY)
-file(GLOB files "test_*.c")
+file(GLOB source_files "test_*.c")
-if (BASH_PROGRAM)
+if (BASH_PROGRAM AND BUILD_TESTING_RT)
add_test (rt_generic ${BASH_PROGRAM} ${CMAKE_CURRENT_SOURCE_DIR}/rt_generic.sh)
endif()
-foreach(file ${files})
- string(REGEX REPLACE "(^.*/|\\.[^.]*$)" "" file_without_ext ${file})
- add_executable(${file_without_ext} ${file})
+foreach(source_file ${source_files})
+ string(REGEX REPLACE "(^.*/|\\.[^.]*$)" "" test_executable ${source_file})
+ add_executable(${test_executable} ${source_file})
if (CMAKE_C_COMPILER_ID STREQUAL "GNU")
- target_compile_options(${file_without_ext} PRIVATE ${nix_cflags} ${nix_gnu_cflags})
+ target_compile_options(${test_executable} PRIVATE ${nix_cflags} ${nix_gnu_cflags})
elseif (CMAKE_C_COMPILER_ID MATCHES "Clang")
- target_compile_options(${file_without_ext} PRIVATE ${nix_cflags} ${nix_clang_cflags})
+ target_compile_options(${test_executable} PRIVATE ${nix_cflags} ${nix_clang_cflags})
elseif (CMAKE_C_COMPILER_ID STREQUAL "MSVC")
- target_compile_options(${file_without_ext} PRIVATE ${win_cflags} ${win_msvc_cflags})
+ target_compile_options(${test_executable} PRIVATE ${win_cflags} ${win_msvc_cflags})
endif()
- target_link_libraries(${file_without_ext} PRIVATE stasis_core)
- add_test(${file_without_ext} ${file_without_ext})
- set_tests_properties(${file_without_ext}
+ target_link_libraries(${test_executable} PRIVATE stasis_core)
+ add_test(${test_executable} ${test_executable})
+ set_tests_properties(${test_executable}
PROPERTIES
- TIMEOUT 120)
- set_tests_properties(${file_without_ext}
+ TIMEOUT 240)
+ set_tests_properties(${test_executable}
PROPERTIES
SKIP_RETURN_CODE 127)
- set_property(TEST ${file_without_ext}
+ set_property(TEST ${test_executable}
PROPERTY ENVIRONMENT "STASIS_SYSCONFDIR=${CMAKE_SOURCE_DIR}")
endforeach() \ No newline at end of file
diff --git a/tests/test_conda.c b/tests/test_conda.c
index 2ed869a..9ad12c8 100644
--- a/tests/test_conda.c
+++ b/tests/test_conda.c
@@ -126,6 +126,57 @@ void test_conda_index() {
STASIS_ASSERT(conda_index("channel") == 0, "cannot index a simple conda channel");
}
+void test_pip_index_provides() {
+ struct testcase {
+ const char *pindex;
+ const char *name;
+ int expected;
+ };
+ struct testcase tc[] = {
+ {.pindex = PYPI_INDEX_DEFAULT, .name = "firewatch", .expected = 1},
+ {.pindex = PYPI_INDEX_DEFAULT, .name = "doesnotexistfirewatch", .expected = 0},
+ {.pindex = "bad_index", .name = "firewatch", .expected = 0},
+ {.pindex = PYPI_INDEX_DEFAULT, .name = "", .expected = -1},
+ {.pindex = "", .name = "", .expected = -1},
+ };
+ for (size_t i = 0; i < sizeof(tc) / sizeof(*tc); i++) {
+ struct testcase *test = &tc[i];
+ int result = pip_index_provides(test->pindex, test->name) ;
+ STASIS_ASSERT(result == test->expected, "Unexpected result");
+ }
+}
+
+void test_conda_get_active_environment() {
+ conda_activate(ctx.storage.conda_install_prefix, "base");
+ STASIS_ASSERT(strcmp(conda_get_active_environment(), "base") == 0, "base environment not active");
+}
+
+void test_conda_provides() {
+ struct testcase {
+ const char *name;
+ int expected;
+ };
+ struct testcase tc[] = {
+ {.name = "fitsverify", .expected = 1},
+ {.name = "doesnotexistfitsverify", .expected = 0},
+ {.name = "", .expected = 0},
+ };
+
+ for (size_t i = 0; i < sizeof(tc) / sizeof(*tc); i++) {
+ struct testcase *test = &tc[i];
+ int result = conda_provides(test->name);
+ printf("%s returned %d, expecting %d\n", test->name, result, test->expected);
+ STASIS_ASSERT(result == test->expected, "Unexpected result");
+ }
+}
+
+void test_delivery_gather_tool_versions() {
+ int status = delivery_gather_tool_versions(&ctx);
+ STASIS_ASSERT(status == 0, "Failed to gather tool versions");
+ STASIS_ASSERT(!isempty(ctx.conda.tool_version), "conda version is empty");
+ STASIS_ASSERT(!isempty(ctx.conda.tool_build_version), "conda_build version is empty");
+}
+
int main(int argc, char *argv[]) {
STASIS_TEST_BEGIN_MAIN();
STASIS_TEST_FUNC *tests[] = {
@@ -133,11 +184,15 @@ int main(int argc, char *argv[]) {
test_conda_installation,
test_conda_activate,
test_conda_setup_headless,
+ test_conda_provides,
+ test_conda_get_active_environment,
test_conda_exec,
test_python_exec,
test_conda_env_create_from_uri,
test_conda_env_create_export_remove,
test_conda_index,
+ test_pip_index_provides,
+ test_delivery_gather_tool_versions,
};
const char *ws = "workspace";
@@ -165,8 +220,8 @@ int main(int argc, char *argv[]) {
ctx._stasis_ini_fp.delivery = ini;
ctx._stasis_ini_fp.delivery_path = realpath("mock.ini", NULL);
- setenv("TMPDIR", cwd_workspace, 1);
- globals.sysconfdir = getenv("STASIS_SYSCONFDIR");
+ const char *sysconfdir = getenv("STASIS_SYSCONFDIR");
+ globals.sysconfdir = strdup(sysconfdir ? sysconfdir : STASIS_SYSCONFDIR);
ctx.storage.root = strdup(cwd_workspace);
setenv("LANG", "C", 1);
@@ -179,5 +234,7 @@ int main(int argc, char *argv[]) {
if (rmtree(cwd_workspace)) {
perror(cwd_workspace);
}
+ delivery_free(&ctx);
+ globals_free();
STASIS_TEST_END_MAIN();
} \ No newline at end of file
diff --git a/tests/test_envctl.c b/tests/test_envctl.c
new file mode 100644
index 0000000..eab0186
--- /dev/null
+++ b/tests/test_envctl.c
@@ -0,0 +1,72 @@
+#include "testing.h"
+#include "envctl.h"
+
+void test_envctl_init() {
+ struct EnvCtl *envctl;
+ STASIS_ASSERT_FATAL((envctl = envctl_init()) != NULL, "envctl could not be initialized");
+ STASIS_ASSERT(envctl->num_alloc == STASIS_ENVCTL_DEFAULT_ALLOC, "freshly initialized envctl does not have the correct number records");
+ STASIS_ASSERT(envctl->num_used == 0, "freshly initialized envctl should have no allocations in use");
+ STASIS_ASSERT(envctl->item != NULL, "freshly initialized envctl should have an empty items array. this one is NULL.");
+ STASIS_ASSERT(envctl->item[0] == NULL, "freshly initialized envctl should not have any items. this one does.");
+ envctl_free(&envctl);
+ STASIS_ASSERT(envctl == NULL, "envctl should be NULL after envctl_free()");
+}
+
+static int except_passthru(const void *a, const void *b) {
+ const struct EnvCtl_Item *item = a;
+ const char *name = b;
+ if (!envctl_check_required(item->flags) && envctl_check_present(item, name)) {
+ return STASIS_ENVCTL_RET_SUCCESS;
+ }
+ return STASIS_ENVCTL_RET_FAIL;
+}
+
+static int except_required(const void *a, const void *b) {
+ const struct EnvCtl_Item *item = a;
+ const char *name = b;
+ if (envctl_check_required(item->flags) && envctl_check_present(item, name)) {
+ return STASIS_ENVCTL_RET_SUCCESS;
+ }
+ return STASIS_ENVCTL_RET_FAIL;
+}
+
+static int except_redact(const void *a, const void *b) {
+ const struct EnvCtl_Item *item = a;
+ const char *name = b;
+ if (envctl_check_redact(item->flags) && envctl_check_present(item, name)) {
+ return STASIS_ENVCTL_RET_SUCCESS;
+ }
+ return STASIS_ENVCTL_RET_FAIL;
+}
+
+void test_envctl_register() {
+ struct EnvCtl *envctl;
+ envctl = envctl_init();
+ setenv("passthru", "true", 1);
+ setenv("required", "true", 1);
+ setenv("redact", "true", 1);
+ envctl_register(&envctl, STASIS_ENVCTL_PASSTHRU, except_passthru, "passthru");
+ envctl_register(&envctl, STASIS_ENVCTL_REQUIRED, except_required, "required");
+ envctl_register(&envctl, STASIS_ENVCTL_REDACT, except_redact, "redact");
+
+ unsigned flags[] = {
+ STASIS_ENVCTL_PASSTHRU,
+ STASIS_ENVCTL_REQUIRED,
+ STASIS_ENVCTL_REDACT,
+ };
+ for (size_t i = 0; i < envctl->num_used; i++) {
+ struct EnvCtl_Item *item = envctl->item[i];
+ STASIS_ASSERT(item->flags == flags[i], "incorrect flag for item");
+ }
+ envctl_free(&envctl);
+}
+
+int main(int argc, char *argv[]) {
+ STASIS_TEST_BEGIN_MAIN();
+ STASIS_TEST_FUNC *tests[] = {
+ test_envctl_init,
+ test_envctl_register,
+ };
+ STASIS_TEST_RUN(tests);
+ STASIS_TEST_END_MAIN();
+} \ No newline at end of file
diff --git a/tests/test_multiprocessing.c b/tests/test_multiprocessing.c
index b9cd309..7c9d695 100644
--- a/tests/test_multiprocessing.c
+++ b/tests/test_multiprocessing.c
@@ -1,5 +1,6 @@
#include "testing.h"
#include "multiprocessing.h"
+#include <pthread.h>
static struct MultiProcessingPool *pool;
char *commands[] = {
@@ -12,6 +13,10 @@ char *commands[] = {
};
void test_mp_pool_init() {
+ STASIS_ASSERT((pool = mp_pool_init(NULL, "mplogs")) == NULL, "Pool should not be initialized with invalid ident");
+ STASIS_ASSERT((pool = mp_pool_init("mypool", NULL)) == NULL, "Pool should not be initialized with invalid logname");
+ STASIS_ASSERT((pool = mp_pool_init(NULL, NULL)) == NULL, "Pool should not be initialized with invalid arguments");
+ pool = NULL;
STASIS_ASSERT((pool = mp_pool_init("mypool", "mplogs")) != NULL, "Pool initialization failed");
STASIS_ASSERT_FATAL(pool != NULL, "Should not be NULL");
STASIS_ASSERT(pool->num_alloc == MP_POOL_TASK_MAX, "Wrong number of default records");
@@ -56,6 +61,7 @@ void test_mp_task() {
pool = mp_pool_init("mypool", "mplogs");
if (pool) {
+ pool->status_interval = 3;
for (size_t i = 0; i < sizeof(commands) / sizeof(*commands); i++) {
struct MultiProcessingTask *task;
char task_name[100] = {0};
@@ -113,6 +119,90 @@ void test_mp_pool_workflow() {
}
}
+void test_mp_fail_fast() {
+ char *commands_ff[128] = {
+ "sleep 3; true",
+ "sleep 5; false",
+ };
+
+ // Pad the array with tasks. None of these should execute when
+ // the "fail fast" conditions are met
+ char *nopcmd = "sleep 30; true";
+ for (size_t i = 2; i < sizeof(commands_ff) / sizeof(*commands_ff); i++) {
+ commands_ff[i] = nopcmd;
+ }
+
+ struct MultiProcessingPool *p;
+ STASIS_ASSERT((p = mp_pool_init("failfast", "failfastlogs")) != NULL, "Failed to initialize pool");
+ for (size_t i = 0; i < sizeof(commands_ff) / sizeof(*commands_ff); i++) {
+ char *command = commands_ff[i];
+ char taskname[100] = {0};
+ snprintf(taskname, sizeof(taskname) - 1, "task_%03zu", i);
+ STASIS_ASSERT(mp_pool_task(p, taskname, NULL, (char *) command) != NULL, "Failed to queue task");
+ }
+
+ STASIS_ASSERT(mp_pool_join(p, get_cpu_count(), MP_POOL_FAIL_FAST) < 0, "Unexpected result");
+
+ struct result {
+ int total_signaled;
+ int total_status_fail;
+ int total_status_success;
+ int total_unused;
+ } result = {
+ .total_signaled = 0,
+ .total_status_fail = 0,
+ .total_status_success = 0,
+ .total_unused = 0,
+ };
+ for (size_t i = 0; i < p->num_used; i++) {
+ struct MultiProcessingTask *task = &p->task[i];
+ if (task->signaled_by) result.total_signaled++;
+ if (task->status > 0) result.total_status_fail++;
+ if (task->status == 0) result.total_status_success++;
+ if (task->pid == MP_POOL_PID_UNUSED && task->status == MP_POOL_TASK_STATUS_INITIAL) result.total_unused++;
+ }
+ fprintf(stderr, "total_status_fail = %d\ntotal_status_success = %d\ntotal_signaled = %d\ntotal_unused = %d\n",
+ result.total_status_fail, result.total_status_success, result.total_signaled, result.total_unused);
+ STASIS_ASSERT(result.total_status_fail, "Should have failures");
+ STASIS_ASSERT(result.total_status_success, "Should have successes");
+ STASIS_ASSERT(result.total_signaled, "Should have signaled PIDs");
+ STASIS_ASSERT(result.total_unused, "Should have PIDs marked UNUSED.");
+ mp_pool_show_summary(p);
+ mp_pool_free(&p);
+}
+
+pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
+static void *pool_container(void *data) {
+ char *commands_sc[] = {
+ "sleep 10; echo done sleeping"
+ };
+ struct MultiProcessingPool **x = (struct MultiProcessingPool **) data;
+ struct MultiProcessingPool *p = (*x);
+ pthread_mutex_lock(&mutex);
+ mp_pool_task(p, "stop_resume_test", NULL, commands_sc[0]);
+ mp_pool_join(p, 1, 0);
+ mp_pool_show_summary(p);
+ mp_pool_free(&p);
+ pthread_mutex_unlock(&mutex);
+ return NULL;
+}
+
+void test_mp_stop_continue() {
+ struct MultiProcessingPool *p = NULL;
+ STASIS_ASSERT((p = mp_pool_init("stopcontinue", "stopcontinuelogs")) != NULL, "Failed to initialize pool");
+ pthread_t th;
+ pthread_create(&th, NULL, pool_container, &p);
+ sleep(2);
+ if (p->task[0].pid != MP_POOL_PID_UNUSED) {
+ STASIS_ASSERT(kill(p->task[0].pid, SIGSTOP) == 0, "SIGSTOP failed");
+ sleep(2);
+ STASIS_ASSERT(kill(p->task[0].pid, SIGCONT) == 0, "SIGCONT failed");
+ } else {
+ STASIS_ASSERT(false, "Task was marked as unused when it shouldn't have been");
+ }
+ pthread_join(th, NULL);
+}
+
int main(int argc, char *argv[]) {
STASIS_TEST_BEGIN_MAIN();
STASIS_TEST_FUNC *tests[] = {
@@ -121,6 +211,8 @@ int main(int argc, char *argv[]) {
test_mp_pool_join,
test_mp_pool_free,
test_mp_pool_workflow,
+ test_mp_fail_fast,
+ test_mp_stop_continue
};
STASIS_TEST_RUN(tests);
STASIS_TEST_END_MAIN();