test: add structure & framework for writing tests
parent
3da95a63fb
commit
c018d8d86c
34
Makefile
34
Makefile
|
@ -2,12 +2,18 @@ LIB_FILENAME ?= libvieter.a
|
|||
|
||||
BUILD_DIR ?= build
|
||||
SRC_DIR ?= src
|
||||
TEST_DIR ?= test
|
||||
INC_DIRS ?= include
|
||||
|
||||
SRCS != find '$(SRC_DIR)' -iname '*.c'
|
||||
SRCS_H != find $(INC_DIRS) -iname '*.h'
|
||||
SRCS_TEST != find '$(TEST_DIR)' -iname '*.c'
|
||||
|
||||
OBJS := $(SRCS:%=$(BUILD_DIR)/%.o)
|
||||
DEPS := $(SRCS:%=$(BUILD_DIR)/%.d)
|
||||
OBJS_TEST := $(SRCS_TEST:%=$(BUILD_DIR)/%.o)
|
||||
DEPS := $(SRCS:%=$(BUILD_DIR)/%.d) $(SRCS_TEST:%=$(BUILD_DIR)/%.d)
|
||||
|
||||
BINS_TEST := $(OBJS_TEST:%.c.o=%)
|
||||
|
||||
INC_FLAGS := $(addprefix -I,$(INC_DIRS))
|
||||
|
||||
|
@ -16,7 +22,7 @@ INC_FLAGS := $(addprefix -I,$(INC_DIRS))
|
|||
# object file is also recompiled if only a header is changed.
|
||||
# -MP: generate a dummy target for every header file (according to the docs it
|
||||
# prevents some errors when removing header files)
|
||||
CFLAGS ?= $(INC_FLAGS) -O3 -MMD -MP -Wall -Werror -Wextra
|
||||
CFLAGS ?= $(INC_FLAGS) -MMD -MP -Wall -Werror -Wextra
|
||||
|
||||
.PHONY: all
|
||||
all: vieter
|
||||
|
@ -28,19 +34,37 @@ vieter: $(BUILD_DIR)/$(LIB_FILENAME)
|
|||
$(BUILD_DIR)/$(LIB_FILENAME): $(OBJS)
|
||||
ar -rcs $@ $(OBJS)
|
||||
|
||||
$(BUILD_DIR)/%.c.o: %.c
|
||||
$(BUILD_DIR)/$(SRC_DIR)/%.c.o: $(SRC_DIR)/%.c
|
||||
mkdir -p $(dir $@)
|
||||
$(CC) $(CFLAGS) -c $< -o $@
|
||||
|
||||
|
||||
# =====TESTING=====
|
||||
.PHONY: test
|
||||
test: build-test
|
||||
@ $(foreach bin,$(BINS_TEST),./$(bin);)
|
||||
|
||||
.PHONY: build-test
|
||||
build-test: $(BINS_TEST)
|
||||
|
||||
# For simplicity, we link every object file to each of the test files. This
|
||||
# might be changed later if this starts to become too slow.
|
||||
$(BINS_TEST): %: %.c.o $(OBJS)
|
||||
$(CC) $^ -o $@
|
||||
|
||||
# Each test includes the test directory, which contains the acutest header file
|
||||
$(BUILD_DIR)/$(TEST_DIR)/%.c.o: $(TEST_DIR)/%.c
|
||||
mkdir -p $(dir $@)
|
||||
$(CC) $(CFLAGS) -I$(TEST_DIR) -c $< -o $@
|
||||
|
||||
# =====MAINTENANCE=====
|
||||
.PHONY: lint
|
||||
lint:
|
||||
clang-format -n --Werror $(SRCS) $(SRCS_H)
|
||||
clang-format -n --Werror $(SRCS) $(SRCS_H) $(SRCS_TEST)
|
||||
|
||||
.PHONY: fmt
|
||||
fmt:
|
||||
clang-format -i $(SRCS) $(SRCS_H)
|
||||
clang-format -i $(SRCS) $(SRCS_H) $(SRCS_TEST)
|
||||
|
||||
.PHONY: clean
|
||||
clean:
|
||||
|
|
16
README.md
16
README.md
|
@ -13,6 +13,21 @@ Currently it contains the following:
|
|||
|
||||
## Development
|
||||
|
||||
### Compilation
|
||||
|
||||
Everything is handled by the provided Makefile. To compile the static library,
|
||||
simply run `make`.
|
||||
|
||||
### Testing
|
||||
|
||||
This library uses [Acutest](https://github.com/mity/acutest) for its tests.
|
||||
Tests should be placed in the `test` subdirectory, further divided into
|
||||
directories that correspond those in `src`. Test files should begin with
|
||||
`test_`, and their format should follow the expected format for Acutest.
|
||||
|
||||
To run the tests, simply run `make test`. If you wish to only run a specific
|
||||
test binary, you can find them in `build/test`.
|
||||
|
||||
### `compile_commands.json`
|
||||
|
||||
Clangd requires a `compile_commands.json` to function properly. You can
|
||||
|
@ -21,6 +36,7 @@ generate it using [bear](https://github.com/rizsotto/Bear):
|
|||
```sh
|
||||
make clean
|
||||
bear -- make
|
||||
bear --append -- make build-test
|
||||
```
|
||||
|
||||
This will create a `compile_commands.json` file in the current directory.
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,69 @@
|
|||
#include "acutest.h"
|
||||
#include "vieter_cron.h"
|
||||
|
||||
void test_tutorial(void) {
|
||||
void *mem;
|
||||
|
||||
mem = malloc(10);
|
||||
TEST_CHECK(mem != NULL);
|
||||
|
||||
mem = realloc(mem, 20);
|
||||
TEST_CHECK(mem != NULL);
|
||||
|
||||
free(mem);
|
||||
}
|
||||
|
||||
void test_fail(void) {
|
||||
int a, b;
|
||||
|
||||
/* This condition is designed to fail so you can see what the failed test
|
||||
* output looks like. */
|
||||
a = 1;
|
||||
b = 2;
|
||||
TEST_CHECK(a + b == 5);
|
||||
|
||||
/* Here is TEST_CHECK_ in action. */
|
||||
TEST_CHECK_(a + b == 5, "%d + %d == 5", a, b);
|
||||
|
||||
/* We may also show more information about the failure. */
|
||||
if (!TEST_CHECK(a + b == 5)) {
|
||||
TEST_MSG("a: %d", a);
|
||||
TEST_MSG("b: %d", b);
|
||||
}
|
||||
|
||||
/* The macro TEST_MSG() only outputs something when the preceding
|
||||
* condition fails, so we can avoid the 'if' statement. */
|
||||
TEST_CHECK(a + b == 3);
|
||||
TEST_MSG("a: %d", a);
|
||||
TEST_MSG("b: %d", b);
|
||||
}
|
||||
|
||||
static void helper(void) {
|
||||
/* Kill the current test with a condition which is never true. */
|
||||
TEST_ASSERT(1 == 2);
|
||||
|
||||
/* This never happens because the test is aborted above. */
|
||||
TEST_CHECK(1 + 2 == 2 + 1);
|
||||
}
|
||||
|
||||
void test_abort(void) {
|
||||
helper();
|
||||
|
||||
/* This test never happens because the test is aborted inside the helper()
|
||||
* function. */
|
||||
TEST_CHECK(1 * 2 == 2 * 1);
|
||||
}
|
||||
|
||||
void test_crash(void) {
|
||||
int *invalid = ((int *)NULL) + 0xdeadbeef;
|
||||
|
||||
*invalid = 42;
|
||||
TEST_CHECK_(1 == 1, "This should never execute, due to a write into "
|
||||
"an invalid address.");
|
||||
}
|
||||
|
||||
TEST_LIST = {{"tutorial", test_tutorial},
|
||||
{"fail", test_fail},
|
||||
{"abort", test_abort},
|
||||
{"crash", test_crash},
|
||||
{NULL, NULL}};
|
Loading…
Reference in New Issue