From 53986b4b0e2909400802d2cfaecfdb5ebfdbb36a Mon Sep 17 00:00:00 2001 From: Chris Mumford Date: Wed, 25 Aug 2021 06:37:59 -0700 Subject: [PATCH] test Refactor unit test scripts. (#2473) * Refactor unit test scripts. Does the following: 1. Remove as many dependencies on the operating system shell as possible. For example, use of shutil.rmtree(...) instead of os.system('rm -r ...'). This brings this script a bit closer to being able to run on Windows. 2. Switch from os.system() to subprocess.check_call(). * This is a bit more secure as check_call() directly invokes the subprocess without evaluation the arguments on a command-line. * Removes the need to evaluate the return code as check_call() does this. * Can directly set environment variables (e.g. env=cmd_env) instead of including with subprocess invocation (e.g. BIN=test.bin). 3. Minor cleanup to main.py sys.argv parsing. 4. PEP8 formatting. * Ignore FileNotFoundError for rmtree('report'). * Back to os.system for gcovr. * Removed unused shutil import. --- tests/build.py | 158 ++++++++++++++++++++++++++--------------------- tests/defines.py | 12 ++-- tests/main.py | 66 ++++++++++---------- 3 files changed, 128 insertions(+), 108 deletions(-) diff --git a/tests/build.py b/tests/build.py index 435cc0569..9275c0016 100755 --- a/tests/build.py +++ b/tests/build.py @@ -2,86 +2,104 @@ import os import re +import subprocess -lvgldirname = os.path.abspath('..') -lvgldirname = os.path.basename(lvgldirname) -lvgldirname = '"' + lvgldirname + '"' +test_dir = os.path.dirname(os.path.realpath(__file__)) +lvgl_dir = os.path.abspath(os.path.join(test_dir, os.pardir)) +lvgl_dir_name = 'lvgl' # LVGL subdirectory (base name) in lvgl_dir. +lvgl_parent_dir = os.path.abspath(os.path.join(lvgl_dir, os.pardir)) + +lv_conf_path = os.path.join(lvgl_dir, 'tests/src/lv_test_conf.h') +base_defines = ['-DLV_CONF_PATH="%s"' % lv_conf_path, '-DLV_BUILD_TEST'] + +def maybe_quote(obj): + if type(obj) == str: + return '"%s"' % obj + return obj + + +def zip_defines(defines): + return ['-D%s=%s' % (key, maybe_quote(defines[key])) for key in defines] -base_defines = '"-DLV_CONF_PATH=' + lvgldirname +'/tests/src/lv_test_conf.h -DLV_BUILD_TEST"' def build(defines): - global base_defines - optimization = '"-O3 -g0"' - d_all = base_defines[:-1] + " "; - - for d in defines: - d_all += " -D" + d + "=" + str(defines[d]) - - d_all += '"' - # -s makes it silence - cmd = "make -s -j BIN=test.bin " + "MAINSRC=src/lv_test_main.c LVGL_DIR_NAME=" + lvgldirname + " DEFINES=" + d_all + " OPTIMIZATION=" + optimization + global base_defines + optimization = ['-O3', '-g0'] + d_all = base_defines + zip_defines(defines) - print("") - print("Build") - print("-----------------------", flush=True) -# print(cmd) - ret = os.system(cmd) - if(ret != 0): - print("BUILD ERROR! (error code " + str(ret) + ")") - exit(1) + cmd_env = os.environ.copy() + cmd_env['BIN'] = 'test.bin' + cmd_env['MAINSRC'] = 'src/lv_test_main.c' + cmd_env['LVGL_DIR'] = lvgl_parent_dir + cmd_env['LVGL_DIR_NAME'] = lvgl_dir_name + cmd_env['DEFINES'] = ' '.join(d_all) + cmd_env['OPTIMIZATION'] = ' '.join(optimization) - print("") - print("Run") - print("-----------------------", flush=True) - ret = os.system("./test.bin") - if(ret != 0): - print("RUN ERROR! (error code " + str(ret) + ")", flush=True) - exit(1) + print("") + print("Build") + print("-----------------------", flush=True) + # -s makes it silence + subprocess.check_call(['make', '-s', '--jobs=%d' % os.cpu_count()], env=cmd_env) + + print("") + print("Run") + print("-----------------------", flush=True) + subprocess.check_call("./test.bin") def build_test(defines, test_name): - global base_defines - optimization = '"-g0"' + global base_defines + optimization = ['-g0'] - print("") - print("") - print("~~~~~~~~~~~~~~~~~~~~~~~~") - print(re.search("/[a-z_]*$", test_name).group(0)[1:]) - print("~~~~~~~~~~~~~~~~~~~~~~~~", flush=True) - - d_all = base_defines[:-1] + " "; - - for d in defines: - d_all += " -D" + d + "=" + str(defines[d]) - - d_all += '"' - test_file_name = test_name + ".c" - test_file_runner_name = test_name + "_Runner.c" - test_file_runner_name = test_file_runner_name.replace("/test_cases/", "/test_runners/") - csrcs = " EXTRA_CSRCS=\"unity/unity.c unity/unity_support.c src/test_fonts/font_1.c src/test_fonts/font_2.c src/test_fonts/font_3.c \" " - # -s makes it silence - cmd = "make -s -j BIN=test.bin MAINSRC=" + test_file_name + " TEST_SRC=" + test_file_runner_name + csrcs + " LVGL_DIR_NAME=" + lvgldirname + " DEFINES=" + d_all + " OPTIMIZATION=" + optimization + print("") + print("") + print("~~~~~~~~~~~~~~~~~~~~~~~~") + print(re.search("/[a-z_]*$", test_name).group(0)[1:]) + print("~~~~~~~~~~~~~~~~~~~~~~~~", flush=True) + + d_all = base_defines + zip_defines(defines) + + test_file_name = test_name + ".c" + test_file_runner_name = test_name + "_Runner.c" + test_file_runner_name = test_file_runner_name.replace( + "/test_cases/", "/test_runners/") + cmd_env = os.environ.copy() + cmd_env['BIN'] = 'test.bin' + cmd_env['MAINSRC'] = test_file_name + cmd_env['TEST_SRC'] = test_file_runner_name + cmd_env['LVGL_DIR'] = lvgl_parent_dir + cmd_env['LVGL_DIR_NAME'] = lvgl_dir_name + cmd_env['DEFINES'] = ' '.join(d_all) + cmd_env['OPTIMIZATION'] = ' '.join(optimization) + extra_csrcs = [ + 'unity/unity.c', 'unity/unity_support.c', + 'src/test_fonts/font_1.c', 'src/test_fonts/font_2.c', + 'src/test_fonts/font_3.c' + ] + cmd_env['EXTRA_CSRCS'] = ' '.join(extra_csrcs) + + print("") + print("Build") + print("-----------------------", flush=True) + # -s makes it silence + subprocess.check_call(['make', '-s', '--jobs=%d' % os.cpu_count()], env=cmd_env) + + print("") + print("Run") + print("-----------------------") + subprocess.check_call('./test.bin') - print("") - print("Build") - print("-----------------------", flush=True) -# print(cmd) - ret = os.system(cmd) - if(ret != 0): - print("BUILD ERROR! (error code " + str(ret) + ")", flush=True) - exit(1) - - print("") - print("Run") - print("-----------------------") - ret = os.system("./test.bin") - if(ret != 0): - print("RUN ERROR! (error code " + str(ret) + ")", flush=True) - exit(1) def clean(): - print("") - print("Clean") - print("-----------------------", flush=True) - os.system("make clean LVGL_DIR_NAME=" + lvgldirname) - os.system("rm -f ./test.bin") + print("") + print("Clean") + print("-----------------------", flush=True) + + cmd_env = os.environ.copy() + cmd_env['LVGL_DIR'] = lvgl_parent_dir + cmd_env['LVGL_DIR_NAME'] = lvgl_dir_name + subprocess.check_call(['make', 'clean'], env=cmd_env) + try: + os.remove('test.bin') + except FileNotFoundError: + pass diff --git a/tests/defines.py b/tests/defines.py index 16aed43fa..a34a0a741 100644 --- a/tests/defines.py +++ b/tests/defines.py @@ -24,7 +24,7 @@ minimal_monochrome = { "LV_BUILD_EXAMPLES":1, - "LV_FONT_DEFAULT":"\\\"&lv_font_montserrat_14\\\"", + "LV_FONT_DEFAULT":"&lv_font_montserrat_14", } @@ -54,7 +54,7 @@ normal_8bit = { "LV_BUILD_EXAMPLES":1, - "LV_FONT_DEFAULT":"\\\"&lv_font_montserrat_14\\\"", + "LV_FONT_DEFAULT":"&lv_font_montserrat_14", } @@ -83,7 +83,7 @@ minimal_16bit = { "LV_BUILD_EXAMPLES":1, - "LV_FONT_DEFAULT":"\\\"&lv_font_montserrat_14\\\"", + "LV_FONT_DEFAULT":"&lv_font_montserrat_14", } @@ -114,7 +114,7 @@ normal_16bit_swap = { "LV_BUILD_EXAMPLES":1, - "LV_FONT_DEFAULT":"\\\"&lv_font_montserrat_14\\\"", + "LV_FONT_DEFAULT":"&lv_font_montserrat_14", } full_32bit = { @@ -182,7 +182,7 @@ full_32bit = { "LV_BUILD_EXAMPLES":1, - "LV_FONT_DEFAULT":"\\\"&lv_font_montserrat_24\\\"", + "LV_FONT_DEFAULT":"&lv_font_montserrat_24", } test = { @@ -227,5 +227,5 @@ test = { "LV_BUILD_EXAMPLES":1, - "LV_FONT_DEFAULT":"\\\"&lv_font_montserrat_14\\\"", + "LV_FONT_DEFAULT":"&lv_font_montserrat_14", } diff --git a/tests/main.py b/tests/main.py index 296deb211..79898c81a 100755 --- a/tests/main.py +++ b/tests/main.py @@ -2,52 +2,54 @@ import defines import build +import shutil import test import sys import os + def build_conf(title, defs): - print("") - print("") - print("============================================") - print(title) - print("============================================") - print("", flush=True) + print("") + print("") + print("============================================") + print(title) + print("============================================") + print("", flush=True) - build.clean() - build.build(defs) + build.clean() + build.build(defs) -test_only = False; -test_report = False; -test_noclean = False; -if "test" in sys.argv: test_only = True; -if "report" in sys.argv: test_report = True; -if "noclean" in sys.argv: test_noclean = True; +test_only = "test" in sys.argv +test_report = "report" in sys.argv +test_noclean = "noclean" in sys.argv if not test_only: - build_conf("Minimal config monochrome", defines.minimal_monochrome) - build_conf("Normal config, 8 bit color depth", defines.normal_8bit) - build_conf("Minimal config, 16 bit color depth", defines.minimal_16bit) - build_conf("Normal config, 16 bit color depth swapped", defines.normal_16bit_swap) - build_conf("Full config, 32 bit color depth", defines.full_32bit) + build_conf("Minimal config monochrome", defines.minimal_monochrome) + build_conf("Normal config, 8 bit color depth", defines.normal_8bit) + build_conf("Minimal config, 16 bit color depth", defines.minimal_16bit) + build_conf("Normal config, 16 bit color depth swapped", + defines.normal_16bit_swap) + build_conf("Full config, 32 bit color depth", defines.full_32bit) files = test.prepare() if test_noclean == False: - build.clean() + build.clean() for f in files: - name = f[:-2] #test_foo.c -> test_foo - build.build_test(defines.test, name) - -if test_report: - print("") - print("Generating report") - print("-----------------------", flush=True) - os.system("rm -r report") - os.system("mkdir report") - os.system("gcovr -r ../ --html-details -o report/index.html --exclude-directories '\.\./examples' --exclude-directories 'src/.*' --exclude-directories 'unity' --exclude 'lv_test_.*\.c'") - os.system("gcovr -r ../ -x report/coverage.xml --exclude-directories '\.\./examples' --exclude-directories 'src/.*' --exclude-directories 'unity' --exclude 'lv_test_.*\.c'") - print("Done: See report/index.html", flush=True) + name = f[:-2] # test_foo.c -> test_foo + build.build_test(defines.test, name) +if test_report: + print("") + print("Generating report") + print("-----------------------", flush=True) + try: + shutil.rmtree('report') + except FileNotFoundError: + pass + os.mkdir('report') + os.system("gcovr -r ../ --html-details -o report/index.html --exclude-directories '\.\./examples' --exclude-directories 'src/.*' --exclude-directories 'unity' --exclude 'lv_test_.*\.c'") + os.system("gcovr -r ../ -x report/coverage.xml --exclude-directories '\.\./examples' --exclude-directories 'src/.*' --exclude-directories 'unity' --exclude 'lv_test_.*\.c'") + print("Done: See report/index.html", flush=True)