Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Unit tests #2

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
*.o
test
unit
6 changes: 4 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@

test: test.c src/commander.c
$(CC) $^ -std=c99 -o $@

unit: unit.c src/commander.c
$(CC) $^ -std=c99 -o $@

clean:
rm -f test
rm -f test unit

.PHONY: clean
24 changes: 15 additions & 9 deletions src/commander.c
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,9 @@
*/

static void
error(char *msg) {
error(command_t *self, char *msg) {
fprintf(stderr, "%s\n", msg);
exit(1);
self->exit(1);
}

/*
Expand All @@ -28,7 +28,12 @@ error(char *msg) {
static void
command_version(command_t *self) {
printf("%s\n", self->version);
exit(0);
self->exit(0);
}

static void
doExit(int status) {
exit(status);
}

/*
Expand All @@ -50,7 +55,7 @@ command_help(command_t *self) {
, option->description);
}
printf("\n");
exit(0);
self->exit(0);
}

/*
Expand All @@ -66,6 +71,7 @@ command_init(command_t *self, const char *name, const char *version) {
self->usage = "[options]";
command_option(self, "-V", "--version", "output program version", command_version);
command_option(self, "-h", "--help", "output help information", command_help);
self->exit = doExit;
}

/*
Expand Down Expand Up @@ -102,7 +108,7 @@ parse_argname(const char *str, char *flag, char *arg) {
void
command_option(command_t *self, const char *small, const char *large, const char *desc, command_callback_t cb) {
int n = self->option_count++;
if (n == COMMANDER_MAX_OPTIONS) error("Maximum option definitions exceeded");
if (n == COMMANDER_MAX_OPTIONS) error(self, "Maximum option definitions exceeded");
command_option_t *option = &self->options[n];
option->cb = cb;
option->small = small;
Expand Down Expand Up @@ -142,7 +148,7 @@ command_parse(command_t *self, int argc, char **argv) {
arg = argv[++i];
if (!arg || '-' == arg[0]) {
fprintf(stderr, "%s %s argument required\n", option->large, option->argname);
exit(1);
self->exit(1);
}
self->arg = arg;
}
Expand All @@ -169,12 +175,12 @@ command_parse(command_t *self, int argc, char **argv) {
// unrecognized
if ('-' == arg[0] && !literal) {
fprintf(stderr, "unrecognized flag %s\n", arg);
exit(1);
self->exit(1);
}

int n = self->argc++;
if (n == COMMANDER_MAX_ARGS) error("Maximum number of arguments exceeded");
if (n == COMMANDER_MAX_ARGS) error(self, "Maximum number of arguments exceeded");
self->argv[n] = (char *) arg;
match:;
}
}
}
3 changes: 2 additions & 1 deletion src/commander.h
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ typedef struct command {
command_option_t options[COMMANDER_MAX_OPTIONS];
int argc;
char *argv[COMMANDER_MAX_ARGS];
void (*exit)(int);
} command_t;

// prototypes
Expand All @@ -81,4 +82,4 @@ command_option(command_t *self, const char *small, const char *large, const char
void
command_parse(command_t *self, int argc, char **argv);

#endif /* COMMANDER_H */
#endif /* COMMANDER_H */
189 changes: 189 additions & 0 deletions unit.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,189 @@
#include <setjmp.h>
#include <stdio.h>
#include <string.h>
#include <sys/time.h>

#include "src/commander.h"

#define assertEquals(a,e) _assertEqualsI(a,e,__func__,__LINE__)
#define assertEqualStr(a,e) _assertEqualsI(strcmp(a,e),0,__func__,__LINE__)

static void _assertEqualsI(int actual, int expected, const char *file, int line) {
if (actual == expected) {
printf("%s/%d:\033[0;32m OK\033[0m\n", file, line);
} else {
printf("%s/%d:\033[0;31m expected %d but got %d\033[0m\n",
file, line, expected, actual);
}
}

#define assertFalse(a) _assertTrue(!(a),__func__,__LINE__)
#define assertTrue(a) _assertTrue((a),__func__,__LINE__)

static void _assertTrue(int actual, const char *file, int line) {
if (actual) {
printf("%s/%d:\033[0;32m OK\033[0m\n", file, line);
} else {
printf("%s/%d:\033[0;31m got %d\033[0m\n", file, line, actual);
}
}

static double getElapsedSecondsSince(struct timeval* start) {
struct timeval now;
gettimeofday(&now, 0);
int usec = now.tv_usec - start->tv_usec;
int sec = now.tv_sec - start->tv_sec;

return usec * 1e-6 + sec;
}

static int isVerboseSet = 0;
static int isRequiredSet = 0;
static int isOptionalSet = 0;
static int exitStatus = 0;

static void
verbose(command_t *self) {
isVerboseSet = 1;
}

static void
required(command_t *self) {
isRequiredSet = 1;
}

static void
optional(command_t *self) {
isOptionalSet = 1;
}

static jmp_buf exitCalled;

static void setStatus(int status) {
exitStatus = status;
longjmp(exitCalled, 1);
}

static void setUp() {
isVerboseSet = 0;
isRequiredSet = 0;
isOptionalSet = 0;
exitStatus = 0;
}

static void init(command_t* target) {
command_init(target, "name", "0.0.1");
target->exit = setStatus;
}

static void shouldEnableVerbose(void)
{
setUp();
command_t target;
init(&target);
command_option(&target, "-v", "--verbose", "enable verbose stuff", verbose);
char* argv[] = { "name", "-v", 0 };
if (!setjmp(exitCalled)) command_parse(&target, 2, argv);
assertTrue(isVerboseSet);
}

static void shouldSetExitStatus(void)
{
setUp();
command_t target;
init(&target);
command_option(&target, "-v", "--verbose", "enable verbose stuff", verbose);
char* argv[] = { "name", "-w", 0 };
if (!setjmp(exitCalled)) command_parse(&target, 2, argv);
assertFalse(isVerboseSet);
assertEquals(exitStatus, 1);
}

static void shouldSetRequired(void)
{
setUp();
command_t target;
init(&target);
command_option(&target, "-r", "--required <arg>", "required arg", required);
char* argv[] = { "name", "-r", "val", 0 };
if (!setjmp(exitCalled)) command_parse(&target, 3, argv);
assertEquals(exitStatus, 0);
assertTrue(isRequiredSet);
}

static void shouldFailIfRequiredIsMissingValue(void)
{
setUp();
command_t target;
init(&target);
command_option(&target, "-r", "--required <arg>", "required arg", required);
char* argv[] = { "name", "-r", 0 };
if (!setjmp(exitCalled)) command_parse(&target, 2, argv);
assertEquals(exitStatus, 1);
assertFalse(isRequiredSet);
}

static void shouldSetOptional(void)
{
setUp();
command_t target;
init(&target);
command_option(&target, "-o", "--optional [arg]", "optional arg", optional);
char* argv[] = { "name", "-o", "val", 0 };
if (!setjmp(exitCalled)) command_parse(&target, 3, argv);
assertEquals(exitStatus, 0);
assertTrue(isOptionalSet);
}

static void shouldNotFailIfOptionalIsMissingValue(void)
{
setUp();
command_t target;
init(&target);
command_option(&target, "-o", "--optional [arg]", "optional arg", optional);
char* argv[] = { "name", "-o", 0 };
if (!setjmp(exitCalled)) command_parse(&target, 2, argv);
assertEquals(exitStatus, 0);
assertTrue(isOptionalSet);
}

static void shouldSetAdditional(void)
{
setUp();
command_t target;
init(&target);
char* argv[] = { "name", "o", 0 };
if (!setjmp(exitCalled)) command_parse(&target, 2, argv);
assertEquals(exitStatus, 0);
assertEquals(target.argc, 1);
assertEqualStr(target.argv[0], "o");
}

static void shouldSetLiteral(void)
{
setUp();
command_t target;
init(&target);
char* argv[] = { "name", "--", "-o", 0 };
if (!setjmp(exitCalled)) command_parse(&target, 3, argv);
assertEquals(exitStatus, 0);
assertEquals(target.argc, 1);
assertEqualStr(target.argv[0], "-o");
}

int main(void) {
struct timeval start;
gettimeofday(&start, 0);

shouldEnableVerbose();
shouldSetExitStatus();
shouldSetRequired();
shouldFailIfRequiredIsMissingValue();
shouldSetOptional();
shouldNotFailIfOptionalIsMissingValue();
shouldSetAdditional();
shouldSetLiteral();
printf("%f seconds\n", getElapsedSecondsSince(&start));

return 0;
}