Unverified Commit ada3d6a0 authored by Yossi Gottlieb's avatar Yossi Gottlieb Committed by GitHub
Browse files

Properly get coverage from all tests. (#70)

* Add COVERAGE=1 Makefile option to build libraft with coverage.
* Refactor CFFI module to use the existing shared object in an
  out-of-line configuration, so there's no need to build and re-build
  it.
* Makefile adjustments to build the module CFFI as needed.
* Remove raft_get_snapshot_entry_idx, a prototype for an undefined
  function that breaks CFFI.
* Add a gcov Makefile target to fetch both unit tests and integration
  test coverage in one shot.
parent 8d7eff58
...@@ -7,8 +7,6 @@ jobs: ...@@ -7,8 +7,6 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
- name: make
run: make
- name: Set up Python for testing - name: Set up Python for testing
uses: actions/setup-python@v1 uses: actions/setup-python@v1
with: with:
...@@ -17,8 +15,13 @@ jobs: ...@@ -17,8 +15,13 @@ jobs:
- name: Install Python dependencies - name: Install Python dependencies
run: run:
python -m pip install -r tests/requirements.txt python -m pip install -r tests/requirements.txt
- name: Build
run: make COVERAGE=1
- name: Run tests - name: Run tests
run: run: |
make tests_full make tests
make test_fuzzer
make test_virtraft
make gcov
- name: Upload to codecov - name: Upload to codecov
uses: codecov/codecov-action@v2 uses: codecov/codecov-action@v2
...@@ -10,7 +10,12 @@ SHELL = /bin/bash ...@@ -10,7 +10,12 @@ SHELL = /bin/bash
CFLAGS += -Iinclude -Werror -Werror=return-type -Werror=uninitialized -Wcast-align \ CFLAGS += -Iinclude -Werror -Werror=return-type -Werror=uninitialized -Wcast-align \
-Wno-pointer-sign -fno-omit-frame-pointer -fno-common -fsigned-char \ -Wno-pointer-sign -fno-omit-frame-pointer -fno-common -fsigned-char \
-Wunused-variable -g -O2 -fPIC -Wunused-variable -g -O2 -fPIC
ifeq ($(COVERAGE), 1)
CFLAGS += $(GCOV_CFLAGS)
TEST_CFLAGS = $(CFLAGS)
else
TEST_CFLAGS = $(CFLAGS) $(GCOV_CFLAGS) TEST_CFLAGS = $(CFLAGS) $(GCOV_CFLAGS)
endif
UNAME := $(shell uname) UNAME := $(shell uname)
...@@ -28,6 +33,9 @@ SHAREDFLAGS = -shared ...@@ -28,6 +33,9 @@ SHAREDFLAGS = -shared
SHAREDEXT = so SHAREDEXT = so
endif endif
# Export LD_LIBRARY_PATH to allow the CFFI shared object to find libraft.so.
export LD_LIBRARY_PATH=$(PWD)
OBJECTS = \ OBJECTS = \
$(BUILD_DIR)/raft_server.o \ $(BUILD_DIR)/raft_server.o \
$(BUILD_DIR)/raft_server_properties.o \ $(BUILD_DIR)/raft_server_properties.o \
...@@ -44,20 +52,29 @@ TEST_HELPERS = \ ...@@ -44,20 +52,29 @@ TEST_HELPERS = \
TESTS = $(wildcard $(TEST_DIR)/test_*.c) TESTS = $(wildcard $(TEST_DIR)/test_*.c)
TEST_TARGETS = $(patsubst $(TEST_DIR)/%.c,$(BIN_DIR)/%,$(TESTS)) TEST_TARGETS = $(patsubst $(TEST_DIR)/%.c,$(BIN_DIR)/%,$(TESTS))
LIBRAFT_STATIC = libraft.a
LIBRAFT_SHARED = libraft.$(SHAREDEXT)
all: static shared all: static shared
.PHONY: shared shared: $(LIBRAFT_SHARED)
shared: $(OBJECTS)
$(CC) $(OBJECTS) $(LDFLAGS) $(CFLAGS) -fPIC $(SHAREDFLAGS) -o libraft.$(SHAREDEXT)
.PHONY: static static: $(LIBRAFT_STATIC)
static: $(OBJECTS)
ar -r libraft.a $(OBJECTS) $(LIBRAFT_SHARED): $(OBJECTS)
$(CC) $^ $(LDFLAGS) $(CFLAGS) -fPIC $(SHAREDFLAGS) -o $@
$(LIBRAFT_STATIC): $(OBJECTS)
ar -r $@ $^
.PHONY: tests .PHONY: tests
tests: $(TEST_TARGETS) tests: $(TEST_TARGETS)
gcov $(TEST_OBJECTS) gcov $(TEST_OBJECTS)
.PHONY: gcov
gcov: $(OBJECTS)
gcov $(OBJECTS) $(TEST_OBJECTS)
$(TEST_TARGETS): $(BIN_DIR)/%: $(TEST_OBJECTS) $(TEST_HELPERS) $(TEST_TARGETS): $(BIN_DIR)/%: $(TEST_OBJECTS) $(TEST_HELPERS)
$(CC) $(TEST_CFLAGS) $(TEST_DIR)/$*.c $(LIB) $(INC) $^ -o $@ $(CC) $(TEST_CFLAGS) $(TEST_DIR)/$*.c $(LIB) $(INC) $^ -o $@
./$@ ./$@
...@@ -75,8 +92,14 @@ $(BUILD_DIR)/%.o: $(SRC_DIR)/%.c ...@@ -75,8 +92,14 @@ $(BUILD_DIR)/%.o: $(SRC_DIR)/%.c
test_helper: $(TEST_HELPERS) test_helper: $(TEST_HELPERS)
$(CC) $(TEST_CFLAGS) -o $@ $(CC) $(TEST_CFLAGS) -o $@
# A file to signal we've built the CFFI module, because its name is not fixed.
RAFT_CFFI_TARGET = tests/.raft_cffi_built
$(RAFT_CFFI_TARGET): $(LIBRAFT_SHARED)
python3 tests/raft_cffi_builder.py
touch $(RAFT_CFFI_TARGET)
.PHONY: test_fuzzer .PHONY: test_fuzzer
test_fuzzer: test_fuzzer: $(RAFT_CFFI_TARGET)
python tests/log_fuzzer.py python tests/log_fuzzer.py
.PHONY: tests_full .PHONY: tests_full
...@@ -87,7 +110,7 @@ tests_full: ...@@ -87,7 +110,7 @@ tests_full:
make test_virtraft make test_virtraft
.PHONY: test_virtraft .PHONY: test_virtraft
test_virtraft: test_virtraft: $(RAFT_CFFI_TARGET)
python3 tests/virtraft2.py --servers 5 -i 20000 --compaction_rate 50 --drop_rate 5 -P 10 --seed 1 -m 3 $(VIRTRAFT_OPTS) python3 tests/virtraft2.py --servers 5 -i 20000 --compaction_rate 50 --drop_rate 5 -P 10 --seed 1 -m 3 $(VIRTRAFT_OPTS)
python3 tests/virtraft2.py --servers 5 -i 20000 --compaction_rate 50 --drop_rate 5 -P 10 --seed 1 -m 3 --client_rate 0 $(VIRTRAFT_OPTS) python3 tests/virtraft2.py --servers 5 -i 20000 --compaction_rate 50 --drop_rate 5 -P 10 --seed 1 -m 3 --client_rate 0 $(VIRTRAFT_OPTS)
python3 tests/virtraft2.py --servers 7 -i 20000 --compaction_rate 50 --drop_rate 5 -P 10 --seed 1 -m 3 $(VIRTRAFT_OPTS) python3 tests/virtraft2.py --servers 7 -i 20000 --compaction_rate 50 --drop_rate 5 -P 10 --seed 1 -m 3 $(VIRTRAFT_OPTS)
...@@ -116,6 +139,4 @@ do_infer: ...@@ -116,6 +139,4 @@ do_infer:
infer -- make infer -- make
clean: clean:
@rm -f ffi_tests.* src/*.o bin/* src/*.gcda src/*.gcno *.gcno *.gcda *.gcov tests/*.o tests/*.gcda tests/*.gcno; \ -@rm -f src/*.o bin/* src/*.gcda src/*.gcno *.gcno *.gcda *.gcov tests/*.o tests/*.gcda tests/*.gcno tests/raft_cffi.* $(RAFT_CFFI_TARGET) $(LIBRAFT_SHARED) $(LIBRAFT_STATIC)
if [ -f "libraft.$(SHAREDEXT)" ]; then rm libraft.$(SHAREDEXT); fi;\
if [ -f libraft.a ]; then rm libraft.a; fi;
...@@ -1289,10 +1289,6 @@ int raft_end_snapshot(raft_server_t *me_); ...@@ -1289,10 +1289,6 @@ int raft_end_snapshot(raft_server_t *me_);
*/ */
int raft_cancel_snapshot(raft_server_t *me_); int raft_cancel_snapshot(raft_server_t *me_);
/** Get the entry index of the entry that was snapshotted
**/
raft_index_t raft_get_snapshot_entry_idx(raft_server_t *me_);
/** Check is a snapshot is in progress /** Check is a snapshot is in progress
**/ **/
int raft_snapshot_is_in_progress(raft_server_t *me_); int raft_snapshot_is_in_progress(raft_server_t *me_);
......
from cffi import FFI
import subprocess import subprocess
import unittest import unittest
import raft_cffi
from hypothesis import given from hypothesis import given
from hypothesis.strategies import lists, just, integers, one_of from hypothesis.strategies import lists, just, integers, one_of
class Libraft(object):
def __init__(self):
ffi = FFI()
ffi.set_source(
"ffi_tests",
"""
""",
sources="""
src/raft_log.c
src/raft_server.c
src/raft_server_properties.c
src/raft_node.c
""".split(),
include_dirs=["include"],
extra_compile_args=["-UNDEBUG"]
)
library = ffi.compile()
self.ffi = ffi = FFI()
self.lib = ffi.dlopen(library)
def load(fname):
return '\n'.join(
[line for line in subprocess.check_output(
["gcc", "-E", fname]).decode('utf-8').split('\n')
if not line.startswith('#')])
ffi.cdef(load('include/raft.h'))
ffi.cdef(load('include/raft_log.h'))
commands = one_of( commands = one_of(
just('append'), just('append'),
just('poll'), just('poll'),
...@@ -71,7 +40,7 @@ class Log(object): ...@@ -71,7 +40,7 @@ class Log(object):
class CoreTestCase(unittest.TestCase): class CoreTestCase(unittest.TestCase):
def setUp(self): def setUp(self):
super(CoreTestCase, self).setUp() super(CoreTestCase, self).setUp()
self.r = Libraft() self.r = raft_cffi
@given(lists(commands)) @given(lists(commands))
def test_sanity_check(self, commands): def test_sanity_check(self, commands):
......
import cffi
import subprocess
ffi = cffi.FFI()
ffi.set_source(
"ffi_tests",
"""
#include "raft.h"
raft_entry_t *raft_entry_newdata(void *data) {
raft_entry_t *e = raft_entry_new(sizeof(void *));
*(void **) e->data = data;
e->refs = 100;
return e;
}
void *raft_entry_getdata(raft_entry_t *ety) {
return *(void **) ety->data;
}
raft_entry_t **raft_entry_array_deepcopy(raft_entry_t **src, int len) {
raft_entry_t **t = malloc(len * sizeof(raft_entry_t *));
int i;
for (i = 0; i < len; i++) {
int sz = sizeof(raft_entry_t) + src[i]->data_len;
t[i] = malloc(sz);
memcpy(t[i], src[i], sz);
}
return t;
}
""",
sources="""
src/raft_log.c
src/raft_server.c
src/raft_server_properties.c
src/raft_node.c
""".split(),
include_dirs=["include"],
extra_compile_args=["-UNDEBUG"]
)
library = ffi.compile()
ffi = cffi.FFI()
lib = ffi.dlopen(library)
def load(fname):
return '\n'.join(
[line for line in subprocess.check_output(
["gcc", "-E", fname]).decode('utf-8').split('\n')])
ffi.cdef('void *malloc(size_t __size);')
ffi.cdef(load('include/raft.h'))
ffi.cdef(load('include/raft_private.h'))
ffi.cdef(load('include/raft_log.h'))
ffi.cdef('raft_entry_t *raft_entry_newdata(void *data);')
ffi.cdef('void *raft_entry_getdata(raft_entry_t *);')
ffi.cdef('raft_entry_t **raft_entry_array_deepcopy(raft_entry_t **src, int len);')
import cffi
import subprocess
def load(fname):
return '\n'.join(
[line for line in subprocess.check_output(
["gcc", "-E", fname]).decode('utf-8').split('\n')])
if __name__ == '__main__':
ffibuilder = cffi.FFI()
ffibuilder.set_source(
"tests.raft_cffi",
"""
#include "raft.h"
#include "raft_private.h"
#include "raft_log.h"
raft_entry_t *raft_entry_newdata(void *data) {
raft_entry_t *e = raft_entry_new(sizeof(void *));
*(void **) e->data = data;
e->refs = 100;
return e;
}
void *raft_entry_getdata(raft_entry_t *ety) {
return *(void **) ety->data;
}
raft_entry_t **raft_entry_array_deepcopy(raft_entry_t **src, int len) {
raft_entry_t **t = malloc(len * sizeof(raft_entry_t *));
int i;
for (i = 0; i < len; i++) {
int sz = sizeof(raft_entry_t) + src[i]->data_len;
t[i] = malloc(sz);
memcpy(t[i], src[i], sz);
}
return t;
}
""",
libraries=["raft"],
include_dirs=["include"],
extra_compile_args=["-UNDEBUG"],
extra_link_args=["-L."]
)
ffibuilder.cdef('void *malloc(size_t __size);')
ffibuilder.cdef(load('include/raft.h'))
ffibuilder.cdef(load('include/raft_private.h'))
ffibuilder.cdef(load('include/raft_log.h'))
ffibuilder.cdef('raft_entry_t *raft_entry_newdata(void *data);')
ffibuilder.cdef('void *raft_entry_getdata(raft_entry_t *);')
ffibuilder.cdef('raft_entry_t **raft_entry_array_deepcopy(raft_entry_t **src, int len);')
ffibuilder.compile(verbose=True)
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment