# FIXME: PBL-17362 Script should be build type (FW or SDK) agnostic (waf & PEP8 compliant) import bitmapgen import png2pblpng import os import re import sh import sys import waflib from waftools.pebble_test import clar def remove_old_coverage_files(bld): # Remove old .gcda files: old_coverage_files = bld.path.get_bld().ant_glob('**/*.gcda lcov.info', remove=False) for old_file in old_coverage_files: os.remove(old_file.abspath()) def update_lcov(bld): """ Update lcov-related files based on the results of `./waf test`""" print "Generating code coverage information using lcov..." lcov_version_cmd = ['lcov', '--version'] # Send stdout of checking for lcov to /dev/null to hide it; stderr will still be visible with open(os.devnull, 'w') as devnull_fp: ret = bld.exec_command(lcov_version_cmd, stdout=devnull_fp) if ret != 0: bld.fatal("Error running `lcov`. Is it installed?") tests_path = bld.path.get_bld().abspath() lcov_info_out_file = os.path.join(tests_path, 'lcov.info') try: platform_specific_lcov_args = [] if sys.platform.startswith('linux'): platform_specific_lcov_args.extend(['--gcov-tool', 'llvm-cov']) cmd = ['lcov', '--capture', '--directory', tests_path, '--output-file', lcov_info_out_file] cmd += platform_specific_lcov_args bld.cmd_and_log(cmd, quiet=waflib.Context.BOTH) # remove unit-tests directory itself from lcov report cmd = ['lcov', '--remove', lcov_info_out_file, 'tests/**', '-o', lcov_info_out_file] cmd += platform_specific_lcov_args bld.cmd_and_log(cmd, quiet=waflib.Context.BOTH) except waflib.Errors.WafError as e: print e.stdout, '\n', e.stderr bld.fatal("Error running `lcov`") if bld.options.coverage: lcov_html_directory = os.path.join(tests_path, 'lcov-html') genhtml_cmd = ['genhtml', lcov_info_out_file, '--output-directory', lcov_html_directory] try: bld.cmd_and_log(genhtml_cmd, quiet=waflib.Context.BOTH) except waflib.Errors.WafError as e: print e.stdout, '\n', e.stderr bld.fatal("Error running `genhtml`") index_html = os.path.join(lcov_html_directory, 'index.html') print "Updated coverage report at %s" % index_html def convert_png_to_pbi(task): src_png = task.inputs[0].srcpath() dest_pbi = task.outputs[0].srcpath() bitdepth = None if any(word in dest_pbi for word in ['.8bit.', '~snowy', '~spalding', '~cutts', '~robert']): img_fmt = 'color_raw' elif any(word in dest_pbi for word in ['.1bit.', '~tintin']): img_fmt = 'bw' else: img_fmt = 'color' # raw and palettized color images bit_suffix = re.search('(\d)bitpalette\.png', dest_pbi) if bit_suffix: bitdepth = int(bit_suffix.group(1)) pb = bitmapgen.PebbleBitmap(src_png, bitmap_format=img_fmt, crop=False, bitdepth=bitdepth) pb.convert_to_pbi_file(dest_pbi) def convert_png_to_pblpng(task): src_png = task.inputs[0].srcpath() dest_png = task.outputs[0].srcpath() # we need to be able to skip the png generator for specific test pngs flagged 'raw' # and copy over the original file if dest_png.endswith('.raw.png'): task.exec_command('cp -f {0} {1}'.format(task.inputs[0].abspath(), task.outputs[0].abspath())) else: palette_name = 'pebble64' bitdepth = None bit_suffix = re.search('(\d)bit(palette)?\.png', dest_png) if bit_suffix: bitdepth = int(bit_suffix.group(1)) elif any(word in dest_png for word in ['~snowy', '~spalding', '~cutts', '~robert']): bitdepth = 8 elif any(word in dest_png for word in ['~tintin']): bitdepth = 1 palette_name = 'pebble2' png2pblpng.convert_png_to_pebble_png(src_png, dest_png, palette_name=palette_name, bitdepth=bitdepth) # Creates a job for each PNG in the test_images directory. # Each of these PNGs will be converted into a PBI in the build directory. # Also exports TEST_IMAGES_PATH to point to the location of the PBIs. def generate_test_pbis(ctx): test_image_pbis = [] bitmapgen_path = ctx.path.find_node('../tools/bitmapgen.py').abspath() for png_file in ctx.path.find_node('test_images').ant_glob("*.png"): dest_pbi = png_file.get_bld().change_ext('.pbi') # if the image contains Xbit in the name, then generate both 1bit and 8bit PBI images if ".Xbit." in str(dest_pbi): dest_pbi = png_file.get_bld().change_ext('.1bit.pbi', '.Xbit.png') ctx(name='png_to_pbi', rule=convert_png_to_pbi, source=png_file, target=dest_pbi, bmp_script=bitmapgen_path) test_image_pbis.append(dest_pbi) dest_pbi = png_file.get_bld().change_ext('.8bit.pbi', '.Xbit.png') ctx(name='png_to_pbi', rule=convert_png_to_pbi, source=png_file, target=dest_pbi, bmp_script=bitmapgen_path) test_image_pbis.append(dest_pbi) else: ctx(name='png_to_pbi', rule=convert_png_to_pbi, source=png_file, target=dest_pbi, bmp_script=bitmapgen_path) test_image_pbis.append(dest_pbi) return test_image_pbis # Creates a job for select PNG in the test_images directory. # Each of these PNGs will be converted into a Pebble PNG8 in the build directory. def generate_test_pngs(ctx): test_image_pngs = [] pblpng_resources_list = [] pblpng_resources_list.extend( ctx.path.find_node('test_images').ant_glob("test_png__*.png")) for png_file in pblpng_resources_list: dest_png = png_file.get_bld() ctx(name='png_to_pblpng', rule=convert_png_to_pblpng, source=png_file, target=dest_png) test_image_pngs.append(dest_png) return test_image_pngs def copy_test_pngs_to_build_dir(ctx): test_image_pngs = [] # copy over test specific files such as png, apng copy_resources_list = [] copy_resources_list.extend( ctx.path.find_node('test_images').ant_glob("test_bitblt_circular__*.png")) copy_resources_list.extend( ctx.path.find_node('test_images').ant_glob("test_gbitmap_sequence__*.apng")) copy_resources_list.extend( ctx.path.find_node('test_images').ant_glob("test_kino_reel__*.apng")) copy_resources_list.extend( ctx.path.find_node('test_images').ant_glob("test_graphics_draw_text_flow__*.png")) for copy_file in copy_resources_list: dest_file = copy_file.get_bld() ctx(name='copy_png', rule='cp -f ${SRC} ${TGT}', source=copy_file, target=dest_file) test_image_pngs.append(dest_file) return test_image_pngs def copy_pdc_files_to_build_dir(ctx): test_image_pdc_files = [] copy_resources_list = ctx.path.find_node('test_images').ant_glob("*.pdc") for copy_file in copy_resources_list: dest_file = copy_file.get_bld() ctx(name='copy_pdc', rule='cp -f ${SRC} ${TGT}', source=copy_file, target=dest_file) test_image_pdc_files.append(dest_file) return test_image_pdc_files def copy_pfo_files_to_build_dir(ctx): test_image_pfo_files = [] copy_resources_list = ctx.path.find_node('test_images').ant_glob("*.pfo") for copy_file in copy_resources_list: dest_file = copy_file.get_bld() ctx(name='copy_pfo', rule='cp -f ${SRC} ${TGT}', source=copy_file, target=dest_file) test_image_pfo_files.append(dest_file) return test_image_pfo_files def convert_test_pdcs(ctx): test_image_pdc_files = [] resources_list = ctx.path.find_node('test_images').ant_glob("*.svg") resources_list.extend(ctx.path.find_node('test_images').ant_glob("*", src=False, dir=True)) import sys sys.path.insert(0, ctx.path.parent.abspath()) from tools.generate_pdcs import pdc_gen def convert_svg_image(task): pdc_gen.create_pdc_from_path( task.inputs[0].abspath(), task.outputs[0].abspath(), viewbox_size=(0, 0), verbose=False, duration=0, play_count=0) def convert_svg_sequence(task): dir_name = os.path.dirname(task.inputs[0].abspath()) pdc_gen.create_pdc_from_path( dir_name, task.outputs[0].abspath(), viewbox_size=(0, 0), verbose=False, duration=33, play_count=1) for input_node in resources_list: output_pdc = input_node.get_bld().change_ext('.pdc') test_image_pdc_files.append(output_pdc) if os.path.isdir(input_node.abspath()): conversion_rule = convert_svg_sequence source_files = input_node.ant_glob("*.svg") else: conversion_rule = convert_svg_image source_files = [input_node] ctx(rule=conversion_rule, source=source_files, target=output_pdc) return test_image_pdc_files def convert_pdc_to_pbi(ctx): bitmapgen_path = ctx.path.find_node('../tools/bitmapgen.py').abspath() test_pdc_pbis = [] pdc_files = ctx.path.find_node('test_images').ant_glob("test_pdc__*.pdc") pdc2png = ctx.path.get_bld().parent.make_node('pdc2png') for pdc in pdc_files: dest_pdc = pdc.get_bld().change_ext('.pdc.pdc') src_png = dest_pdc.change_ext('.png') dest_pbi = dest_pdc.change_ext('.pbi') ctx(rule='cp ${SRC} ${TGT}', source=pdc, target=dest_pdc) ctx(rule='${SRC[0].abspath()} ${SRC[1].abspath()}', source=[pdc2png, dest_pdc], target=src_png) ctx(rule=convert_png_to_pbi, source=src_png, target=dest_pbi, bmp_script=bitmapgen_path) test_pdc_pbis.append(dest_pbi) return test_pdc_pbis def options(opt): gr = opt.add_option_group('test options') gr.add_option('-D', '--debug_test', action='store_true', help='Execute tests within GDB. Use alongside -M.') gr.add_option('-M', '--match', dest='regex', default=None, action='store', help='Run regex match tests. Example: ./waf test -M "test.*resource.*"') gr.add_option('-L', '--list_tests', dest='list_tests', action='store_true', help='List all test names. Usually used in conjunction with -M. Example: ' './waf test -M test_animation -L') gr.add_option('-T', '--test_name', dest='test_name', default=None, action='store', help='Run only the given test name. Usually used in conjunction with -M. Example: ' './waf test -M test_animation -T unschedule') gr.add_option('-C', '--coverage', dest='coverage', action='store_true', help='Generate gcov test coverage data and use lcov to generate HTML report') gr.add_option('--show_output', action='store_true', help='show test output') gr.add_option('--no_run', action='store_true', help='Do not run the tests, just build them') gr.add_option('--no_images', action='store_true', help='skip generation of test images, ' 'which are only required for some tests and can slow down build times') def build(bld): if bld.options.debug_test: if not bld.options.regex: bld.fatal('When using --debug_test, you must also use --match to' ' specify the test file to debug') bld.env.append_value('DEFINES', 'UNITTEST_DEBUG') bld.env.CFLAGS.append('-I' + bld.path.abspath() + '/../src/fw/util/time') bld.env.CFLAGS.append('-I' + bld.path.abspath() + '/../src/include') # clang on Linux errors on true == true or false == false compile-time assertions bld.env.CFLAGS.append('-Wno-tautological-compare') # time_t is defined in sys/types in newlib, and time.h on recent Linux # so just force the defined type for testing time bld.env.CFLAGS.append('-Dtime_t=__SYSCALL_SLONG_TYPE') # Many tests operate on a set of test images and require tools to process these # images and therefore need extra defines. Set up our environment first before running any # tests. test_images_dest_dir = bld.path.find_node('test_images').get_bld() # Set up the fail directory, and make it. This is used to output data from the tests for # comparison with the expected results. fail_dir = test_images_dest_dir.parent.make_node('failed') fail_path = fail_dir.abspath().strip() sh.rm('-rf', fail_path) fail_dir.mkdir() def convert_to_emscripten_fs_path_if_needed(node): real_fs_abspath = node.abspath() if bld.variant != 'test_rocky_emx': return real_fs_abspath # When transpiling unittests with Emscripten, the host machine's # filesystem is mounted at /node_fs, so we need to translate paths. return '/node_fs' + real_fs_abspath bld.env.test_image_defines = [ 'TEST_IMAGES_PATH="%s"' % convert_to_emscripten_fs_path_if_needed(test_images_dest_dir), 'TEST_OUTPUT_PATH="%s"' % convert_to_emscripten_fs_path_if_needed(fail_dir), 'PBI2PNG_EXE="%s"' % bld.path.find_node('../tools/pbi2png.py').abspath()] # Add test_pbis or test_pngs to runtime_deps for tests that require them if not bld.options.no_images: bld.env.test_pbis = generate_test_pbis(bld) bld.env.test_pngs = copy_test_pngs_to_build_dir(bld) bld.env.test_pngs.extend(generate_test_pngs(bld)) bld.env.test_pfos = copy_pfo_files_to_build_dir(bld) # Includes reference pdc and pbi generated from ref png bld.env.test_pdcs = bld.env.test_pbis + convert_test_pdcs(bld) + copy_pdc_files_to_build_dir(bld) bld.env.pdcs2png_test_files = bld.env.test_pbis + convert_pdc_to_pbi(bld) if bld.options.coverage: bld.env.append_value('CFLAGS', '-fprofile-arcs') bld.env.append_value('CFLAGS', '-ftest-coverage') bld.env.append_value('LINKFLAGS', '--coverage') test_wscript_dirs = [os.path.dirname(f.abspath()) for f in bld.path.ant_glob('**/wscript')] for dir in test_wscript_dirs: bld.recurse(dir) if bld.options.coverage: bld.add_pre_fun(remove_old_coverage_files) bld.add_post_fun(update_lcov) # vim:filetype=python