An initial simple unit test framework (#344)
The core idea was to take a lot of the stuff from the C unity framework and adapt it a bit here. Each file in the `unit` directory that starts with `test_` is automatically assumed to be a test suite. Within each file, all functions that start with `test_` are assumed to be a test. See unit/README.md for details about the implementation. Instead of compiling basically a net new binary, the way the tests are compiled is that the main valkey server is compiled as a static archive, which we then compile the individual test files against to create a new test executable. This is not all that important now, other than it makes the compilation simpler, but what it will allow us to do is overwrite functions in the archive to enable mocking for cross compilation unit functions. There are also ways to enable mocking from within the same compilation unit, but I don't know how important this is. Tests are also written in one of two styles: 1. Including the header file and directly calling functions from the archive. 2. Importing the original file, and then calling the functions. This second approach is cool because we can call static functions. It won't mess up the archive either. --------- Signed-off-by: Madelyn Olson <madelyneolson@gmail.com>
This commit is contained in:
parent
443d80f168
commit
5b1fd222ed
5
.github/workflows/ci.yml
vendored
5
.github/workflows/ci.yml
vendored
@ -14,7 +14,7 @@ jobs:
|
||||
- name: make
|
||||
# Fail build if there are warnings
|
||||
# build with TLS just for compilation coverage
|
||||
run: make SERVER_CFLAGS='-Werror' BUILD_TLS=yes
|
||||
run: make all-with-unit-tests SERVER_CFLAGS='-Werror' BUILD_TLS=yes
|
||||
- name: test
|
||||
run: |
|
||||
sudo apt-get install tcl8.6 tclx
|
||||
@ -27,6 +27,9 @@ jobs:
|
||||
make commands.def
|
||||
dirty=$(git diff)
|
||||
if [[ ! -z $dirty ]]; then echo $dirty; exit 1; fi
|
||||
- name: unit tests
|
||||
run: |
|
||||
./src/valkey-unit-tests
|
||||
|
||||
test-sanitizer-address:
|
||||
runs-on: ubuntu-latest
|
||||
|
31
.github/workflows/daily.yml
vendored
31
.github/workflows/daily.yml
vendored
@ -54,7 +54,7 @@ jobs:
|
||||
repository: ${{ env.GITHUB_REPOSITORY }}
|
||||
ref: ${{ env.GITHUB_HEAD_REF }}
|
||||
- name: make
|
||||
run: make SERVER_CFLAGS='-Werror -DSERVER_TEST'
|
||||
run: make all-with-unit-tests SERVER_CFLAGS='-Werror -DSERVER_TEST'
|
||||
- name: testprep
|
||||
run: sudo apt-get install tcl8.6 tclx
|
||||
- name: test
|
||||
@ -69,9 +69,12 @@ jobs:
|
||||
- name: cluster tests
|
||||
if: true && !contains(github.event.inputs.skiptests, 'cluster')
|
||||
run: ./runtest-cluster ${{github.event.inputs.cluster_test_args}}
|
||||
- name: unittest
|
||||
- name: legacy unit tests
|
||||
if: true && !contains(github.event.inputs.skiptests, 'unittest')
|
||||
run: ./src/valkey-server test all --accurate
|
||||
- name: new unit tests
|
||||
if: true && !contains(github.event.inputs.skiptests, 'unittest')
|
||||
run: ./src/valkey-unit-tests --accurate
|
||||
|
||||
test-ubuntu-jemalloc-fortify:
|
||||
runs-on: ubuntu-latest
|
||||
@ -113,9 +116,12 @@ jobs:
|
||||
- name: cluster tests
|
||||
if: true && !contains(github.event.inputs.skiptests, 'cluster')
|
||||
run: ./runtest-cluster ${{github.event.inputs.cluster_test_args}}
|
||||
- name: unittest
|
||||
- name: legacy unit tests
|
||||
if: true && !contains(github.event.inputs.skiptests, 'unittest')
|
||||
run: ./src/valkey-server test all --accurate
|
||||
- name: new unit tests
|
||||
if: true && !contains(github.event.inputs.skiptests, 'unittest')
|
||||
run: make valkey-unit-tests && ./src/valkey-unit-tests --accurate
|
||||
|
||||
test-ubuntu-libc-malloc:
|
||||
runs-on: ubuntu-latest
|
||||
@ -231,9 +237,12 @@ jobs:
|
||||
- name: cluster tests
|
||||
if: true && !contains(github.event.inputs.skiptests, 'cluster')
|
||||
run: ./runtest-cluster ${{github.event.inputs.cluster_test_args}}
|
||||
- name: unittest
|
||||
- name: legacy unit tests
|
||||
if: true && !contains(github.event.inputs.skiptests, 'unittest')
|
||||
run: ./src/valkey-server test all --accurate
|
||||
- name: new unit tests
|
||||
if: true && !contains(github.event.inputs.skiptests, 'unittest')
|
||||
run: ./src/valkey-unit-tests --accurate
|
||||
|
||||
test-ubuntu-tls:
|
||||
runs-on: ubuntu-latest
|
||||
@ -589,7 +598,7 @@ jobs:
|
||||
repository: ${{ env.GITHUB_REPOSITORY }}
|
||||
ref: ${{ env.GITHUB_HEAD_REF }}
|
||||
- name: make
|
||||
run: make SANITIZER=address SERVER_CFLAGS='-DSERVER_TEST -Werror -DDEBUG_ASSERTIONS'
|
||||
run: make all-with-unit-tests SANITIZER=address SERVER_CFLAGS='-DSERVER_TEST -Werror -DDEBUG_ASSERTIONS'
|
||||
- name: testprep
|
||||
# Work around ASAN issue, see https://github.com/google/sanitizers/issues/1716
|
||||
run: |
|
||||
@ -608,9 +617,12 @@ jobs:
|
||||
- name: cluster tests
|
||||
if: true && !contains(github.event.inputs.skiptests, 'cluster')
|
||||
run: ./runtest-cluster ${{github.event.inputs.cluster_test_args}}
|
||||
- name: unittest
|
||||
- name: legacy unit tests
|
||||
if: true && !contains(github.event.inputs.skiptests, 'unittest')
|
||||
run: ./src/valkey-server test all
|
||||
- name: new unit tests
|
||||
if: true && !contains(github.event.inputs.skiptests, 'unittest')
|
||||
run: ./src/valkey-unit-tests
|
||||
|
||||
test-sanitizer-undefined:
|
||||
runs-on: ubuntu-latest
|
||||
@ -638,7 +650,7 @@ jobs:
|
||||
repository: ${{ env.GITHUB_REPOSITORY }}
|
||||
ref: ${{ env.GITHUB_HEAD_REF }}
|
||||
- name: make
|
||||
run: make SANITIZER=undefined SERVER_CFLAGS='-DSERVER_TEST -Werror' LUA_DEBUG=yes # we (ab)use this flow to also check Lua C API violations
|
||||
run: make all-with-unit-tests SANITIZER=undefined SERVER_CFLAGS='-DSERVER_TEST -Werror' LUA_DEBUG=yes # we (ab)use this flow to also check Lua C API violations
|
||||
- name: testprep
|
||||
run: |
|
||||
sudo apt-get update
|
||||
@ -655,9 +667,12 @@ jobs:
|
||||
- name: cluster tests
|
||||
if: true && !contains(github.event.inputs.skiptests, 'cluster')
|
||||
run: ./runtest-cluster ${{github.event.inputs.cluster_test_args}}
|
||||
- name: unittest
|
||||
- name: legacy unit tests
|
||||
if: true && !contains(github.event.inputs.skiptests, 'unittest')
|
||||
run: ./src/valkey-server test all --accurate
|
||||
- name: new unit tests
|
||||
if: true && !contains(github.event.inputs.skiptests, 'unittest')
|
||||
run: ./src/valkey-unit-tests --accurate
|
||||
|
||||
test-centos7-jemalloc:
|
||||
runs-on: ubuntu-latest
|
||||
|
2
.gitignore
vendored
2
.gitignore
vendored
@ -1,5 +1,6 @@
|
||||
.*.swp
|
||||
*.o
|
||||
*.a
|
||||
*.xo
|
||||
*.so
|
||||
*.d
|
||||
@ -12,6 +13,7 @@ dump.rdb
|
||||
*-cli
|
||||
*-sentinel
|
||||
*-server
|
||||
*-unit-tests
|
||||
doc-tools
|
||||
release
|
||||
misc/*
|
||||
|
41
src/Makefile
41
src/Makefile
@ -98,6 +98,15 @@ ifeq ($(USE_JEMALLOC),no)
|
||||
MALLOC=libc
|
||||
endif
|
||||
|
||||
# Some unit tests compile files a second time to get access to static functions, the "--allow-multiple-definition" flag
|
||||
# allows us to do that without an error, by using the first instance of function. This behavior can also be used
|
||||
# to tweak behavior of code just for unit tests. The version of ld on MacOS apparently always does this.
|
||||
ifneq ($(uname_S),Darwin)
|
||||
ALLOW_DUPLICATE_FLAG=-Wl,--allow-multiple-definition
|
||||
else
|
||||
ALLOW_DUPLICATE_FLAG=
|
||||
endif
|
||||
|
||||
ifdef SANITIZER
|
||||
ifeq ($(SANITIZER),address)
|
||||
MALLOC=libc
|
||||
@ -357,6 +366,7 @@ else
|
||||
endif
|
||||
|
||||
SERVER_CC=$(QUIET_CC)$(CC) $(FINAL_CFLAGS)
|
||||
SERVER_AR=$(QUIET_AR)$(AR)
|
||||
SERVER_LD=$(QUIET_LINK)$(CC) $(FINAL_LDFLAGS)
|
||||
ENGINE_INSTALL=$(QUIET_INSTALL)$(INSTALL)
|
||||
|
||||
@ -372,6 +382,7 @@ QUIET_CC = @printf ' %b %b\n' $(CCCOLOR)CC$(ENDCOLOR) $(SRCCOLOR)$@$(ENDCOLOR
|
||||
QUIET_GEN = @printf ' %b %b\n' $(CCCOLOR)GEN$(ENDCOLOR) $(SRCCOLOR)$@$(ENDCOLOR) 1>&2;
|
||||
QUIET_LINK = @printf ' %b %b\n' $(LINKCOLOR)LINK$(ENDCOLOR) $(BINCOLOR)$@$(ENDCOLOR) 1>&2;
|
||||
QUIET_INSTALL = @printf ' %b %b\n' $(LINKCOLOR)INSTALL$(ENDCOLOR) $(BINCOLOR)$@$(ENDCOLOR) 1>&2;
|
||||
QUIET_AR = @printf ' %b %b\n' $(CCCOLOR)ARCHIVE$(ENDCOLOR) $(BINCOLOR)$@$(ENDCOLOR) 1>&2;
|
||||
endif
|
||||
|
||||
ifneq (, $(findstring LOG_REQ_RES, $(SERVER_CFLAGS)))
|
||||
@ -392,6 +403,10 @@ ENGINE_BENCHMARK_NAME=$(ENGINE_NAME)-benchmark$(PROG_SUFFIX)
|
||||
ENGINE_BENCHMARK_OBJ=ae.o anet.o valkey-benchmark.o adlist.o dict.o zmalloc.o serverassert.o release.o crcspeed.o crccombine.o crc64.o siphash.o crc16.o monotonic.o cli_common.o mt19937-64.o strl.o
|
||||
ENGINE_CHECK_RDB_NAME=$(ENGINE_NAME)-check-rdb$(PROG_SUFFIX)
|
||||
ENGINE_CHECK_AOF_NAME=$(ENGINE_NAME)-check-aof$(PROG_SUFFIX)
|
||||
ENGINE_LIB_NAME=lib$(ENGINE_NAME).a
|
||||
ENGINE_TEST_FILES:=$(wildcard unit/*.c)
|
||||
ENGINE_TEST_OBJ:=$(sort $(patsubst unit/%.c,unit/%.o,$(ENGINE_TEST_FILES)))
|
||||
ENGINE_UNIT_TESTS:=$(ENGINE_NAME)-unit-tests$(PROG_SUFFIX)
|
||||
ALL_SOURCES=$(sort $(patsubst %.o,%.c,$(ENGINE_SERVER_OBJ) $(ENGINE_CLI_OBJ) $(ENGINE_BENCHMARK_OBJ)))
|
||||
|
||||
all: $(SERVER_NAME) $(ENGINE_SENTINEL_NAME) $(ENGINE_CLI_NAME) $(ENGINE_BENCHMARK_NAME) $(ENGINE_CHECK_RDB_NAME) $(ENGINE_CHECK_AOF_NAME) $(TLS_MODULE)
|
||||
@ -408,6 +423,9 @@ endif
|
||||
|
||||
.PHONY: all
|
||||
|
||||
all-with-unit-tests: all $(ENGINE_UNIT_TESTS)
|
||||
.PHONY: all
|
||||
|
||||
persist-settings: distclean
|
||||
echo STD=$(STD) >> .make-settings
|
||||
echo WARN=$(WARN) >> .make-settings
|
||||
@ -442,6 +460,14 @@ endif
|
||||
$(SERVER_NAME): $(ENGINE_SERVER_OBJ)
|
||||
$(SERVER_LD) -o $@ $^ ../deps/hiredis/libhiredis.a ../deps/lua/src/liblua.a ../deps/hdr_histogram/libhdrhistogram.a ../deps/fpconv/libfpconv.a $(FINAL_LIBS)
|
||||
|
||||
# Valkey static library, used to compile against for unit testing
|
||||
$(ENGINE_LIB_NAME): $(ENGINE_SERVER_OBJ)
|
||||
$(SERVER_AR) rcs $@ $^
|
||||
|
||||
# valkey-unit-tests
|
||||
$(ENGINE_UNIT_TESTS): $(ENGINE_TEST_OBJ) $(ENGINE_LIB_NAME)
|
||||
$(SERVER_LD) $(ALLOW_DUPLICATE_FLAG) -o $@ $^ ../deps/fpconv/libfpconv.a $(FINAL_LIBS)
|
||||
|
||||
# valkey-sentinel
|
||||
$(ENGINE_SENTINEL_NAME): $(SERVER_NAME)
|
||||
$(ENGINE_INSTALL) $(SERVER_NAME) $(ENGINE_SENTINEL_NAME)
|
||||
@ -475,6 +501,9 @@ DEP = $(ENGINE_SERVER_OBJ:%.o=%.d) $(ENGINE_CLI_OBJ:%.o=%.d) $(ENGINE_BENCHMARK_
|
||||
%.o: %.c .make-prerequisites
|
||||
$(SERVER_CC) -MMD -o $@ -c $<
|
||||
|
||||
unit/%.o: unit/%.c .make-prerequisites
|
||||
$(SERVER_CC) -MMD -o $@ -c $<
|
||||
|
||||
# The following files are checked in and don't normally need to be rebuilt. They
|
||||
# are built only if python is available and their prereqs are modified.
|
||||
ifneq (,$(PYTHON))
|
||||
@ -485,12 +514,17 @@ fmtargs.h: ../utils/generate-fmtargs.py
|
||||
$(QUITE_GEN)sed '/Everything below this line/,$$d' $@ > $@.tmp
|
||||
$(QUITE_GEN)$(PYTHON) ../utils/generate-fmtargs.py >> $@.tmp
|
||||
$(QUITE_GEN)mv $@.tmp $@
|
||||
|
||||
unit/test_files.h: unit/*.c ../utils/generate-unit-test-header.py
|
||||
$(QUIET_GEN)$(PYTHON) ../utils/generate-unit-test-header.py
|
||||
|
||||
unit/test_main.o: unit/test_files.h
|
||||
endif
|
||||
|
||||
commands.c: $(COMMANDS_DEF_FILENAME).def
|
||||
|
||||
clean:
|
||||
rm -rf $(SERVER_NAME) $(ENGINE_SENTINEL_NAME) $(ENGINE_CLI_NAME) $(ENGINE_BENCHMARK_NAME) $(ENGINE_CHECK_RDB_NAME) $(ENGINE_CHECK_AOF_NAME) *.o *.gcda *.gcno *.gcov valkey.info lcov-html Makefile.dep *.so
|
||||
rm -rf $(SERVER_NAME) $(ENGINE_SENTINEL_NAME) $(ENGINE_CLI_NAME) $(ENGINE_BENCHMARK_NAME) $(ENGINE_CHECK_RDB_NAME) $(ENGINE_CHECK_AOF_NAME) $(ENGINE_UNIT_TESTS) $(ENGINE_LIB_NAME) unit/*.o unit/*.d *.o *.gcda *.gcno *.gcov valkey.info lcov-html Makefile.dep *.so
|
||||
rm -f $(DEP)
|
||||
|
||||
.PHONY: clean
|
||||
@ -506,6 +540,9 @@ distclean: clean
|
||||
test: $(SERVER_NAME) $(ENGINE_CHECK_AOF_NAME) $(ENGINE_CLI_NAME) $(ENGINE_BENCHMARK_NAME)
|
||||
@(cd ..; ./runtest)
|
||||
|
||||
test-unit: $(ENGINE_UNIT_TESTS)
|
||||
./$(ENGINE_UNIT_TESTS)
|
||||
|
||||
test-modules: $(SERVER_NAME)
|
||||
@(cd ..; ./runtest-moduleapi)
|
||||
|
||||
@ -533,7 +570,7 @@ bench: $(ENGINE_BENCHMARK_NAME)
|
||||
@echo ""
|
||||
@echo "WARNING: if it fails under Linux you probably need to install libc6-dev-i386"
|
||||
@echo ""
|
||||
$(MAKE) CFLAGS="-m32" LDFLAGS="-m32"
|
||||
$(MAKE) all-with-unit-tests CFLAGS="-m32" LDFLAGS="-m32"
|
||||
|
||||
gcov:
|
||||
$(MAKE) SERVER_CFLAGS="-fprofile-arcs -ftest-coverage -DCOVERAGE_TEST" SERVER_LDFLAGS="-fprofile-arcs -ftest-coverage"
|
||||
|
223
src/crc64.c
223
src/crc64.c
@ -141,226 +141,3 @@ void crc64_init(void) {
|
||||
uint64_t crc64(uint64_t crc, const unsigned char *s, uint64_t l) {
|
||||
return crcspeed64native(crc64_table, crc, (void *) s, l);
|
||||
}
|
||||
|
||||
/* Test main */
|
||||
#ifdef SERVER_TEST
|
||||
#include <stdio.h>
|
||||
|
||||
static void genBenchmarkRandomData(char *data, int count);
|
||||
static int bench_crc64(unsigned char *data, uint64_t size, long long passes, uint64_t check, char *name, int csv);
|
||||
static void bench_combine(char *label, uint64_t size, uint64_t expect, int csv);
|
||||
long long _ustime(void);
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <time.h>
|
||||
#include <sys/time.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "zmalloc.h"
|
||||
#include "crccombine.h"
|
||||
|
||||
long long _ustime(void) {
|
||||
struct timeval tv;
|
||||
long long ust;
|
||||
|
||||
gettimeofday(&tv, NULL);
|
||||
ust = ((long long)tv.tv_sec)*1000000;
|
||||
ust += tv.tv_usec;
|
||||
return ust;
|
||||
}
|
||||
|
||||
static int bench_crc64(unsigned char *data, uint64_t size, long long passes, uint64_t check, char *name, int csv) {
|
||||
uint64_t min = size, hash;
|
||||
long long original_start = _ustime(), original_end;
|
||||
for (long long i=passes; i > 0; i--) {
|
||||
hash = crc64(0, data, size);
|
||||
}
|
||||
original_end = _ustime();
|
||||
min = (original_end - original_start) * 1000 / passes;
|
||||
/* approximate nanoseconds without nstime */
|
||||
if (csv) {
|
||||
printf("%s,%" PRIu64 ",%" PRIu64 ",%d\n",
|
||||
name, size, (1000 * size) / min, hash == check);
|
||||
} else {
|
||||
printf("test size=%" PRIu64 " algorithm=%s %" PRIu64 " M/sec matches=%d\n",
|
||||
size, name, (1000 * size) / min, hash == check);
|
||||
}
|
||||
return hash != check;
|
||||
}
|
||||
|
||||
const uint64_t BENCH_RPOLY = UINT64_C(0x95ac9329ac4bc9b5);
|
||||
|
||||
static void bench_combine(char *label, uint64_t size, uint64_t expect, int csv) {
|
||||
uint64_t min = size, start = expect, thash = expect ^ (expect >> 17);
|
||||
long long original_start = _ustime(), original_end;
|
||||
for (int i=0; i < 1000; i++) {
|
||||
crc64_combine(thash, start, size, BENCH_RPOLY, 64);
|
||||
}
|
||||
original_end = _ustime();
|
||||
/* ran 1000 times, want ns per, counted us per 1000 ... */
|
||||
min = original_end - original_start;
|
||||
if (csv) {
|
||||
printf("%s,%" PRIu64 ",%" PRIu64 "\n", label, size, min);
|
||||
} else {
|
||||
printf("%s size=%" PRIu64 " in %" PRIu64 " nsec\n", label, size, min);
|
||||
}
|
||||
}
|
||||
|
||||
static void genBenchmarkRandomData(char *data, int count) {
|
||||
static uint32_t state = 1234;
|
||||
int i = 0;
|
||||
|
||||
while (count--) {
|
||||
state = (state*1103515245+12345);
|
||||
data[i++] = '0'+((state>>16)&63);
|
||||
}
|
||||
}
|
||||
|
||||
#define UNUSED(x) (void)(x)
|
||||
int crc64Test(int argc, char *argv[], int flags) {
|
||||
UNUSED(flags);
|
||||
|
||||
uint64_t crc64_test_size = 0;
|
||||
int i, lastarg, csv = 0, loop = 0, combine = 0;
|
||||
again:
|
||||
for (i = 3; i < argc; i++) {
|
||||
lastarg = (i == (argc-1));
|
||||
if (!strcmp(argv[i],"--help")) {
|
||||
goto usage;
|
||||
} else if (!strcmp(argv[i],"--csv")) {
|
||||
csv = 1;
|
||||
} else if (!strcmp(argv[i],"-l")) {
|
||||
loop = 1;
|
||||
} else if (!strcmp(argv[i],"--crc")) {
|
||||
if (lastarg) goto invalid;
|
||||
crc64_test_size = atoll(argv[++i]);
|
||||
} else if (!strcmp(argv[i],"--combine")) {
|
||||
combine = 1;
|
||||
} else {
|
||||
invalid:
|
||||
printf("Invalid option \"%s\" or option argument missing\n\n",argv[i]);
|
||||
usage:
|
||||
printf(
|
||||
"Usage: crc64 [OPTIONS]\n\n"
|
||||
" --csv Output in CSV format\n"
|
||||
" -l Loop. Run the tests forever\n"
|
||||
" --crc <bytes> Benchmark crc64 faster options, using a buffer this big, and quit when done.\n"
|
||||
" --combine Benchmark crc64 combine value ranges and timings.\n"
|
||||
);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (crc64_test_size == 0 && combine == 0) {
|
||||
crc64_init();
|
||||
printf("[calcula]: e9c6d914c4b8d9ca == %016" PRIx64 "\n",
|
||||
(uint64_t)_crc64(0, "123456789", 9));
|
||||
printf("[64speed]: e9c6d914c4b8d9ca == %016" PRIx64 "\n",
|
||||
(uint64_t)crc64(0, (unsigned char*)"123456789", 9));
|
||||
char li[] = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed "
|
||||
"do eiusmod tempor incididunt ut labore et dolore magna "
|
||||
"aliqua. Ut enim ad minim veniam, quis nostrud exercitation "
|
||||
"ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis "
|
||||
"aute irure dolor in reprehenderit in voluptate velit esse "
|
||||
"cillum dolore eu fugiat nulla pariatur. Excepteur sint "
|
||||
"occaecat cupidatat non proident, sunt in culpa qui officia "
|
||||
"deserunt mollit anim id est laborum.";
|
||||
printf("[calcula]: c7794709e69683b3 == %016" PRIx64 "\n",
|
||||
(uint64_t)_crc64(0, li, sizeof(li)));
|
||||
printf("[64speed]: c7794709e69683b3 == %016" PRIx64 "\n",
|
||||
(uint64_t)crc64(0, (unsigned char*)li, sizeof(li)));
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
int init_this_loop = 1;
|
||||
long long init_start, init_end;
|
||||
|
||||
do {
|
||||
unsigned char* data = NULL;
|
||||
uint64_t passes = 0;
|
||||
if (crc64_test_size) {
|
||||
data = zmalloc(crc64_test_size);
|
||||
genBenchmarkRandomData((char*)data, crc64_test_size);
|
||||
/* We want to hash about 1 gig of data in total, looped, to get a good
|
||||
* idea of our performance.
|
||||
*/
|
||||
passes = (UINT64_C(0x100000000) / crc64_test_size);
|
||||
passes = passes >= 2 ? passes : 2;
|
||||
passes = passes <= 1000 ? passes : 1000;
|
||||
}
|
||||
|
||||
crc64_init();
|
||||
/* warm up the cache */
|
||||
set_crc64_cutoffs(crc64_test_size+1, crc64_test_size+1);
|
||||
uint64_t expect = crc64(0, data, crc64_test_size);
|
||||
|
||||
if (!combine && crc64_test_size) {
|
||||
if (csv && init_this_loop) printf("algorithm,buffer,performance,crc64_matches\n");
|
||||
|
||||
/* get the single-character version for single-byte Redis behavior */
|
||||
set_crc64_cutoffs(0, crc64_test_size+1);
|
||||
if (bench_crc64(data, crc64_test_size, passes, expect, "crc_1byte", csv)) return 1;
|
||||
|
||||
set_crc64_cutoffs(crc64_test_size+1, crc64_test_size+1);
|
||||
/* run with 8-byte "single" path, crcfaster */
|
||||
if (bench_crc64(data, crc64_test_size, passes, expect, "crcspeed", csv)) return 1;
|
||||
|
||||
/* run with dual 8-byte paths */
|
||||
set_crc64_cutoffs(1, crc64_test_size+1);
|
||||
if (bench_crc64(data, crc64_test_size, passes, expect, "crcdual", csv)) return 1;
|
||||
|
||||
/* run with tri 8-byte paths */
|
||||
set_crc64_cutoffs(1, 1);
|
||||
if (bench_crc64(data, crc64_test_size, passes, expect, "crctri", csv)) return 1;
|
||||
|
||||
/* Be free memory region, be free. */
|
||||
zfree(data);
|
||||
data = NULL;
|
||||
}
|
||||
|
||||
uint64_t INIT_SIZE = UINT64_C(0xffffffffffffffff);
|
||||
if (combine) {
|
||||
if (init_this_loop) {
|
||||
init_start = _ustime();
|
||||
crc64_combine(
|
||||
UINT64_C(0xdeadbeefdeadbeef),
|
||||
UINT64_C(0xfeebdaedfeebdaed),
|
||||
INIT_SIZE,
|
||||
BENCH_RPOLY, 64);
|
||||
init_end = _ustime();
|
||||
|
||||
init_end -= init_start;
|
||||
init_end *= 1000;
|
||||
if (csv) {
|
||||
printf("operation,size,nanoseconds\n");
|
||||
printf("init_64,%" PRIu64 ",%" PRIu64 "\n", INIT_SIZE, (uint64_t)init_end);
|
||||
} else {
|
||||
printf("init_64 size=%" PRIu64 " in %" PRIu64 " nsec\n", INIT_SIZE, (uint64_t)init_end);
|
||||
}
|
||||
/* use the hash itself as the size (unpredictable) */
|
||||
bench_combine("hash_as_size_combine", crc64_test_size, expect, csv);
|
||||
|
||||
/* let's do something big (predictable, so fast) */
|
||||
bench_combine("largest_combine", INIT_SIZE, expect, csv);
|
||||
}
|
||||
bench_combine("combine", crc64_test_size, expect, csv);
|
||||
}
|
||||
init_this_loop = 0;
|
||||
/* step down by ~1.641 for a range of test sizes */
|
||||
crc64_test_size -= (crc64_test_size >> 2) + (crc64_test_size >> 3) + (crc64_test_size >> 6);
|
||||
} while (crc64_test_size > 3);
|
||||
if (loop) goto again;
|
||||
return 0;
|
||||
}
|
||||
# endif
|
||||
|
||||
|
||||
#ifdef SERVER_TEST_MAIN
|
||||
int main(int argc, char *argv[]) {
|
||||
return crc64Test(argc, argv);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@ -6,8 +6,4 @@
|
||||
void crc64_init(void);
|
||||
uint64_t crc64(uint64_t crc, const unsigned char *s, uint64_t l);
|
||||
|
||||
#ifdef SERVER_TEST
|
||||
int crc64Test(int argc, char *argv[], int flags);
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
217
src/intset.c
217
src/intset.c
@ -341,220 +341,3 @@ int intsetValidateIntegrity(const unsigned char *p, size_t size, int deep) {
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
#ifdef SERVER_TEST
|
||||
#include <sys/time.h>
|
||||
#include <time.h>
|
||||
|
||||
#if 0
|
||||
static void intsetRepr(intset *is) {
|
||||
for (uint32_t i = 0; i < intrev32ifbe(is->length); i++) {
|
||||
printf("%lld\n", (uint64_t)_intsetGet(is,i));
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
static void error(char *err) {
|
||||
printf("%s\n", err);
|
||||
exit(1);
|
||||
}
|
||||
#endif
|
||||
|
||||
static void ok(void) {
|
||||
printf("OK\n");
|
||||
}
|
||||
|
||||
static long long usec(void) {
|
||||
struct timeval tv;
|
||||
gettimeofday(&tv,NULL);
|
||||
return (((long long)tv.tv_sec)*1000000)+tv.tv_usec;
|
||||
}
|
||||
|
||||
static intset *createSet(int bits, int size) {
|
||||
uint64_t mask = (1<<bits)-1;
|
||||
uint64_t value;
|
||||
intset *is = intsetNew();
|
||||
|
||||
for (int i = 0; i < size; i++) {
|
||||
if (bits > 32) {
|
||||
value = (rand()*rand()) & mask;
|
||||
} else {
|
||||
value = rand() & mask;
|
||||
}
|
||||
is = intsetAdd(is,value,NULL);
|
||||
}
|
||||
return is;
|
||||
}
|
||||
|
||||
static void checkConsistency(intset *is) {
|
||||
for (uint32_t i = 0; i < (intrev32ifbe(is->length)-1); i++) {
|
||||
uint32_t encoding = intrev32ifbe(is->encoding);
|
||||
|
||||
if (encoding == INTSET_ENC_INT16) {
|
||||
int16_t *i16 = (int16_t*)is->contents;
|
||||
assert(i16[i] < i16[i+1]);
|
||||
} else if (encoding == INTSET_ENC_INT32) {
|
||||
int32_t *i32 = (int32_t*)is->contents;
|
||||
assert(i32[i] < i32[i+1]);
|
||||
} else {
|
||||
int64_t *i64 = (int64_t*)is->contents;
|
||||
assert(i64[i] < i64[i+1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#define UNUSED(x) (void)(x)
|
||||
int intsetTest(int argc, char **argv, int flags) {
|
||||
uint8_t success;
|
||||
int i;
|
||||
intset *is;
|
||||
srand(time(NULL));
|
||||
|
||||
UNUSED(argc);
|
||||
UNUSED(argv);
|
||||
UNUSED(flags);
|
||||
|
||||
printf("Value encodings: "); {
|
||||
assert(_intsetValueEncoding(-32768) == INTSET_ENC_INT16);
|
||||
assert(_intsetValueEncoding(+32767) == INTSET_ENC_INT16);
|
||||
assert(_intsetValueEncoding(-32769) == INTSET_ENC_INT32);
|
||||
assert(_intsetValueEncoding(+32768) == INTSET_ENC_INT32);
|
||||
assert(_intsetValueEncoding(-2147483648) == INTSET_ENC_INT32);
|
||||
assert(_intsetValueEncoding(+2147483647) == INTSET_ENC_INT32);
|
||||
assert(_intsetValueEncoding(-2147483649) == INTSET_ENC_INT64);
|
||||
assert(_intsetValueEncoding(+2147483648) == INTSET_ENC_INT64);
|
||||
assert(_intsetValueEncoding(-9223372036854775808ull) ==
|
||||
INTSET_ENC_INT64);
|
||||
assert(_intsetValueEncoding(+9223372036854775807ull) ==
|
||||
INTSET_ENC_INT64);
|
||||
ok();
|
||||
}
|
||||
|
||||
printf("Basic adding: "); {
|
||||
is = intsetNew();
|
||||
is = intsetAdd(is,5,&success); assert(success);
|
||||
is = intsetAdd(is,6,&success); assert(success);
|
||||
is = intsetAdd(is,4,&success); assert(success);
|
||||
is = intsetAdd(is,4,&success); assert(!success);
|
||||
assert(6 == intsetMax(is));
|
||||
assert(4 == intsetMin(is));
|
||||
ok();
|
||||
zfree(is);
|
||||
}
|
||||
|
||||
printf("Large number of random adds: "); {
|
||||
uint32_t inserts = 0;
|
||||
is = intsetNew();
|
||||
for (i = 0; i < 1024; i++) {
|
||||
is = intsetAdd(is,rand()%0x800,&success);
|
||||
if (success) inserts++;
|
||||
}
|
||||
assert(intrev32ifbe(is->length) == inserts);
|
||||
checkConsistency(is);
|
||||
ok();
|
||||
zfree(is);
|
||||
}
|
||||
|
||||
printf("Upgrade from int16 to int32: "); {
|
||||
is = intsetNew();
|
||||
is = intsetAdd(is,32,NULL);
|
||||
assert(intrev32ifbe(is->encoding) == INTSET_ENC_INT16);
|
||||
is = intsetAdd(is,65535,NULL);
|
||||
assert(intrev32ifbe(is->encoding) == INTSET_ENC_INT32);
|
||||
assert(intsetFind(is,32));
|
||||
assert(intsetFind(is,65535));
|
||||
checkConsistency(is);
|
||||
zfree(is);
|
||||
|
||||
is = intsetNew();
|
||||
is = intsetAdd(is,32,NULL);
|
||||
assert(intrev32ifbe(is->encoding) == INTSET_ENC_INT16);
|
||||
is = intsetAdd(is,-65535,NULL);
|
||||
assert(intrev32ifbe(is->encoding) == INTSET_ENC_INT32);
|
||||
assert(intsetFind(is,32));
|
||||
assert(intsetFind(is,-65535));
|
||||
checkConsistency(is);
|
||||
ok();
|
||||
zfree(is);
|
||||
}
|
||||
|
||||
printf("Upgrade from int16 to int64: "); {
|
||||
is = intsetNew();
|
||||
is = intsetAdd(is,32,NULL);
|
||||
assert(intrev32ifbe(is->encoding) == INTSET_ENC_INT16);
|
||||
is = intsetAdd(is,4294967295,NULL);
|
||||
assert(intrev32ifbe(is->encoding) == INTSET_ENC_INT64);
|
||||
assert(intsetFind(is,32));
|
||||
assert(intsetFind(is,4294967295));
|
||||
checkConsistency(is);
|
||||
zfree(is);
|
||||
|
||||
is = intsetNew();
|
||||
is = intsetAdd(is,32,NULL);
|
||||
assert(intrev32ifbe(is->encoding) == INTSET_ENC_INT16);
|
||||
is = intsetAdd(is,-4294967295,NULL);
|
||||
assert(intrev32ifbe(is->encoding) == INTSET_ENC_INT64);
|
||||
assert(intsetFind(is,32));
|
||||
assert(intsetFind(is,-4294967295));
|
||||
checkConsistency(is);
|
||||
ok();
|
||||
zfree(is);
|
||||
}
|
||||
|
||||
printf("Upgrade from int32 to int64: "); {
|
||||
is = intsetNew();
|
||||
is = intsetAdd(is,65535,NULL);
|
||||
assert(intrev32ifbe(is->encoding) == INTSET_ENC_INT32);
|
||||
is = intsetAdd(is,4294967295,NULL);
|
||||
assert(intrev32ifbe(is->encoding) == INTSET_ENC_INT64);
|
||||
assert(intsetFind(is,65535));
|
||||
assert(intsetFind(is,4294967295));
|
||||
checkConsistency(is);
|
||||
zfree(is);
|
||||
|
||||
is = intsetNew();
|
||||
is = intsetAdd(is,65535,NULL);
|
||||
assert(intrev32ifbe(is->encoding) == INTSET_ENC_INT32);
|
||||
is = intsetAdd(is,-4294967295,NULL);
|
||||
assert(intrev32ifbe(is->encoding) == INTSET_ENC_INT64);
|
||||
assert(intsetFind(is,65535));
|
||||
assert(intsetFind(is,-4294967295));
|
||||
checkConsistency(is);
|
||||
ok();
|
||||
zfree(is);
|
||||
}
|
||||
|
||||
printf("Stress lookups: "); {
|
||||
long num = 100000, size = 10000;
|
||||
int i, bits = 20;
|
||||
long long start;
|
||||
is = createSet(bits,size);
|
||||
checkConsistency(is);
|
||||
|
||||
start = usec();
|
||||
for (i = 0; i < num; i++) intsetSearch(is,rand() % ((1<<bits)-1),NULL);
|
||||
printf("%ld lookups, %ld element set, %lldusec\n",
|
||||
num,size,usec()-start);
|
||||
zfree(is);
|
||||
}
|
||||
|
||||
printf("Stress add+delete: "); {
|
||||
int i, v1, v2;
|
||||
is = intsetNew();
|
||||
for (i = 0; i < 0xffff; i++) {
|
||||
v1 = rand() % 0xfff;
|
||||
is = intsetAdd(is,v1,NULL);
|
||||
assert(intsetFind(is,v1));
|
||||
|
||||
v2 = rand() % 0xfff;
|
||||
is = intsetRemove(is,v2,NULL);
|
||||
assert(!intsetFind(is,v2));
|
||||
}
|
||||
checkConsistency(is);
|
||||
ok();
|
||||
zfree(is);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
@ -6894,12 +6894,10 @@ struct serverTest {
|
||||
} serverTests[] = {
|
||||
{"ziplist", ziplistTest},
|
||||
{"quicklist", quicklistTest},
|
||||
{"intset", intsetTest},
|
||||
{"zipmap", zipmapTest},
|
||||
{"sha1test", sha1Test},
|
||||
{"util", utilTest},
|
||||
{"endianconv", endianconvTest},
|
||||
{"crc64", crc64Test},
|
||||
{"zmalloc", zmalloc_test},
|
||||
{"sds", sdsTest},
|
||||
{"dict", dictTest},
|
||||
|
54
src/unit/README.md
Normal file
54
src/unit/README.md
Normal file
@ -0,0 +1,54 @@
|
||||
## Introduction
|
||||
Valkey uses a very simple C testing framework, built up over time but now based loosely off of [Unity](https://www.throwtheswitch.org/unity).
|
||||
|
||||
All test files being test_ in the unit directory.
|
||||
A single test file can have multiple individual tests, and they must be of the form `int test_<test_name>(int argc, char *argv[], int flags) {`, where test_name is the name of the test.
|
||||
The test name must be globally unique.
|
||||
A test will be marked as successful if returns 0, and will be marked failed in all other cases.
|
||||
|
||||
The test framework also parses several flags passed in, and sets them based on the arguments to the tests.
|
||||
|
||||
Tests flags:
|
||||
* UNIT_TEST_ACCURATE: Corresponds to the --accurate flag. This flag indicates the test should use extra computation to more accurately validate the tests.
|
||||
* UNIT_TEST_LARGE_MEMORY: Corresponds to the --large-memory flag. This flag indicates whether or not tests should use more than 100mb of memory.
|
||||
* UNIT_TEST_SINGLE: Corresponds to the --single flag. This flag indicates that a single test is being executed.
|
||||
|
||||
Tests are allowed to be passed in additional arbitrary argv/argc, which they can access from the argc and argv arguments of the test.
|
||||
|
||||
## Assertions
|
||||
|
||||
There are a few built in assertions that can be used, that will automatically return from the current function and return the correct error code.
|
||||
Assertions are also useful as they will print out the line number that they failed on.
|
||||
|
||||
* `TEST_ASSERT(condition)`: Will evaluate the condition, and if it fails it will return 1 and print out the condition that failed.
|
||||
* `TEST_ASSERT_MESSAGE(message, condition`): Will evaluate the condition, and if it fails it will return 1 and print out the provided message.
|
||||
|
||||
## Other utilities
|
||||
|
||||
If you would like to print out additional data, use the `TEST_PRINT_INFO(info, ...)` option, which has arguments similar to printf.
|
||||
This macro will also print out the function the code was executed from in addition to the line it was printed from.
|
||||
|
||||
## Example test
|
||||
|
||||
```
|
||||
int test_example(int argc, char *argv[], int flags) {
|
||||
TEST_ASSERT(5 == 5);
|
||||
TEST_ASSERT_MESSAGE("This should pass", 6 == 6);
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
|
||||
## Running tests
|
||||
Tests can be run by executing:
|
||||
|
||||
```
|
||||
make valkey-unit-tests
|
||||
./valkey-unit-tests
|
||||
```
|
||||
|
||||
Running a single unit test file
|
||||
```
|
||||
./valkey-unit-tests --single crc64.c
|
||||
```
|
||||
|
||||
Will just run the crc64.c file.
|
30
src/unit/test_crc64.c
Normal file
30
src/unit/test_crc64.c
Normal file
@ -0,0 +1,30 @@
|
||||
#include <stdio.h>
|
||||
#include "../crc64.h"
|
||||
|
||||
#include "test_help.h"
|
||||
|
||||
extern uint64_t _crc64(uint_fast64_t crc, const void *in_data, const uint64_t len);
|
||||
|
||||
int test_crc64(int argc, char **argv, int flags) {
|
||||
UNUSED(argc);
|
||||
UNUSED(argv);
|
||||
UNUSED(flags);
|
||||
crc64_init();
|
||||
|
||||
unsigned char numbers[] = "123456789";
|
||||
TEST_ASSERT_MESSAGE("[calcula]: CRC64 '123456789'", (uint64_t)_crc64(0, numbers, 9) == 16845390139448941002ull);
|
||||
TEST_ASSERT_MESSAGE("[calcula]: CRC64 '123456789'", (uint64_t)crc64(0, numbers, 9) == 16845390139448941002ull);
|
||||
|
||||
unsigned char li[] = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed "
|
||||
"do eiusmod tempor incididunt ut labore et dolore magna "
|
||||
"aliqua. Ut enim ad minim veniam, quis nostrud exercitation "
|
||||
"ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis "
|
||||
"aute irure dolor in reprehenderit in voluptate velit esse "
|
||||
"cillum dolore eu fugiat nulla pariatur. Excepteur sint "
|
||||
"occaecat cupidatat non proident, sunt in culpa qui officia "
|
||||
"deserunt mollit anim id est laborum.";
|
||||
|
||||
TEST_ASSERT_MESSAGE("[calcula]: CRC64 TEXT'", (uint64_t)_crc64(0, li, sizeof(li)) == 14373597793578550195ull);
|
||||
TEST_ASSERT_MESSAGE("[calcula]: CRC64 TEXT", (uint64_t)crc64(0, li, sizeof(li)) == 14373597793578550195ull);
|
||||
return 0;
|
||||
}
|
193
src/unit/test_crc64combine.c
Normal file
193
src/unit/test_crc64combine.c
Normal file
@ -0,0 +1,193 @@
|
||||
#include <inttypes.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <time.h>
|
||||
#include <sys/time.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "test_help.h"
|
||||
#include "../zmalloc.h"
|
||||
#include "../crc64.h"
|
||||
#include "../crcspeed.h"
|
||||
#include "../crccombine.h"
|
||||
|
||||
static void genBenchmarkRandomData(char *data, int count);
|
||||
static int bench_crc64(unsigned char *data, uint64_t size, long long passes, uint64_t check, char *name, int csv);
|
||||
static void bench_combine(char *label, uint64_t size, uint64_t expect, int csv);
|
||||
|
||||
long long _ustime(void) {
|
||||
struct timeval tv;
|
||||
long long ust;
|
||||
|
||||
gettimeofday(&tv, NULL);
|
||||
ust = ((long long)tv.tv_sec)*1000000;
|
||||
ust += tv.tv_usec;
|
||||
return ust;
|
||||
}
|
||||
|
||||
static int bench_crc64(unsigned char *data, uint64_t size, long long passes, uint64_t check, char *name, int csv) {
|
||||
uint64_t min = size, hash;
|
||||
long long original_start = _ustime(), original_end;
|
||||
for (long long i=passes; i > 0; i--) {
|
||||
hash = crc64(0, data, size);
|
||||
}
|
||||
original_end = _ustime();
|
||||
min = (original_end - original_start) * 1000 / passes;
|
||||
/* approximate nanoseconds without nstime */
|
||||
if (csv) {
|
||||
printf("%s,%" PRIu64 ",%" PRIu64 ",%d\n",
|
||||
name, size, (1000 * size) / min, hash == check);
|
||||
} else {
|
||||
TEST_PRINT_INFO("test size=%" PRIu64 " algorithm=%s %" PRIu64 " M/sec matches=%d",
|
||||
size, name, (1000 * size) / min, hash == check);
|
||||
}
|
||||
return hash != check;
|
||||
}
|
||||
|
||||
const uint64_t BENCH_RPOLY = UINT64_C(0x95ac9329ac4bc9b5);
|
||||
|
||||
static void bench_combine(char *label, uint64_t size, uint64_t expect, int csv) {
|
||||
uint64_t min = size, start = expect, thash = expect ^ (expect >> 17);
|
||||
long long original_start = _ustime(), original_end;
|
||||
for (int i=0; i < 1000; i++) {
|
||||
crc64_combine(thash, start, size, BENCH_RPOLY, 64);
|
||||
}
|
||||
original_end = _ustime();
|
||||
/* ran 1000 times, want ns per, counted us per 1000 ... */
|
||||
min = original_end - original_start;
|
||||
if (csv) {
|
||||
printf("%s,%" PRIu64 ",%" PRIu64 "\n", label, size, min);
|
||||
} else {
|
||||
printf("%s size=%" PRIu64 " in %" PRIu64 " nsec\n", label, size, min);
|
||||
}
|
||||
}
|
||||
|
||||
static void genBenchmarkRandomData(char *data, int count) {
|
||||
static uint32_t state = 1234;
|
||||
int i = 0;
|
||||
|
||||
while (count--) {
|
||||
state = (state*1103515245+12345);
|
||||
data[i++] = '0'+((state>>16)&63);
|
||||
}
|
||||
}
|
||||
|
||||
/* This is a special unit test useful for benchmarking crc64combine performance. The
|
||||
* benchmarking is only done when the tests are invoked with a single test target,
|
||||
* like 'valkey-unit-tests --single test_crc64combine.c --crc 16384'. */
|
||||
int test_crc64combine(int argc, char **argv, int flags) {
|
||||
if (!(flags & UNIT_TEST_SINGLE)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint64_t crc64_test_size = 0;
|
||||
int i, lastarg, csv = 0, loop = 0, combine = 0;
|
||||
again:
|
||||
for (i = 3; i < argc; i++) {
|
||||
lastarg = (i == (argc-1));
|
||||
if (!strcmp(argv[i],"--help")) {
|
||||
goto usage;
|
||||
} else if (!strcmp(argv[i],"--csv")) {
|
||||
csv = 1;
|
||||
} else if (!strcmp(argv[i],"-l")) {
|
||||
loop = 1;
|
||||
} else if (!strcmp(argv[i],"--crc")) {
|
||||
if (lastarg) goto invalid;
|
||||
crc64_test_size = atoll(argv[++i]);
|
||||
} else if (!strcmp(argv[i],"--combine")) {
|
||||
combine = 1;
|
||||
} else {
|
||||
invalid:
|
||||
printf("Invalid option \"%s\" or option argument missing\n\n",argv[i]);
|
||||
usage:
|
||||
printf(
|
||||
"Usage: --single test_crc64combine.c [OPTIONS]\n\n"
|
||||
" --csv Output in CSV format\n"
|
||||
" -l Loop. Run the tests forever\n"
|
||||
" --crc <bytes> Benchmark crc64 faster options, using a buffer this big, and quit when done.\n"
|
||||
" --combine Benchmark crc64 combine value ranges and timings.\n"
|
||||
);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
int init_this_loop = 1;
|
||||
long long init_start, init_end;
|
||||
|
||||
do {
|
||||
unsigned char* data = NULL;
|
||||
uint64_t passes = 0;
|
||||
if (crc64_test_size) {
|
||||
data = zmalloc(crc64_test_size);
|
||||
genBenchmarkRandomData((char*)data, crc64_test_size);
|
||||
/* We want to hash about 1 gig of data in total, looped, to get a good
|
||||
* idea of our performance.
|
||||
*/
|
||||
passes = (UINT64_C(0x100000000) / crc64_test_size);
|
||||
passes = passes >= 2 ? passes : 2;
|
||||
passes = passes <= 1000 ? passes : 1000;
|
||||
}
|
||||
|
||||
crc64_init();
|
||||
/* warm up the cache */
|
||||
set_crc64_cutoffs(crc64_test_size+1, crc64_test_size+1);
|
||||
uint64_t expect = crc64(0, data, crc64_test_size);
|
||||
|
||||
if (!combine && crc64_test_size) {
|
||||
if (csv && init_this_loop) printf("algorithm,buffer,performance,crc64_matches\n");
|
||||
|
||||
/* get the single-character version for single-byte Redis behavior */
|
||||
set_crc64_cutoffs(0, crc64_test_size+1);
|
||||
if (bench_crc64(data, crc64_test_size, passes, expect, "crc_1byte", csv)) return 1;
|
||||
|
||||
set_crc64_cutoffs(crc64_test_size+1, crc64_test_size+1);
|
||||
/* run with 8-byte "single" path, crcfaster */
|
||||
if (bench_crc64(data, crc64_test_size, passes, expect, "crcspeed", csv)) return 1;
|
||||
|
||||
/* run with dual 8-byte paths */
|
||||
set_crc64_cutoffs(1, crc64_test_size+1);
|
||||
if (bench_crc64(data, crc64_test_size, passes, expect, "crcdual", csv)) return 1;
|
||||
|
||||
/* run with tri 8-byte paths */
|
||||
set_crc64_cutoffs(1, 1);
|
||||
if (bench_crc64(data, crc64_test_size, passes, expect, "crctri", csv)) return 1;
|
||||
|
||||
/* Be free memory region, be free. */
|
||||
zfree(data);
|
||||
data = NULL;
|
||||
}
|
||||
|
||||
uint64_t INIT_SIZE = UINT64_C(0xffffffffffffffff);
|
||||
if (combine) {
|
||||
if (init_this_loop) {
|
||||
init_start = _ustime();
|
||||
crc64_combine(
|
||||
UINT64_C(0xdeadbeefdeadbeef),
|
||||
UINT64_C(0xfeebdaedfeebdaed),
|
||||
INIT_SIZE,
|
||||
BENCH_RPOLY, 64);
|
||||
init_end = _ustime();
|
||||
|
||||
init_end -= init_start;
|
||||
init_end *= 1000;
|
||||
if (csv) {
|
||||
printf("operation,size,nanoseconds\n");
|
||||
printf("init_64,%" PRIu64 ",%" PRIu64 "\n", INIT_SIZE, (uint64_t)init_end);
|
||||
} else {
|
||||
TEST_PRINT_INFO("init_64 size=%" PRIu64 " in %" PRIu64 " nsec", INIT_SIZE, (uint64_t)init_end);
|
||||
}
|
||||
/* use the hash itself as the size (unpredictable) */
|
||||
bench_combine("hash_as_size_combine", crc64_test_size, expect, csv);
|
||||
|
||||
/* let's do something big (predictable, so fast) */
|
||||
bench_combine("largest_combine", INIT_SIZE, expect, csv);
|
||||
}
|
||||
bench_combine("combine", crc64_test_size, expect, csv);
|
||||
}
|
||||
init_this_loop = 0;
|
||||
/* step down by ~1.641 for a range of test sizes */
|
||||
crc64_test_size -= (crc64_test_size >> 2) + (crc64_test_size >> 3) + (crc64_test_size >> 6);
|
||||
} while (crc64_test_size > 3);
|
||||
if (loop) goto again;
|
||||
return 0;
|
||||
}
|
31
src/unit/test_files.h
Normal file
31
src/unit/test_files.h
Normal file
@ -0,0 +1,31 @@
|
||||
/* Do not modify this file, it's automatically generated from utils/generate-unit-test-header.py */
|
||||
typedef int unitTestProc(int argc, char **argv, int flags);
|
||||
|
||||
typedef struct unitTest {
|
||||
char *name;
|
||||
unitTestProc *proc;
|
||||
} unitTest;
|
||||
|
||||
int test_crc64(int argc, char **argv, int flags);
|
||||
int test_crc64combine(int argc, char **argv, int flags);
|
||||
int test_intsetValueEncodings(int argc, char **argv, int flags);
|
||||
int test_intsetBasicAdding(int argc, char **argv, int flags);
|
||||
int test_intsetLargeNumberRandomAdd(int argc, char **argv, int flags);
|
||||
int test_intsetUpgradeFromint16Toint32(int argc, char **argv, int flags);
|
||||
int test_intsetUpgradeFromint16Toint64(int argc, char **argv, int flags);
|
||||
int test_intsetUpgradeFromint32Toint64(int argc, char **argv, int flags);
|
||||
int test_intsetStressLookups(int argc, char **argv, int flags);
|
||||
int test_intsetStressAddDelete(int argc, char **argv, int flags);
|
||||
|
||||
unitTest __test_crc64_c[] = {{"test_crc64", test_crc64}, {NULL, NULL}};
|
||||
unitTest __test_crc64combine_c[] = {{"test_crc64combine", test_crc64combine}, {NULL, NULL}};
|
||||
unitTest __test_intset_c[] = {{"test_intsetValueEncodings", test_intsetValueEncodings}, {"test_intsetBasicAdding", test_intsetBasicAdding}, {"test_intsetLargeNumberRandomAdd", test_intsetLargeNumberRandomAdd}, {"test_intsetUpgradeFromint16Toint32", test_intsetUpgradeFromint16Toint32}, {"test_intsetUpgradeFromint16Toint64", test_intsetUpgradeFromint16Toint64}, {"test_intsetUpgradeFromint32Toint64", test_intsetUpgradeFromint32Toint64}, {"test_intsetStressLookups", test_intsetStressLookups}, {"test_intsetStressAddDelete", test_intsetStressAddDelete}, {NULL, NULL}};
|
||||
|
||||
struct unitTestSuite {
|
||||
char *filename;
|
||||
unitTest *tests;
|
||||
} unitTestSuite[] = {
|
||||
{"test_crc64.c", __test_crc64_c},
|
||||
{"test_crc64combine.c", __test_crc64combine_c},
|
||||
{"test_intset.c", __test_intset_c},
|
||||
};
|
48
src/unit/test_help.h
Normal file
48
src/unit/test_help.h
Normal file
@ -0,0 +1,48 @@
|
||||
/* A very simple test framework for valkey. See unit/README.me for more information on usage.
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* int test_example(int argc, char *argv[], int flags) {
|
||||
* TEST_ASSERT_MESSAGE("Check if 1 == 1", 1==1);
|
||||
* TEST_ASSERT(5 == 5);
|
||||
* return 0;
|
||||
* }
|
||||
*/
|
||||
|
||||
#ifndef __TESTHELP_H
|
||||
#define __TESTHELP_H
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
|
||||
/* The flags are the following:
|
||||
* --accurate: Runs tests with more iterations.
|
||||
* --large-memory: Enables tests that consume more than 100mb.
|
||||
* --single: A flag to indicate a specific test file was executed. */
|
||||
#define UNIT_TEST_ACCURATE (1<<0)
|
||||
#define UNIT_TEST_LARGE_MEMORY (1<<1)
|
||||
#define UNIT_TEST_SINGLE (1<<2)
|
||||
|
||||
#define KRED "\33[31m"
|
||||
#define KGRN "\33[32m"
|
||||
#define KBLUE "\33[34m"
|
||||
#define KRESET "\33[0m"
|
||||
|
||||
#define TEST_PRINT_ERROR(descr) \
|
||||
printf("[" KRED "%s - %s:%d" KRESET "] %s\n", __func__, __FILE__, __LINE__, descr)
|
||||
|
||||
#define TEST_PRINT_INFO(descr, ...) \
|
||||
printf("[" KBLUE "%s - %s:%d" KRESET "] " descr "\n", __func__, __FILE__, __LINE__, __VA_ARGS__)
|
||||
|
||||
#define TEST_ASSERT_MESSAGE(descr, _c) do { \
|
||||
if (!(_c)) { \
|
||||
TEST_PRINT_ERROR(descr); \
|
||||
return 1; \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
#define TEST_ASSERT(_c) TEST_ASSERT_MESSAGE("Failed assertion: " #_c, _c)
|
||||
|
||||
#define UNUSED(x) (void)(x)
|
||||
|
||||
#endif
|
229
src/unit/test_intset.c
Normal file
229
src/unit/test_intset.c
Normal file
@ -0,0 +1,229 @@
|
||||
#include <sys/time.h>
|
||||
#include <time.h>
|
||||
|
||||
#include "../intset.c"
|
||||
#include "test_help.h"
|
||||
|
||||
static long long usec(void) {
|
||||
struct timeval tv;
|
||||
gettimeofday(&tv,NULL);
|
||||
return (((long long)tv.tv_sec)*1000000)+tv.tv_usec;
|
||||
}
|
||||
|
||||
static intset *createSet(int bits, int size) {
|
||||
uint64_t mask = (1<<bits)-1;
|
||||
uint64_t value;
|
||||
intset *is = intsetNew();
|
||||
|
||||
for (int i = 0; i < size; i++) {
|
||||
if (bits > 32) {
|
||||
value = (rand()*rand()) & mask;
|
||||
} else {
|
||||
value = rand() & mask;
|
||||
}
|
||||
is = intsetAdd(is,value,NULL);
|
||||
}
|
||||
return is;
|
||||
}
|
||||
|
||||
static int checkConsistency(intset *is) {
|
||||
for (uint32_t i = 0; i < (intrev32ifbe(is->length)-1); i++) {
|
||||
uint32_t encoding = intrev32ifbe(is->encoding);
|
||||
|
||||
if (encoding == INTSET_ENC_INT16) {
|
||||
int16_t *i16 = (int16_t*)is->contents;
|
||||
TEST_ASSERT(i16[i] < i16[i+1]);
|
||||
} else if (encoding == INTSET_ENC_INT32) {
|
||||
int32_t *i32 = (int32_t*)is->contents;
|
||||
TEST_ASSERT(i32[i] < i32[i+1]);
|
||||
} else {
|
||||
int64_t *i64 = (int64_t*)is->contents;
|
||||
TEST_ASSERT(i64[i] < i64[i+1]);
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
int test_intsetValueEncodings(int argc, char **argv, int flags) {
|
||||
UNUSED(argc);
|
||||
UNUSED(argv);
|
||||
UNUSED(flags);
|
||||
|
||||
TEST_ASSERT(_intsetValueEncoding(-32768) == INTSET_ENC_INT16);
|
||||
TEST_ASSERT(_intsetValueEncoding(+32767) == INTSET_ENC_INT16);
|
||||
TEST_ASSERT(_intsetValueEncoding(-32769) == INTSET_ENC_INT32);
|
||||
TEST_ASSERT(_intsetValueEncoding(+32768) == INTSET_ENC_INT32);
|
||||
TEST_ASSERT(_intsetValueEncoding(-2147483648) == INTSET_ENC_INT32);
|
||||
TEST_ASSERT(_intsetValueEncoding(+2147483647) == INTSET_ENC_INT32);
|
||||
TEST_ASSERT(_intsetValueEncoding(-2147483649) == INTSET_ENC_INT64);
|
||||
TEST_ASSERT(_intsetValueEncoding(+2147483648) == INTSET_ENC_INT64);
|
||||
TEST_ASSERT(_intsetValueEncoding(-9223372036854775808ull) ==
|
||||
INTSET_ENC_INT64);
|
||||
TEST_ASSERT(_intsetValueEncoding(+9223372036854775807ull) ==
|
||||
INTSET_ENC_INT64);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int test_intsetBasicAdding(int argc, char **argv, int flags) {
|
||||
UNUSED(argc);
|
||||
UNUSED(argv);
|
||||
UNUSED(flags);
|
||||
|
||||
intset *is = intsetNew();
|
||||
uint8_t success;
|
||||
is = intsetAdd(is,5,&success); TEST_ASSERT(success);
|
||||
is = intsetAdd(is,6,&success); TEST_ASSERT(success);
|
||||
is = intsetAdd(is,4,&success); TEST_ASSERT(success);
|
||||
is = intsetAdd(is,4,&success); TEST_ASSERT(!success);
|
||||
TEST_ASSERT(6 == intsetMax(is));
|
||||
TEST_ASSERT(4 == intsetMin(is));
|
||||
zfree(is);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int test_intsetLargeNumberRandomAdd(int argc, char **argv, int flags) {
|
||||
UNUSED(argc);
|
||||
UNUSED(argv);
|
||||
UNUSED(flags);
|
||||
|
||||
uint32_t inserts = 0;
|
||||
uint8_t success;
|
||||
intset *is = intsetNew();
|
||||
for (int i = 0; i < 1024; i++) {
|
||||
is = intsetAdd(is,rand()%0x800,&success);
|
||||
if (success) inserts++;
|
||||
}
|
||||
TEST_ASSERT(intrev32ifbe(is->length) == inserts);
|
||||
TEST_ASSERT(checkConsistency(is) == 1);
|
||||
zfree(is);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int test_intsetUpgradeFromint16Toint32(int argc, char **argv, int flags) {
|
||||
UNUSED(argc);
|
||||
UNUSED(argv);
|
||||
UNUSED(flags);
|
||||
|
||||
intset *is = intsetNew();
|
||||
is = intsetAdd(is,32,NULL);
|
||||
TEST_ASSERT(intrev32ifbe(is->encoding) == INTSET_ENC_INT16);
|
||||
is = intsetAdd(is,65535,NULL);
|
||||
TEST_ASSERT(intrev32ifbe(is->encoding) == INTSET_ENC_INT32);
|
||||
TEST_ASSERT(intsetFind(is,32));
|
||||
TEST_ASSERT(intsetFind(is,65535));
|
||||
TEST_ASSERT(checkConsistency(is) == 1);
|
||||
zfree(is);
|
||||
|
||||
is = intsetNew();
|
||||
is = intsetAdd(is,32,NULL);
|
||||
TEST_ASSERT(intrev32ifbe(is->encoding) == INTSET_ENC_INT16);
|
||||
is = intsetAdd(is,-65535,NULL);
|
||||
TEST_ASSERT(intrev32ifbe(is->encoding) == INTSET_ENC_INT32);
|
||||
TEST_ASSERT(intsetFind(is,32));
|
||||
TEST_ASSERT(intsetFind(is,-65535));
|
||||
TEST_ASSERT(checkConsistency(is) == 1);
|
||||
zfree(is);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int test_intsetUpgradeFromint16Toint64(int argc, char **argv, int flags) {
|
||||
UNUSED(argc);
|
||||
UNUSED(argv);
|
||||
UNUSED(flags);
|
||||
|
||||
intset *is = intsetNew();
|
||||
is = intsetNew();
|
||||
is = intsetAdd(is,32,NULL);
|
||||
TEST_ASSERT(intrev32ifbe(is->encoding) == INTSET_ENC_INT16);
|
||||
is = intsetAdd(is,4294967295,NULL);
|
||||
TEST_ASSERT(intrev32ifbe(is->encoding) == INTSET_ENC_INT64);
|
||||
TEST_ASSERT(intsetFind(is,32));
|
||||
TEST_ASSERT(intsetFind(is,4294967295));
|
||||
TEST_ASSERT(checkConsistency(is) == 1);
|
||||
zfree(is);
|
||||
|
||||
is = intsetNew();
|
||||
is = intsetAdd(is,32,NULL);
|
||||
TEST_ASSERT(intrev32ifbe(is->encoding) == INTSET_ENC_INT16);
|
||||
is = intsetAdd(is,-4294967295,NULL);
|
||||
TEST_ASSERT(intrev32ifbe(is->encoding) == INTSET_ENC_INT64);
|
||||
TEST_ASSERT(intsetFind(is,32));
|
||||
TEST_ASSERT(intsetFind(is,-4294967295));
|
||||
TEST_ASSERT(checkConsistency(is) == 1);
|
||||
zfree(is);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int test_intsetUpgradeFromint32Toint64(int argc, char **argv, int flags) {
|
||||
UNUSED(argc);
|
||||
UNUSED(argv);
|
||||
UNUSED(flags);
|
||||
|
||||
intset *is = intsetNew();
|
||||
is = intsetAdd(is,65535,NULL);
|
||||
TEST_ASSERT(intrev32ifbe(is->encoding) == INTSET_ENC_INT32);
|
||||
is = intsetAdd(is,4294967295,NULL);
|
||||
TEST_ASSERT(intrev32ifbe(is->encoding) == INTSET_ENC_INT64);
|
||||
TEST_ASSERT(intsetFind(is,65535));
|
||||
TEST_ASSERT(intsetFind(is,4294967295));
|
||||
TEST_ASSERT(checkConsistency(is) == 1);
|
||||
zfree(is);
|
||||
|
||||
is = intsetNew();
|
||||
is = intsetAdd(is,65535,NULL);
|
||||
TEST_ASSERT(intrev32ifbe(is->encoding) == INTSET_ENC_INT32);
|
||||
is = intsetAdd(is,-4294967295,NULL);
|
||||
TEST_ASSERT(intrev32ifbe(is->encoding) == INTSET_ENC_INT64);
|
||||
TEST_ASSERT(intsetFind(is,65535));
|
||||
TEST_ASSERT(intsetFind(is,-4294967295));
|
||||
TEST_ASSERT(checkConsistency(is) == 1);
|
||||
zfree(is);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int test_intsetStressLookups(int argc, char **argv, int flags) {
|
||||
UNUSED(argc);
|
||||
UNUSED(argv);
|
||||
UNUSED(flags);
|
||||
|
||||
long num = 100000, size = 10000;
|
||||
int i, bits = 20;
|
||||
long long start;
|
||||
intset *is = createSet(bits,size);
|
||||
TEST_ASSERT(checkConsistency(is) == 1);
|
||||
|
||||
start = usec();
|
||||
for (i = 0; i < num; i++) intsetSearch(is,rand() % ((1<<bits)-1),NULL);
|
||||
TEST_PRINT_INFO("%ld lookups, %ld element set, %lldusec\n",
|
||||
num,size,usec()-start);
|
||||
zfree(is);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int test_intsetStressAddDelete(int argc, char **argv, int flags) {
|
||||
UNUSED(argc);
|
||||
UNUSED(argv);
|
||||
UNUSED(flags);
|
||||
|
||||
int i, v1, v2;
|
||||
intset *is = intsetNew();
|
||||
for (i = 0; i < 0xffff; i++) {
|
||||
v1 = rand() % 0xfff;
|
||||
is = intsetAdd(is,v1,NULL);
|
||||
TEST_ASSERT(intsetFind(is,v1));
|
||||
|
||||
v2 = rand() % 0xfff;
|
||||
is = intsetRemove(is,v2,NULL);
|
||||
TEST_ASSERT(!intsetFind(is,v2));
|
||||
}
|
||||
TEST_ASSERT(checkConsistency(is) == 1);
|
||||
zfree(is);
|
||||
|
||||
return 0;
|
||||
}
|
67
src/unit/test_main.c
Normal file
67
src/unit/test_main.c
Normal file
@ -0,0 +1,67 @@
|
||||
/*
|
||||
* Copyright Valkey contributors.
|
||||
* All rights reserved.
|
||||
* SPDX-License-Identifier: BSD 3-Clause
|
||||
*/
|
||||
|
||||
#include <strings.h>
|
||||
#include <stdio.h>
|
||||
#include "test_files.h"
|
||||
#include "test_help.h"
|
||||
|
||||
/* We override the default assertion mechanism, so that it prints out info and then dies. */
|
||||
void _serverAssert(const char *estr, const char *file, int line) {
|
||||
printf("[" KRED "serverAssert - %s:%d" KRESET "] - %s\n", file, line, estr);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/* Run the tests defined by the test suite. */
|
||||
int runTestSuite(struct unitTestSuite *test, int argc, char **argv, int flags) {
|
||||
int test_num = 0;
|
||||
int failed_tests = 0;
|
||||
printf("[" KBLUE "START" KRESET "] - %s\n", test->filename);
|
||||
|
||||
for (int id = 0; test->tests[id].proc != NULL; id++) {
|
||||
test_num++;
|
||||
int test_result = (test->tests[id].proc(argc, argv, flags) != 0);
|
||||
if (!test_result) {
|
||||
printf("[" KGRN "ok" KRESET "] - %s:%s\n", test->filename, test->tests[id].name);
|
||||
} else {
|
||||
printf("[" KRED "fail" KRESET "] - %s:%s\n", test->filename, test->tests[id].name);
|
||||
failed_tests++;
|
||||
}
|
||||
}
|
||||
|
||||
printf("[" KBLUE "END" KRESET "] - %s: ", test->filename);
|
||||
printf("%d tests, %d passed, %d failed\n", test_num,
|
||||
test_num - failed_tests, failed_tests);
|
||||
return !failed_tests;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
int flags = 0;
|
||||
char *file = NULL;
|
||||
for (int j = 1; j < argc; j++) {
|
||||
char *arg = argv[j];
|
||||
if (!strcasecmp(arg, "--accurate")) flags |= UNIT_TEST_ACCURATE;
|
||||
else if (!strcasecmp(arg, "--large-memory")) flags |= UNIT_TEST_LARGE_MEMORY;
|
||||
else if (!strcasecmp(arg, "--single") && (j + 1 < argc)) {
|
||||
flags |= UNIT_TEST_SINGLE;
|
||||
file = argv[j + 1];
|
||||
}
|
||||
}
|
||||
|
||||
int numtests = sizeof(unitTestSuite)/sizeof(struct unitTest);
|
||||
int failed_num = 0, suites_executed = 0;
|
||||
for (int j = 0; j < numtests; j++) {
|
||||
if (file && strcasecmp(file, unitTestSuite[j].filename)) continue;
|
||||
if (!runTestSuite(&unitTestSuite[j], argc, argv, flags)) {
|
||||
failed_num++;
|
||||
}
|
||||
suites_executed++;
|
||||
}
|
||||
printf("%d test suites executed, %d passed, %d failed\n", suites_executed,
|
||||
suites_executed-failed_num, failed_num);
|
||||
|
||||
return failed_num == 0 ? 0 : 1;
|
||||
}
|
59
utils/generate-unit-test-header.py
Executable file
59
utils/generate-unit-test-header.py
Executable file
@ -0,0 +1,59 @@
|
||||
#!/usr/bin/env python3
|
||||
import os
|
||||
import re
|
||||
|
||||
UNIT_DIR = os.path.abspath(os.path.dirname(os.path.abspath(__file__)) + '/../src/unit')
|
||||
TEST_FILE = UNIT_DIR + '/test_files.h'
|
||||
TEST_PROTOTYPE = '(int (test_[a-zA-Z0-9_]*)\(.*\)).*{'
|
||||
|
||||
if __name__ == '__main__':
|
||||
with open(TEST_FILE, 'w') as output:
|
||||
# Find each test file and collect the test names.
|
||||
test_suites = []
|
||||
for root, dirs, files in os.walk(UNIT_DIR):
|
||||
for file in files:
|
||||
file_path = UNIT_DIR + '/' + file
|
||||
if not file.endswith('.c') or file == 'test_main.c':
|
||||
continue
|
||||
tests = []
|
||||
with open(file_path, 'r') as f:
|
||||
for line in f:
|
||||
match = re.match(TEST_PROTOTYPE, line)
|
||||
if match:
|
||||
function = match.group(1)
|
||||
test_name = match.group(2)
|
||||
tests.append((test_name, function))
|
||||
test_suites.append({'file': file, 'tests': tests})
|
||||
test_suites.sort(key=lambda test_suite: test_suite['file'])
|
||||
output.write("""/* Do not modify this file, it's automatically generated from utils/generate-unit-test-header.py */
|
||||
typedef int unitTestProc(int argc, char **argv, int flags);
|
||||
|
||||
typedef struct unitTest {
|
||||
char *name;
|
||||
unitTestProc *proc;
|
||||
} unitTest;
|
||||
|
||||
""")
|
||||
|
||||
# Write the headers for the functions
|
||||
for test_suite in test_suites:
|
||||
for test in test_suite['tests']:
|
||||
output.write('{};\n'.format(test[1]))
|
||||
output.write("\n")
|
||||
|
||||
# Create test suite lists
|
||||
for test_suite in test_suites:
|
||||
output.write('unitTest __{}[] = {{'.format(test_suite['file'].replace('.c', '_c')))
|
||||
for test in test_suite['tests']:
|
||||
output.write('{{"{}", {}}}, '.format(test[0], test[0]))
|
||||
output.write('{NULL, NULL}};\n')
|
||||
|
||||
output.write("""
|
||||
struct unitTestSuite {
|
||||
char *filename;
|
||||
unitTest *tests;
|
||||
} unitTestSuite[] = {
|
||||
""")
|
||||
for test_suite in test_suites:
|
||||
output.write(' {{"{0}", __{1}}},\n'.format(test_suite['file'], test_suite['file'].replace('.c', '_c')))
|
||||
output.write('};\n')
|
Loading…
Reference in New Issue
Block a user