forked from vieter-v/libvieter
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
|
BUILD_DIR ?= build
|
||||||
SRC_DIR ?= src
|
SRC_DIR ?= src
|
||||||
|
TEST_DIR ?= test
|
||||||
INC_DIRS ?= include
|
INC_DIRS ?= include
|
||||||
|
|
||||||
SRCS != find '$(SRC_DIR)' -iname '*.c'
|
SRCS != find '$(SRC_DIR)' -iname '*.c'
|
||||||
SRCS_H != find $(INC_DIRS) -iname '*.h'
|
SRCS_H != find $(INC_DIRS) -iname '*.h'
|
||||||
|
SRCS_TEST != find '$(TEST_DIR)' -iname '*.c'
|
||||||
|
|
||||||
OBJS := $(SRCS:%=$(BUILD_DIR)/%.o)
|
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))
|
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.
|
# 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
|
# -MP: generate a dummy target for every header file (according to the docs it
|
||||||
# prevents some errors when removing header files)
|
# 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
|
.PHONY: all
|
||||||
all: vieter
|
all: vieter
|
||||||
|
@ -28,19 +34,37 @@ vieter: $(BUILD_DIR)/$(LIB_FILENAME)
|
||||||
$(BUILD_DIR)/$(LIB_FILENAME): $(OBJS)
|
$(BUILD_DIR)/$(LIB_FILENAME): $(OBJS)
|
||||||
ar -rcs $@ $(OBJS)
|
ar -rcs $@ $(OBJS)
|
||||||
|
|
||||||
$(BUILD_DIR)/%.c.o: %.c
|
$(BUILD_DIR)/$(SRC_DIR)/%.c.o: $(SRC_DIR)/%.c
|
||||||
mkdir -p $(dir $@)
|
mkdir -p $(dir $@)
|
||||||
$(CC) $(CFLAGS) -c $< -o $@
|
$(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=====
|
# =====MAINTENANCE=====
|
||||||
.PHONY: lint
|
.PHONY: lint
|
||||||
lint:
|
lint:
|
||||||
clang-format -n --Werror $(SRCS) $(SRCS_H)
|
clang-format -n --Werror $(SRCS) $(SRCS_H) $(SRCS_TEST)
|
||||||
|
|
||||||
.PHONY: fmt
|
.PHONY: fmt
|
||||||
fmt:
|
fmt:
|
||||||
clang-format -i $(SRCS) $(SRCS_H)
|
clang-format -i $(SRCS) $(SRCS_H) $(SRCS_TEST)
|
||||||
|
|
||||||
.PHONY: clean
|
.PHONY: clean
|
||||||
clean:
|
clean:
|
||||||
|
|
16
README.md
16
README.md
|
@ -13,6 +13,21 @@ Currently it contains the following:
|
||||||
|
|
||||||
## Development
|
## 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`
|
### `compile_commands.json`
|
||||||
|
|
||||||
Clangd requires a `compile_commands.json` to function properly. You can
|
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
|
```sh
|
||||||
make clean
|
make clean
|
||||||
bear -- make
|
bear -- make
|
||||||
|
bear --append -- make build-test
|
||||||
```
|
```
|
||||||
|
|
||||||
This will create a `compile_commands.json` file in the current directory.
|
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