From 27d70cb32c4ee5560f065d66774cc10e9e993ca3 Mon Sep 17 00:00:00 2001 From: Leonardo Vencovsky Date: Tue, 30 Jun 2020 23:29:52 -0300 Subject: [PATCH] Trying to fix code coverage --- autogen.py | 280 +++++++++++++++++++++++++++++------------------- tests/codecov.c | 140 ++++++++++++++++++++++++ 2 files changed, 310 insertions(+), 110 deletions(-) create mode 100644 tests/codecov.c diff --git a/autogen.py b/autogen.py index bb4bec0..5275234 100644 --- a/autogen.py +++ b/autogen.py @@ -1,5 +1,4 @@ -# A script that automatically generates code for code coverage, updates tests -# and Makefiles, and creates necessary directories +# A script for automation import argparse import os @@ -12,11 +11,9 @@ from typing import List, Text # Config Script Configuration # Directories Names for directories -# Output Where the outputted code is to be put -# Build Where the build objects are to be located +# Files Names for files # Compiler Compiler Configurations -# Compilation Tests, code coverage and flags -# Macro Expansion Configuration for the code expansion +# Macro Expansion Configuration for the code expansion # Collections All collections # # Variables marked as const are to not be modified @@ -25,7 +22,7 @@ from typing import List, Text # Output directory # In this directory it is assumed that the following files and directories are -# included: main.c, unt folder +# included: main.c, codecov.c, unt folder OUTPUT_DIR = './tests' # All libraries ALL_LIBS = ['CMC'] @@ -39,33 +36,46 @@ BUILD_DIR = f'{OUTPUT_DIR}/build' INCLUDE_DIR = f"{OUTPUT_DIR}/{DIR_MAP['HEADER']}" # const SRC_DIR = f"{OUTPUT_DIR}/{DIR_MAP['SOURCE']}" +# Where executables go +BIN_DIR = f'{BUILD_DIR}/bin' # Where object files .o go OBJ_DIR = BUILD_DIR -# The name of the main test executable -TEST_EXE = f'{OUTPUT_DIR}/main.exe' # Where .gcov files should go GCOV_DIR = f'{BUILD_DIR}/gcov' +# Files +# All const + +# The name of the main test +MAIN = 'main' +# The name of the code coverage file +CODECOV = 'codecov' +# The name of the single test file +SINGLE = 'single' + # Compiler -# Compilation +# Compiler to be used CC = 'gcc' # Compilation Flags -CFLAGS = ['-Wall', '-Werror'] +CFLAGS = ['-Wall', '-Werror', '-O3'] # Link flags LFLAGS = [] # Coverage Flags CVFLAGS = ['--coverage', '-O0'] # gcov flags GCOV_FLAGS = [] -# Include to the main Library +# Include of the main library # const INCLUDE = ['-I', './src'] # Include to the test file headers # const TINCLUDE = ['-I', f'{INCLUDE_DIR}'] +# Include to code coverage +# const +CVINCLUDE = ['-I', INCLUDE_DIR, '-I', SRC_DIR] -# Macro expansion +# Macro Expansion # -E Only preprocessor # -P No linemarkers @@ -78,30 +88,6 @@ UNIQUE_FLAG = '// C_MACRO_COLLECTIONS_CODE' # Temporary file used to expand the macros TMP_FILE = './main.c' -# Just a realy helpfull overview -CONFIG = f'''Directory Settings - OUTPUT_DIR = {OUTPUT_DIR} - ALL_LIBS = {ALL_LIBS} - DIR_MAP = {DIR_MAP} - EXT_MAP = {EXT_MAP} - BUILD_DIR = {BUILD_DIR} - INCLUDE_DIR = {INCLUDE_DIR} - SRC_DIR = {SRC_DIR} - OBJ_DIR = {OBJ_DIR} - GCOV_DIR = {GCOV_DIR} -Compiler Settings - CC = {CC} - CFLAGS = {CFLAGS} - LFLAGS = {LFLAGS} - CVFLAGS = {CVFLAGS} - GCOV_FLAGS = {GCOV_FLAGS} - INCLUDE = {INCLUDE} - TINCLUDE = {TINCLUDE} -Macro Expansion - EXPAND_FLAGS = {EXPAND_FLAGS} - UNIQUE_FLAG = {UNIQUE_FLAG} - TMP_FILE = {TMP_FILE}''' - # All collections that can be used for testing COLLECTIONS = [ @@ -125,6 +111,36 @@ COLLECTIONS = [ ] +# Just a realy helpfull overview +CONFIG = f'''Directory Settings + OUTPUT_DIR = {OUTPUT_DIR} + ALL_LIBS = {ALL_LIBS} + DIR_MAP = {DIR_MAP} + EXT_MAP = {EXT_MAP} + BUILD_DIR = {BUILD_DIR} + INCLUDE_DIR = {INCLUDE_DIR} + SRC_DIR = {SRC_DIR} + OBJ_DIR = {OBJ_DIR} + GCOV_DIR = {GCOV_DIR} +File Settings + MAIN = {MAIN} + CODECOV = {CODECOV} + SINGLE = {SINGLE} +Compiler Settings + CC = {CC} + CFLAGS = {CFLAGS} + LFLAGS = {LFLAGS} + CVFLAGS = {CVFLAGS} + GCOV_FLAGS = {GCOV_FLAGS} + INCLUDE = {INCLUDE} + TINCLUDE = {TINCLUDE} + CVINCLUDE = {CVINCLUDE} +Macro Expansion + EXPAND_FLAGS = {EXPAND_FLAGS} + UNIQUE_FLAG = {UNIQUE_FLAG} + TMP_FILE = {TMP_FILE}''' + + def full_path(path: Text) -> Text: ''' Returns the full path of a 'path' with ralative paths, e.g. './' or '../' @@ -142,18 +158,28 @@ def execute_command(commands: List[Text]): # exit(1) -def assert_executable(program: Text): +def require_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. Raises an exception if it doesn't. ''' if shutil.which(program) is None: raise Exception(f'The required executable {program} was not found in your path') +def require_file(file: Text): + ''' + Asserts that a given file exists. Raises an exception if it doesn't. + ''' + f = pathlib.Path(file).resolve() + if not f.is_file(): + raise Exception(f'The required file {str(f)} was not found') + + def assert_dir(directory: Text): ''' Given a string 'directory' that is a path to a directory, make sure that it exists. ''' + # TODO change this to use pathlib ? if not os.path.exists(directory): os.makedirs(directory) @@ -175,19 +201,30 @@ def assert_structure(): assert_dir(BUILD_DIR) assert_dir(OBJ_DIR) + assert_dir(BIN_DIR) assert_dir(GCOV_DIR) for lib in ALL_LIBS: assert_dir(f'{OBJ_DIR}/{lib.lower()}') +def format_code(source: bool, tests: bool): + ''' + Format all source files from the main library or test files. + ''' + if source: + execute_command(['find', './src/', '-name', '*.h', '|', 'xargs', 'clang-format', '--style=file', '--verbose', '-i']) + if tests: + execute_command(['find', './tests/', '-name', '*.c', '-o', '-name', '*.h', '|', 'xargs', 'clang-format', '--style=file', '--verbose', '-i']) + + def clean_build(cleanfull: bool): ''' Removes all files generated by compilation and code coverage along with folders. ''' # -f prevents 'rm' from failing execute_command(['rm', '-r', '-f', f'{GCOV_DIR}/*.gcov']) - execute_command(['rm', '-f', TEST_EXE]) + execute_command(['rm', '-r', '-f', f'{OUTPUT_DIR}/*.exe']) execute_command(['rm', '-r', '-f', BUILD_DIR]) if cleanfull: @@ -267,30 +304,12 @@ def format_expand(): execute_command(cmd) -def format_code(source: bool, tests: bool): +def build_main(): ''' - Format all source files from the main library or test files. + Build all object files and main.c for tests. ''' - if source: - execute_command(['find', './src/', '-name', '*.h', '|', 'xargs', 'clang-format', '--style=file', '--verbose', '-i']) - if tests: - 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 - else: - cflags += ['-O3'] - for data in COLLECTIONS: 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"] @@ -299,34 +318,48 @@ def build_tests(with_coverage: bool): # 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"] + 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 += LFLAGS - cmd += ['-o', TEST_EXE] + cmd += ['-o', f'{BIN_DIR}/{MAIN}.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): +def run_main(): ''' - Runs tests. If coverage, run gcov. + Runs main.exe ''' - execute_command([full_path(TEST_EXE)]) + execute_command([full_path(f'{BIN_DIR}/{MAIN}.exe')]) - if coverage: - objects = [f'{OBJ_DIR}/main.gcda'] - objects += [f'{OBJ_DIR}/{data["LIB"].lower()}/{data["SNAME"]}.gcda' for data in COLLECTIONS] - execute_command(['gcov'] + GCOV_FLAGS + objects) - execute_command(['mv', '*.gcov', GCOV_DIR]) + +def build_codecov(): + ''' + Builds codecov.c for code coverage. + ''' + cmd = [CC] + cmd += CVFLAGS + cmd += CVINCLUDE # This one needs to go first + cmd += INCLUDE + cmd += [f'{OUTPUT_DIR}/{CODECOV}.c'] + cmd += ['-o', f'{BIN_DIR}/{CODECOV}.exe'] + execute_command(cmd) + + +def run_codecov(): + ''' + Runs codecov.exe and then gcov on the generated gcda files. All gcov files then go to GCOV_DIR. + ''' + execute_command([full_path(f'{BIN_DIR}/{CODECOV}.exe')]) + execute_command(['gcov'] + GCOV_FLAGS + [f'{CODECOV}.gcda']) + execute_command(['mv', '*.gcov', GCOV_DIR]) if __name__ == '__main__': @@ -334,53 +367,64 @@ if __name__ == '__main__': p.add_argument('-sc', '--show-config', help='Displays the current configuration', + default=False, action='store_true', dest='config') - tests = p.add_argument_group('tests', 'For dealing with tests') + source = p.add_argument_group('source', 'For dealing with the source files and formatting') - tests.add_argument('-c', '--clean', + expand = p.add_argument_group('expand', 'For dealing with code generation and formatting for tests') + + task = p.add_argument_group('task', 'Specify a task (mutually exclusive)') + task_ = task.add_mutually_exclusive_group(required=True) + + action = p.add_argument_group('action', 'Actions on task') + + task_.add_argument('-cv', '--codecov', + help='Designates task as code coverage (uses codecov.c)', + default=False, + action='store_true', + dest='codecov') + task_.add_argument('-m', '--main', + help='Designates task as the main tests (uses main.c)', + default=False, + action='store_true', + dest='main') + task_.add_argument('-s', '--single', + help='Designates task as an individual test (uses single.c)', + dest='single') + + expand.add_argument('-c', '--clean', help='Cleans all build objects', default=False, action='store_true', dest='clean') - tests.add_argument('-cf', '--clean-full', + expand.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', + expand.add_argument('-e', '--expand', help='Expands the header and source files', default=False, action='store_true', dest='expand') - tests.add_argument('-nf', '--no-format', + expand.add_argument('-nf', '--no-format', help='Does not format the output of expand. Can be used when you might not have clang-format', default=False, action='store_true', dest='noformat') - tests.add_argument('-b', '--build', - help='Compile the object files and the main executable', + + action.add_argument('-b', '--build', + help='Build task', default=False, action='store_true', dest='build') - tests.add_argument('-r', '--run', - help='Runs tests, assuming that it is already built', + action.add_argument('-r', '--run', + help='Run task', default=False, action='store_true', dest='run') - tests.add_argument('-cv', '--coverage', - help='Adds the code coverage flags for compilation', - default=False, - action='store_true', - dest='coverage') - tests.add_argument('-rr', '--rerun', - help='Compile the object files again and then run the tests', - default=False, - action='store_true', - dest='rerun') - - source = p.add_argument_group('source', 'For dealing with the source files and formatting') source.add_argument('-fs', '--format-source', help='Format the source code', @@ -399,34 +443,50 @@ if __name__ == '__main__': print(CONFIG) exit(0) - if args.clean or args.cleanfull: - clean_build(args.cleanfull) + if args.fmt_source or args.fmt_tests: + require_executable('find') + require_executable('xargs') + format_code(args.fmt_source, args.fmt_tests) + # Prevent mixing code + exit(0) if not args.cleanfull: assert_structure() - assert_executable(CC) + if args.clean: + clean_build(False) + else: + clean_build(True) + # Nothing else will work, so just exit + exit(0) if args.expand: expand_code() if not args.noformat: - assert_executable('clang-format') + require_executable('clang-format') format_expand() else: # In case the next task is to build the code from source # This warning is a very annoying one from gcc CFLAGS += ['-Wno-misleading-indentation'] - if args.fmt_source or args.fmt_tests: - assert_executable('find') - assert_executable('xargs') - format_code(args.fmt_source, args.fmt_tests) - if args.build: - build_tests(args.coverage) + require_executable(CC) + if args.codecov: + require_executable('gcov') + + if args.codecov: + require_file(f'{OUTPUT_DIR}/{CODECOV}.c') + require_executable + if args.build: + build_codecov() if args.run: - run_tests(args.coverage) - elif args.rerun: - build_tests(args.coverage) - run_tests(args.coverage) - elif args.run: - run_tests(args.coverage) + run_codecov() + elif args.main: + require_file(f'{OUTPUT_DIR}/{MAIN}.c') + if args.build: + build_main() + if args.run: + run_main() + elif args.single: + require_file(f'{OUTPUT_DIR}/{SINGLE}.c') + # TODO diff --git a/tests/codecov.c b/tests/codecov.c new file mode 100644 index 0000000..442ba00 --- /dev/null +++ b/tests/codecov.c @@ -0,0 +1,140 @@ +// Specifically used for code coverage + +#include +#include + +#include "macro_collections.h" + +#include "cmc/bitset.h" +#include "cmc/deque.h" +#include "cmc/hashbidimap.h" +#include "cmc/hashmap.h" +#include "cmc/hashmultimap.h" +#include "cmc/hashmultiset.h" +#include "cmc/hashset.h" +#include "cmc/heap.h" +#include "cmc/intervalheap.h" +#include "cmc/linkedlist.h" +#include "cmc/list.h" +#include "cmc/queue.h" +#include "cmc/sortedlist.h" +#include "cmc/stack.h" +#include "cmc/treemap.h" +#include "cmc/treeset.h" + +#include "cmc/bitset.c" +#include "cmc/deque.c" +#include "cmc/hashbidimap.c" +#include "cmc/hashmap.c" +#include "cmc/hashmultimap.c" +#include "cmc/hashmultiset.c" +#include "cmc/hashset.c" +#include "cmc/heap.c" +#include "cmc/intervalheap.c" +#include "cmc/linkedlist.c" +#include "cmc/list.c" +#include "cmc/queue.c" +#include "cmc/sortedlist.c" +#include "cmc/stack.c" +#include "cmc/treemap.c" +#include "cmc/treeset.c" + +#include "unt/cmc/bitset.h" +#include "unt/cmc/deque.h" +#include "unt/cmc/hashbidimap.h" +#include "unt/cmc/hashmap.h" +#include "unt/cmc/hashmultimap.h" +#include "unt/cmc/hashmultiset.h" +#include "unt/cmc/hashset.h" +#include "unt/cmc/heap.h" +#include "unt/cmc/intervalheap.h" +#include "unt/cmc/linkedlist.h" +#include "unt/cmc/list.h" +#include "unt/cmc/queue.h" +#include "unt/cmc/sortedlist.h" +#include "unt/cmc/stack.h" +#include "unt/cmc/treemap.h" +#include "unt/cmc/treeset.h" + +#include "unt/utl/foreach.h" + +#include "utl/assert.h" +#include "utl/timer.h" + +#include "./unt/utl.h" + +#define cmc_run(unit, unit_fails, test_fails) \ + do \ + { \ + uintmax_t f = unit(); \ + if (f != 0) \ + unit_fails += 1; \ + test_fails += f; \ + } while (0) + +int main(void) +{ + // clang-format off + printf(" +---------------------------------------------------------------+\n"); + printf(" | C Macro Collections Library |\n"); + printf(" | |\n"); + printf(" | Code Coverage Suite |\n"); + printf(" | |\n"); + printf(" | https://github.com/LeoVen/C-Macro-Collections |\n"); + printf(" | Leonardo Vencovsky (leonardo.vencovsky@gmail.com) |\n"); + printf(" +---------------------------------------------------------------+\n\n"); + // clang-format on + + struct cmc_timer timer; + + cmc_timer_start(timer); + uintmax_t tests = 0, units = 0; + + cmc_run(CMCBitSet, units, tests); + cmc_run(CMCBitSetIter, units, tests); + cmc_run(CMCDeque, units, tests); + cmc_run(CMCDequeIter, units, tests); + cmc_run(CMCHashBidiMap, units, tests); + cmc_run(CMCHashBidiMapIter, units, tests); + cmc_run(CMCHashMap, units, tests); + cmc_run(CMCHashMapIter, units, tests); + cmc_run(CMCHashMultiMap, units, tests); + cmc_run(CMCHashMultiMapIter, units, tests); + cmc_run(CMCHashMultiSet, units, tests); + cmc_run(CMCHashMultiSetIter, units, tests); + cmc_run(CMCHashSet, units, tests); + cmc_run(CMCHashSetIter, units, tests); + cmc_run(CMCHeap, units, tests); + cmc_run(CMCHeapIter, units, tests); + cmc_run(CMCIntervalHeap, units, tests); + cmc_run(CMCIntervalHeapIter, units, tests); + cmc_run(CMCLinkedList, units, tests); + cmc_run(CMCLinkedListIter, units, tests); + cmc_run(CMCList, units, tests); + cmc_run(CMCListIter, units, tests); + cmc_run(CMCQueue, units, tests); + cmc_run(CMCQueueIter, units, tests); + cmc_run(CMCSortedList, units, tests); + cmc_run(CMCSortedListIter, units, tests); + cmc_run(CMCStack, units, tests); + cmc_run(CMCStackIter, units, tests); + cmc_run(CMCTreeMap, units, tests); + cmc_run(CMCTreeMapIter, units, tests); + cmc_run(CMCTreeSet, units, tests); + cmc_run(CMCTreeSetIter, units, tests); + + cmc_run(ForEach, units, tests); + + cmc_timer_stop(timer); + + printf(" +---------------------------------------------------------------+\n"); + printf(" | SUMMARY |\n"); + printf(" +---------------------------------------------------------------+\n"); + printf(" | Total Units Failed : %25" PRIuMAX " units |\n", units); + printf(" | Total Tests Failed : %25" PRIuMAX " tests |\n", tests); + printf(" | Total Asserts Failed : %25" PRIuMAX " asserts |\n", cmc_assert_failed); + printf(" | Total Runtime : %25.0lf milliseconds |\n", timer.result); + printf(" +---------------------------------------------------------------+\n"); + + return tests; +}