Skip to content

Commit

Permalink
Add signal and exit code support (#9)
Browse files Browse the repository at this point in the history
Resolves #6

Decided on syntax `EXPECT_EXIT(status, { /* code */ }` because it's the most versatile.
Also a EXPECT_SIGNAL variant with the same syntax.
  • Loading branch information
Timothy-Gonzalez authored Feb 6, 2024
1 parent 859b8f8 commit 5593bd8
Show file tree
Hide file tree
Showing 9 changed files with 166 additions and 3 deletions.
15 changes: 15 additions & 0 deletions src/assertions.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#include "config.h"
#include "evaluators.h"
#include "formatters.h"
#include "fork.h"

#ifndef CAUGHT_ASSERTIONS
#define CAUGHT_ASSERTIONS
Expand Down Expand Up @@ -72,4 +73,18 @@ bool caught_internal_handle_assertion_result(caught_internal_assertion_result as
#define EXPECT_STR_PTR(lhs, op, rhs) \
CAUGHT_INTERNAL_EXPECT_HANDLE(STR_PTR, char **, lhs, op, rhs, caught_internal_evaluator_str_ptr, caught_internal_formatter_str_ptr)

#define CAUGHT_INTERNAL_EXPECT_TERMINATE_HANDLE(func_name, expected_status, execute_block) \
do \
{ \
CAUGHT_INTERNAL_FORK(execute_block) \
CAUGHT_INTERNAL_EXPECT_HANDLE(func_name, caught_internal_process_status, caught_internal_fork_child_status, ==, expected_status, \
caught_internal_evaluator_exit_status, caught_internal_formatter_exit_status); \
} while (0)

#define EXPECT_EXIT(expected_status, execute_block) \
CAUGHT_INTERNAL_EXPECT_TERMINATE_HANDLE(EXIT, create_caught_internal_process_status(0, expected_status), execute_block)

#define EXPECT_SIGNAL(expected_status, execute_block) \
CAUGHT_INTERNAL_EXPECT_TERMINATE_HANDLE(EXIT, create_caught_internal_process_status(1, expected_status), execute_block)

#endif
11 changes: 11 additions & 0 deletions src/evaluators.c
Original file line number Diff line number Diff line change
Expand Up @@ -94,3 +94,14 @@ bool caught_internal_evaluator_str_ptr(char **lhs, enum caught_operator operator
CAUGHT_GENERATE_GENERIC_EVALUATOR_NULL_GUARD(lhs, operator, rhs);
return caught_internal_evaluator_str(*lhs, operator, * rhs);
}

bool caught_internal_evaluator_exit_status(caught_internal_process_status lhs, enum caught_operator operator, caught_internal_process_status rhs)
{
if (operator!= CAUGHT_OP_EQUAL)
{
fprintf(stderr, "Cannot compare exit statuses with %s, only == is supported!", caught_operator_to_str(operator));
exit(1);
}

return lhs.type == rhs.type && lhs.status == rhs.status;
}
3 changes: 3 additions & 0 deletions src/evaluators.h
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#include <stdbool.h>
#include <string.h>
#include "fork.h"

#ifndef CAUGHT_EVALUATORS
#define CAUGHT_EVALUATORS
Expand Down Expand Up @@ -52,6 +53,8 @@ bool caught_internal_evaluator_char_ptr(char *lhs, enum caught_operator operator
bool caught_internal_evaluator_str(char *lhs, enum caught_operator operator, char * rhs);
bool caught_internal_evaluator_str_ptr(char **lhs, enum caught_operator operator, char ** rhs);

bool caught_internal_evaluator_exit_status(caught_internal_process_status lhs, enum caught_operator operator, caught_internal_process_status rhs);

// Uses default operators (==, <=, >=, ...) to compare lhs to rhs
#define CAUGHT_GENERATE_GENERIC_EVALUATOR(lhs, operator, rhs) \
switch (operator) \
Expand Down
19 changes: 19 additions & 0 deletions src/fork.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
#include "fork.h"

caught_internal_process_status create_caught_internal_process_status(int type, int status)
{
caught_internal_process_status new = {
.type = type,
.status = status,
.status_str = NULL,
};
if (type == 1 && status >= 1 && status <= 31)
{
new.status_str = signal_names[status - 1];
}
else if (type == 0 && status >= 0 && status <= 1)
{
new.status_str = exit_status_names[status];
}
return new;
}
59 changes: 59 additions & 0 deletions src/fork.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
#ifndef CAUGHT_FORK
#define CAUGHT_FORK

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>

typedef struct caught_internal_process_status
{
int type; // 0 for exit status, 1 for signal status
int status; // The exit status or signal, depending on above
const char *status_str; // The string signal, if there is one
} caught_internal_process_status;

static const char *exit_status_names[] = {"EXIT_SUCCESS", "EXIT_FAILURE"};

static const char *signal_names[] = {
"SIGHUP", "SIGINT", "SIGQUIT", "SIGILL", "SIGTRAP", "SIGABRT", "SIGBUS",
"SIGFPE", "SIGKILL", "SIGUSR1", "SIGSEGV", "SIGUSR2", "SIGPIPE", "SIGALRM",
"SIGTERM", "SIGSTKFLT", "SIGCHLD", "SIGCONT", "SIGSTOP", "SIGTSTP", "SIGTTIN",
"SIGTTOU", "SIGURG", "SIGXCPU", "SIGXFSZ", "SIGVTALRM", "SIGPROF", "SIGWINCH",
"SIGIO", "SIGPWR", "SIGSYS"};

caught_internal_process_status create_caught_internal_process_status(int type, int status);

#define CAUGHT_INTERNAL_FORK(child_execute_block) \
caught_internal_process_status caught_internal_fork_child_status = {}; \
pid_t caught_internal_pid = fork(); \
if (caught_internal_pid == -1) \
{ \
perror("Caught: failed to fork\n"); \
exit(EXIT_FAILURE); \
} \
if (caught_internal_pid == 0) \
{ \
child_execute_block \
\
perror("Caught: fork segment must call exit to prevent fork bombs\n"); \
exit(EXIT_FAILURE); \
} \
else \
{ \
int caught_internal_status = 0; \
waitpid(caught_internal_pid, &caught_internal_status, 0); \
if (WIFEXITED(caught_internal_status)) \
{ \
caught_internal_fork_child_status = \
create_caught_internal_process_status(0, WEXITSTATUS(caught_internal_status)); \
} \
else if (WIFSIGNALED(caught_internal_status)) \
{ \
caught_internal_fork_child_status = \
create_caught_internal_process_status(1, WTERMSIG(caught_internal_status)); \
} \
}

#endif
10 changes: 10 additions & 0 deletions src/formatters.c
Original file line number Diff line number Diff line change
Expand Up @@ -53,3 +53,13 @@ char *caught_internal_formatter_str_ptr(char **value)
CAUGHT_INTERNAL_FORMATTER_NULL_GUARD(value)
return caught_internal_formatter_str(*value);
}

char *caught_internal_formatter_exit_status(caught_internal_process_status value)
{
const char *type = value.type ? "Signal" : "Exit code";
if (value.status_str)
{
CAUGHT_INTERNAL_FORMATTER_FORMAT("%s (%i)", value.status_str, value.status)
}
CAUGHT_INTERNAL_FORMATTER_FORMAT("%s %i", type, value.status)
}
9 changes: 6 additions & 3 deletions src/formatters.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#define _GNU_SOURCE
#include <string.h>
#include <stdbool.h>
#include "fork.h"

#ifndef CAUGHT_FORMATTERS
#define CAUGHT_FORMATTERS
Expand All @@ -23,9 +24,11 @@ char *caught_internal_formatter_char_ptr(char *value);
char *caught_internal_formatter_str(char *value);
char *caught_internal_formatter_str_ptr(char **value);

#define CAUGHT_INTERNAL_FORMATTER_FORMAT(fstr, value) \
char *result; \
asprintf(&result, fstr, value); \
char *caught_internal_formatter_exit_status(caught_internal_process_status value);

#define CAUGHT_INTERNAL_FORMATTER_FORMAT(fstr, ...) \
char *result; \
asprintf(&result, fstr, __VA_ARGS__); \
return result;
#define CAUGHT_INTERNAL_FORMATTER_NULL_GUARD(value) \
if (value == NULL) \
Expand Down
21 changes: 21 additions & 0 deletions tests/exit.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// NOTE: The location of this include might differ in your code depending on location
// For example, it could be: #include "caught.h"
#include "../src/caught.h"

TEST("exit - success")
{
EXPECT_EXIT(EXIT_SUCCESS, {
exit(EXIT_SUCCESS);
});

EXPECT_INT(1 + 1, ==, 2); // This still runs
}

TEST("exit - failure")
{
EXPECT_EXIT(EXIT_FAILURE, {
exit(EXIT_FAILURE);
});

EXPECT_INT(1 + 1, ==, 2); // This still runs
}
22 changes: 22 additions & 0 deletions tests/signal.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// NOTE: The location of this include might differ in your code depending on location
// For example, it could be: #include "caught.h"
#include "../src/caught.h"

TEST("signal - SIGABRT")
{
EXPECT_SIGNAL(SIGABRT, {
raise(SIGABRT);
});

EXPECT_INT(1 + 1, ==, 2); // This still runs
}

TEST("signal - SIGSEGV")
{
EXPECT_SIGNAL(SIGSEGV, {
int *ptr = NULL;
ptr[1] = 123; // BAD!
});

EXPECT_INT(1 + 1, ==, 2); // This still runs
}

0 comments on commit 5593bd8

Please sign in to comment.