diff --git a/.travis.yml b/.travis.yml index 7a47c6d..13ec3b0 100644 --- a/.travis.yml +++ b/.travis.yml @@ -17,6 +17,6 @@ script: - export CC=gcc-9 - gcc --version - python --version - - make + - python autogen.py -e -b -cv -r after_success: - bash <(curl -s https://codecov.io/bash) diff --git a/autogen.py b/autogen.py index 833a151..b5b2644 100644 --- a/autogen.py +++ b/autogen.py @@ -3,6 +3,7 @@ import argparse import os +import pathlib import re import shutil import subprocess @@ -39,7 +40,7 @@ INCLUDE_DIR = f"{OUTPUT_DIR}/{DIR_MAP['HEADER']}" # const SRC_DIR = f"{OUTPUT_DIR}/{DIR_MAP['SOURCE']}" # Where object files .o go -OBJ_DIR = f'{BUILD_DIR}/obj' +OBJ_DIR = BUILD_DIR # The name of the main test executable TEST_EXE = f'{OUTPUT_DIR}/main.exe' @@ -53,10 +54,10 @@ CFLAGS = ['-Wall', '-Werror'] CVFLAGS = ['--coverage', '-O0'] # Include to the main Library # const -INCLUDE = '-I ./src' +INCLUDE = ['-I', './src'] # Include to the test file headers # const -TINCLUDE = f'-I {INCLUDE_DIR}' +TINCLUDE = ['-I', f'{INCLUDE_DIR}'] # Macro expansion @@ -115,25 +116,36 @@ COLLECTIONS = [ ] +def full_path(path: Text) -> Text: + ''' + Returns the full path of a 'path' with ralative paths, e.g. './' or '../' + ''' + return str(pathlib.Path(path).resolve()) + + def execute_command(commands: List[Text]): print(' '.join(commands)) - subprocess.run(commands, shell=True) + cp = subprocess.run(commands, shell=True) + + # if cp.returncode != 0: + # print('Error when executing the last command') + # exit(1) def assert_executable(program: Text): ''' - Asserts that a given program is installed and available in PATH + Asserts that a given program is installed and available in PATH. ''' if shutil.which(program) is None: raise Exception(f'The required executable {program} was not found in your path') -def assert_dir(dir: Text): +def assert_dir(directory: Text): ''' - Given a string 'dir' that is a path to a directory, make sure that it exists. + Given a string 'directory' that is a path to a directory, make sure that it exists. ''' - if not os.path.exists(dir): - os.makedirs(dir) + if not os.path.exists(directory): + os.makedirs(directory) def assert_structure(): @@ -143,13 +155,13 @@ def assert_structure(): assert_dir(OUTPUT_DIR) # include and src - for dir in DIR_MAP.values(): - assert_dir(f'{OUTPUT_DIR}/{dir}') + for directory in DIR_MAP.values(): + assert_dir(f'{OUTPUT_DIR}/{directory}') # For each library, create a directory for include and src for lib in ALL_LIBS: - for dir in DIR_MAP.values(): - assert_dir(f'{OUTPUT_DIR}/{dir}/{lib.lower()}') + for directory in DIR_MAP.values(): + assert_dir(f'{OUTPUT_DIR}/{directory}/{lib.lower()}') assert_dir(BUILD_DIR) assert_dir(OBJ_DIR) @@ -158,12 +170,21 @@ def assert_structure(): assert_dir(f'{OBJ_DIR}/{lib.lower()}') -def clean_build(): - execute_command(['find', BUILD_DIR, '-type', 'f', '-delete']) - execute_command(['find', '.', '-iname', '*.gcov', '-type', 'f', '-delete']) +def clean_build(cleanfull: bool): + ''' + Removes all files generated by compilation and code coverage along with folders. + ''' + execute_command(['rm', '-r', '*.gcov']) + execute_command(['rm', TEST_EXE]) + + for directory in [BUILD_DIR, SRC_DIR, INCLUDE_DIR]: + execute_command(['rm', '-r', directory]) def expand_code(): + ''' + Expands code to be used in tests. This is necessary because code coverage doesn't work with macros. + ''' # Generate Header and Source separately for ftype in ['HEADER', 'SOURCE']: for data in COLLECTIONS: @@ -183,7 +204,7 @@ def expand_code(): file.flush() file.close() - result = subprocess.getoutput(f'{CC} {EXPAND_FLAGS} {INCLUDE} {TMP_FILE}') + result = subprocess.getoutput(f'{CC} {EXPAND_FLAGS} {" ".join(INCLUDE)} {TMP_FILE}') # (?s) makes '.' match anything, even '\n' match = re.search(fr'(?s){UNIQUE_FLAG}(?P.+){UNIQUE_FLAG}', result) @@ -223,6 +244,9 @@ def expand_code(): def format_expand(): + ''' + Format the files outputted by 'expand_code'. + ''' cmd = ['clang-format', '--style=file', '--verbose', '-i'] dirs_to_format = [f'{OUTPUT_DIR}/{DIR_MAP[ftype]}/{lib.lower()}/*.{EXT_MAP[ftype]}' for ftype in DIR_MAP.keys() for lib in ALL_LIBS] cmd.extend(dirs_to_format) @@ -230,79 +254,63 @@ def format_expand(): def format_code(source: bool, tests: bool): + ''' + Format all source files from the main library or test files. + ''' if source: - execute_command(['find', './src/', '-iname', '*.h', '|', 'xargs', 'clang-format', '--style=file', '--verbose', '-i']) + execute_command(['find', './src/', '-name', '*.h', '|', 'xargs', 'clang-format', '--style=file', '--verbose', '-i']) if tests: - execute_command(['find', './tests/', '-iname', '*.c', '-o', '-iname', '*.h', '|', 'xargs', 'clang-format', '--style=file', '--verbose', '-i']) + execute_command(['find', './tests/', '-name', '*.c', '-o', '-name', '*.h', '|', 'xargs', 'clang-format', '--style=file', '--verbose', '-i']) def build_tests(with_coverage: bool): + ''' + Build all object files and the main executable for tests + ''' cflags = CFLAGS.copy() if with_coverage: cflags += CVFLAGS - - print("WIP") - - -def run_tests(): - execute_command([TEST_EXE]) - - -def unused(): - # Makefile - # Contains many usefull things dealing with tests, code coverage and formatting - - print('Updating Makefile...') - - file = open(f'Makefile', 'w') - - file.write(f'''# This file was generated by a Python script - CC = {CC} - CFLAGS = -std=c11 -Wall -Wextra - CFLAGS += -Wno-missing-braces -DCMC_TEST_COLOR - CVFLAGS = --coverage -O0 -g3 - INCLUDE = {INCLUDE_DIR} -I src - - # Runs the script to update tests, run all tests and code coverage - all: script coverage - - tests: script build - \t{OUTPUT_DIR}/main.exe - - # Like build but with coverage - coverage: {' '.join([f'{OBJ_DIR}/{data["LIB"].lower()}/{data["SNAME"]}.o' for data in COLLECTIONS])} {OBJ_DIR}/main.o - \t$(CC) $^ -o {OUTPUT_DIR}/main.exe $(CFLAGS) $(CVFLAGS) - \t{OUTPUT_DIR}/main.exe - \tgcov {OBJ_DIR}/main.o {' '.join([f'{OBJ_DIR}/{data["LIB"].lower()}/{data["SNAME"]}.o' for data in COLLECTIONS])} - - # Run without coverage - run: build - \t{OUTPUT_DIR}/main.exe - - script: - \tpython autogen.py - - clean: - \tfind {BUILD_DIR} -type f -delete - \trm *.gcov - - # No coverage build - build: {' '.join([f'{OBJ_DIR}/{data["LIB"].lower()}/{data["SNAME"]}.o' for data in COLLECTIONS])} {OBJ_DIR}/main.o - \t$(CC) $^ -o {OUTPUT_DIR}/main.exe $(CFLAGS) - - {OBJ_DIR}/main.o: {OUTPUT_DIR}/main.c - \t$(CC) -c $^ -o $@ -I $(INCLUDE) $(CFLAGS) - ''') + else: + cflags += ['-O3'] for data in COLLECTIONS: - file.write(f''' - {OBJ_DIR}/{data['LIB'].lower()}/{data['SNAME']}.o: {SRC_DIR}/{data['LIB'].lower()}/{data['SNAME']}.c {INCLUDE_DIR}/{data['LIB'].lower()}/{data['SNAME']}.h - \t$(CC) -c $< -o $@ -I $(INCLUDE) $(CFLAGS) $(CVFLAGS) - ''') + cmd = [CC] + cmd += cflags + cmd += TINCLUDE # This one needs to go first + cmd += INCLUDE + cmd += ['-c', f"{SRC_DIR}/{data['LIB'].lower()}/{data['SNAME']}.c"] + cmd += ['-o', f"{OBJ_DIR}/{data['LIB'].lower()}/{data['SNAME']}.o"] + execute_command(cmd) - file.close() - print('\nDone') + # Build OUTPUT_DIR/main.c + cmd = [CC] + cmd += cflags + cmd += TINCLUDE # This one needs to go first + cmd += INCLUDE + cmd += ['-c', f'{OUTPUT_DIR}/main.c'] + cmd += ['-o', f"{OBJ_DIR}/main.o"] + execute_command(cmd) + + # Link everything producing OUTPUT_DIR/main.exe + cmd = [CC] + cmd += cflags + cmd += ['-o', TEST_EXE] + cmd += [f'{OBJ_DIR}/main.o'] + cmd += [f'{OBJ_DIR}/{data["LIB"].lower()}/{data["SNAME"]}.o' for data in COLLECTIONS] + execute_command(cmd) + + +def run_tests(coverage: bool): + ''' + Runs tests. If coverage, run gcov. + ''' + execute_command([full_path(TEST_EXE)]) + + if coverage: + objects = [f'{OBJ_DIR}/main.o'] + objects += [f'{OBJ_DIR}/{data["LIB"].lower()}/{data["SNAME"]}.o' for data in COLLECTIONS] + execute_command(['gcov'] + objects) if __name__ == '__main__': @@ -320,6 +328,11 @@ if __name__ == '__main__': default=False, action='store_true', dest='clean') + tests.add_argument('-cf', '--clean-full', + help='Cleans all build objects, source and header files, and removes folders', + default=False, + action='store_true', + dest='cleanfull') tests.add_argument('-e', '--expand', help='Expands the header and source files', default=False, @@ -330,18 +343,23 @@ if __name__ == '__main__': default=False, action='store_true', dest='noformat') + tests.add_argument('-b', '--build', + help='Compile the object files and the main executable', + default=False, + action='store_true', + dest='build') tests.add_argument('-r', '--run', - help='Runs tests without code coverage', + help='Runs tests, assuming that it is already built', default=False, action='store_true', dest='run') tests.add_argument('-cv', '--coverage', - help='Compiles and runs tests with code coverage', + help='Adds the code coverage flags for compilation', default=False, action='store_true', dest='coverage') tests.add_argument('-rr', '--rerun', - help='Builds tests without code coverage and runs it', + help='Compile the object files again and then run the tests', default=False, action='store_true', dest='rerun') @@ -365,11 +383,12 @@ if __name__ == '__main__': print(CONFIG) exit(0) - if args.clean: - clean_build() + if args.clean or args.cleanfull: + clean_build(args.cleanfull) - assert_structure() - assert_executable(CC) + if not args.cleanfull: + assert_structure() + assert_executable(CC) if args.expand: expand_code() @@ -382,11 +401,12 @@ if __name__ == '__main__': assert_executable('xargs') format_code(args.fmt_source, args.fmt_tests) - if args.rerun: - build_tests(False) - run_tests() + if args.build: + build_tests(args.coverage) + if args.run: + run_tests(args.coverage) + elif args.rerun: + build_tests(args.coverage) + run_tests(args.coverage) elif args.run: - run_tests() - elif args.coverage: - build_tests(True) - run_tests() + run_tests(args.coverage) diff --git a/src/cmc/hashmap.h b/src/cmc/hashmap.h index 54ca72c..0406261 100644 --- a/src/cmc/hashmap.h +++ b/src/cmc/hashmap.h @@ -461,8 +461,8 @@ return false; \ } \ \ - K max_key; \ - V max_val; \ + K max_key = (K) { 0 }; \ + V max_val = (V) { 0 }; \ size_t first = 0; \ \ for (; first < _map_->capacity; first++) \ @@ -505,8 +505,8 @@ return false; \ } \ \ - K min_key; \ - V min_val; \ + K min_key = (K) { 0 }; \ + V min_val = (V) { 0 }; \ size_t first = 0; \ \ for (; first < _map_->capacity; first++) \ diff --git a/src/cmc/hashmultimap.h b/src/cmc/hashmultimap.h index 51a700a..053f55f 100644 --- a/src/cmc/hashmultimap.h +++ b/src/cmc/hashmultimap.h @@ -684,8 +684,8 @@ return false; \ } \ \ - K max_key; \ - V max_val; \ + K max_key = (K) { 0 }; \ + V max_val = (V) { 0 }; \ \ struct CMC_DEF_ITER(SNAME) iter = CMC_(PFX, _iter_start)(_map_); \ \ @@ -728,8 +728,8 @@ return false; \ } \ \ - K min_key; \ - V min_val; \ + K min_key = (K) { 0 }; \ + V min_val = (V) { 0 }; \ \ struct CMC_DEF_ITER(SNAME) iter = CMC_(PFX, _iter_start)(_map_); \ \ diff --git a/src/cmc/hashmultiset.h b/src/cmc/hashmultiset.h index f94ac96..be14e5f 100644 --- a/src/cmc/hashmultiset.h +++ b/src/cmc/hashmultiset.h @@ -477,7 +477,7 @@ return false; \ } \ \ - V max_val; \ + V max_val = (V) { 0 }; \ struct CMC_DEF_ITER(SNAME) iter = CMC_(PFX, _iter_start)(_set_); \ \ /* TODO transform this into a normal loop */ \ @@ -510,7 +510,7 @@ return false; \ } \ \ - V min_val; \ + V min_val = (V) { 0 }; \ struct CMC_DEF_ITER(SNAME) iter = CMC_(PFX, _iter_start)(_set_); \ \ /* TODO transform this into a normal loop */ \ diff --git a/src/cmc/hashset.h b/src/cmc/hashset.h index 04b2c2e..47bdf4f 100644 --- a/src/cmc/hashset.h +++ b/src/cmc/hashset.h @@ -378,7 +378,7 @@ return false; \ } \ \ - V max_value; \ + V max_val = (V) { 0 }; \ struct CMC_DEF_ITER(SNAME) iter = CMC_(PFX, _iter_start)(_set_); \ \ /* TODO turn this into a normal loop */ \ @@ -388,13 +388,13 @@ size_t index = CMC_(PFX, _iter_index)(&iter); \ \ if (index == 0) \ - max_value = result; \ - else if (_set_->f_val->cmp(result, max_value) > 0) \ - max_value = result; \ + max_val = result; \ + else if (_set_->f_val->cmp(result, max_val) > 0) \ + max_val = result; \ } \ \ if (value) \ - *value = max_value; \ + *value = max_val; \ \ _set_->flag = CMC_FLAG_OK; \ \ @@ -411,7 +411,7 @@ return false; \ } \ \ - V min_value; \ + V min_val = (V) { 0 }; \ struct CMC_DEF_ITER(SNAME) iter = CMC_(PFX, _iter_start)(_set_); \ \ /* TODO turn this into a normal loop */ \ @@ -421,13 +421,13 @@ size_t index = CMC_(PFX, _iter_index)(&iter); \ \ if (index == 0) \ - min_value = result; \ - else if (_set_->f_val->cmp(result, min_value) < 0) \ - min_value = result; \ + min_val = result; \ + else if (_set_->f_val->cmp(result, min_val) < 0) \ + min_val = result; \ } \ \ if (value) \ - *value = min_value; \ + *value = min_val; \ \ _set_->flag = CMC_FLAG_OK; \ \ diff --git a/tests/include/cmc/queue.h b/tests/include/cmc/queue.h index 33b4904..1fcd7b1 100644 --- a/tests/include/cmc/queue.h +++ b/tests/include/cmc/queue.h @@ -25,14 +25,6 @@ struct queue_fval size_t (*hash)(size_t); int (*pri)(size_t, size_t); }; -struct queue_iter -{ - struct queue *target; - size_t cursor; - size_t index; - _Bool start; - _Bool end; -}; struct queue *q_new(size_t capacity, struct queue_fval *f_val); struct queue *q_new_custom(size_t capacity, struct queue_fval *f_val, struct cmc_alloc_node *alloc, struct cmc_callbacks *callbacks); @@ -51,6 +43,14 @@ int q_flag(struct queue *_queue_); _Bool q_resize(struct queue *_queue_, size_t capacity); struct queue *q_copy_of(struct queue *_queue_); _Bool q_equals(struct queue *_queue1_, struct queue *_queue2_); +struct queue_iter +{ + struct queue *target; + size_t cursor; + size_t index; + _Bool start; + _Bool end; +}; struct queue_iter q_iter_start(struct queue *target); struct queue_iter q_iter_end(struct queue *target); _Bool q_iter_at_start(struct queue_iter *iter); diff --git a/tests/src/cmc/hashmap.c b/tests/src/cmc/hashmap.c index 7fb16b5..20f99e5 100644 --- a/tests/src/cmc/hashmap.c +++ b/tests/src/cmc/hashmap.c @@ -201,8 +201,8 @@ _Bool hm_max(struct hashmap *_map_, size_t *key, size_t *value) _map_->flag = CMC_FLAG_EMPTY; return 0; } - size_t max_key; - size_t max_val; + size_t max_key = (size_t){ 0 }; + size_t max_val = (size_t){ 0 }; size_t first = 0; for (; first < _map_->capacity; first++) { @@ -240,8 +240,8 @@ _Bool hm_min(struct hashmap *_map_, size_t *key, size_t *value) _map_->flag = CMC_FLAG_EMPTY; return 0; } - size_t min_key; - size_t min_val; + size_t min_key = (size_t){ 0 }; + size_t min_val = (size_t){ 0 }; size_t first = 0; for (; first < _map_->capacity; first++) { diff --git a/tests/src/cmc/hashmultimap.c b/tests/src/cmc/hashmultimap.c index 45ef628..41d0817 100644 --- a/tests/src/cmc/hashmultimap.c +++ b/tests/src/cmc/hashmultimap.c @@ -353,8 +353,8 @@ _Bool hmm_max(struct hashmultimap *_map_, size_t *key, size_t *value) _map_->flag = CMC_FLAG_EMPTY; return 0; } - size_t max_key; - size_t max_val; + size_t max_key = (size_t){ 0 }; + size_t max_val = (size_t){ 0 }; struct hashmultimap_iter iter = hmm_iter_start(_map_); for (; !hmm_iter_at_end(&iter); hmm_iter_next(&iter)) { @@ -389,8 +389,8 @@ _Bool hmm_min(struct hashmultimap *_map_, size_t *key, size_t *value) _map_->flag = CMC_FLAG_EMPTY; return 0; } - size_t min_key; - size_t min_val; + size_t min_key = (size_t){ 0 }; + size_t min_val = (size_t){ 0 }; struct hashmultimap_iter iter = hmm_iter_start(_map_); for (; !hmm_iter_at_end(&iter); hmm_iter_next(&iter)) { diff --git a/tests/src/cmc/hashmultiset.c b/tests/src/cmc/hashmultiset.c index e348d67..6aa25a2 100644 --- a/tests/src/cmc/hashmultiset.c +++ b/tests/src/cmc/hashmultiset.c @@ -218,7 +218,7 @@ _Bool hms_max(struct hashmultiset *_set_, size_t *value) _set_->flag = CMC_FLAG_EMPTY; return 0; } - size_t max_val; + size_t max_val = (size_t){ 0 }; struct hashmultiset_iter iter = hms_iter_start(_set_); for (; !hms_iter_at_end(&iter); hms_iter_next(&iter)) { @@ -244,7 +244,7 @@ _Bool hms_min(struct hashmultiset *_set_, size_t *value) _set_->flag = CMC_FLAG_EMPTY; return 0; } - size_t min_val; + size_t min_val = (size_t){ 0 }; struct hashmultiset_iter iter = hms_iter_start(_set_); for (; !hms_iter_at_end(&iter); hms_iter_next(&iter)) { diff --git a/tests/src/cmc/hashset.c b/tests/src/cmc/hashset.c index de158d7..e0605f1 100644 --- a/tests/src/cmc/hashset.c +++ b/tests/src/cmc/hashset.c @@ -164,19 +164,19 @@ _Bool hs_max(struct hashset *_set_, size_t *value) _set_->flag = CMC_FLAG_EMPTY; return 0; } - size_t max_value; + size_t max_val = (size_t){ 0 }; struct hashset_iter iter = hs_iter_start(_set_); for (; !hs_iter_at_end(&iter); hs_iter_next(&iter)) { size_t result = hs_iter_value(&iter); size_t index = hs_iter_index(&iter); if (index == 0) - max_value = result; - else if (_set_->f_val->cmp(result, max_value) > 0) - max_value = result; + max_val = result; + else if (_set_->f_val->cmp(result, max_val) > 0) + max_val = result; } if (value) - *value = max_value; + *value = max_val; _set_->flag = CMC_FLAG_OK; if ((_set_)->callbacks && (_set_)->callbacks->read) (_set_)->callbacks->read(); @@ -190,19 +190,19 @@ _Bool hs_min(struct hashset *_set_, size_t *value) _set_->flag = CMC_FLAG_EMPTY; return 0; } - size_t min_value; + size_t min_val = (size_t){ 0 }; struct hashset_iter iter = hs_iter_start(_set_); for (; !hs_iter_at_end(&iter); hs_iter_next(&iter)) { size_t result = hs_iter_value(&iter); size_t index = hs_iter_index(&iter); if (index == 0) - min_value = result; - else if (_set_->f_val->cmp(result, min_value) < 0) - min_value = result; + min_val = result; + else if (_set_->f_val->cmp(result, min_val) < 0) + min_val = result; } if (value) - *value = min_value; + *value = min_val; _set_->flag = CMC_FLAG_OK; if ((_set_)->callbacks && (_set_)->callbacks->read) (_set_)->callbacks->read();