From d500178c242936d7bb657fd8c804be8b8577e78b Mon Sep 17 00:00:00 2001 From: "Salvador E. Tropea" Date: Thu, 14 May 2020 13:11:31 -0300 Subject: [PATCH] Recoded all the tests. I used the class I wrote for kicad-automation-scripts. Now the tests are simpler, I also suitched to test the application from "outside" because I was having some ridiculous fails. Also added coverage meassurement, which is currently pathetic. --- .coveragerc | 10 + .gitignore | 4 + Makefile | 2 +- tests/conftest.py | 6 +- tests/test_plot/__init__.py | 0 tests/test_plot/plotting_test_utils.py | 121 -------- tests/test_plot/test_position.py | 187 ++++--------- tests/test_plot/test_simple_2layer.py | 116 +++----- tests/utils/context.py | 263 ++++++++++++++++++ ...ition.yaml => simple_position.kiplot.yaml} | 0 ...aml => simple_position_inches.kiplot.yaml} | 0 ...ml => simple_position_unified.kiplot.yaml} | 0 ...=> simple_position_unified_th.kiplot.yaml} | 0 13 files changed, 365 insertions(+), 344 deletions(-) create mode 100644 .coveragerc delete mode 100644 tests/test_plot/__init__.py delete mode 100644 tests/test_plot/plotting_test_utils.py create mode 100644 tests/utils/context.py rename tests/yaml_samples/{simple_position.yaml => simple_position.kiplot.yaml} (100%) rename tests/yaml_samples/{simple_position_inches.yaml => simple_position_inches.kiplot.yaml} (100%) rename tests/yaml_samples/{simple_position_unified.yaml => simple_position_unified.kiplot.yaml} (100%) rename tests/yaml_samples/{simple_position_unified_th.yaml => simple_position_unified_th.kiplot.yaml} (100%) diff --git a/.coveragerc b/.coveragerc new file mode 100644 index 00000000..96714f2b --- /dev/null +++ b/.coveragerc @@ -0,0 +1,10 @@ +[run] +source = + kiplot + src + +[report] +exclude_lines = + pragma: no cover +# raise RuntimeError + diff --git a/.gitignore b/.gitignore index 96b61e70..df30bb84 100644 --- a/.gitignore +++ b/.gitignore @@ -30,3 +30,7 @@ MANIFEST *.kicad_pcb-bak *.epr +htmlcov/ +pp/ +output/ +.coverage diff --git a/Makefile b/Makefile index fd76acd1..7dd561cb 100644 --- a/Makefile +++ b/Makefile @@ -21,7 +21,7 @@ test: lint test_local: lint rm -rf output $(PY_COV) erase - pytest-3 --plot_dir output + pytest-3 --test_dir output $(PY_COV) report $(PY_COV) html x-www-browser htmlcov/index.html diff --git a/tests/conftest.py b/tests/conftest.py index 549a7831..a39dc226 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -4,6 +4,6 @@ Test configuration def pytest_addoption(parser): - parser.addoption("--plot_dir", action="store", default=None, - help="the plot dir to use (omit to use a temp dir). " - "If given, plots will _not_ be cleared after testing.") + parser.addoption("--test_dir", action="store", default=None, + help="the test output dir to use (omit to use a temp dir). " + "If given, outputs will _not_ be cleared after testing.") diff --git a/tests/test_plot/__init__.py b/tests/test_plot/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/tests/test_plot/plotting_test_utils.py b/tests/test_plot/plotting_test_utils.py deleted file mode 100644 index bf01ee68..00000000 --- a/tests/test_plot/plotting_test_utils.py +++ /dev/null @@ -1,121 +0,0 @@ - -import os -import shutil -import tempfile -import logging -import pytest -import sys - -here = os.path.abspath(os.path.dirname(__file__)) -sys.path.insert(0, os.path.dirname(os.path.dirname(here))) - -from kiplot import kiplot -from kiplot import config_reader - - -KICAD_PCB_EXT = '.kicad_pcb' - - -class KiPlotTestContext(object): - - def __init__(self, test_name): - self.cfg = None - - # The name used for the test output dirs and other logging - self.test_name = test_name - - # The name of the PCB board file (will be interpolated into the plot - # files by pcbnewm so we need to know - self.board_name = None - - # The actual board file that will be loaded - self.board_file = None - - # The directory under which to place plots (None: use a temp dir) - self.plot_dir = pytest.config.getoption('plot_dir') - - # The actual output dir for this plot run - self._output_dir = None - # Clean the output dir afterwards (true for temp dirs) - self._del_dir_after = self.plot_dir is None - - def _get_text_cfg_dir(self): - - this_dir = os.path.dirname(os.path.realpath(__file__)) - - return os.path.join(this_dir, '../yaml_samples') - - def _get_board_cfg_dir(self): - - this_dir = os.path.dirname(os.path.realpath(__file__)) - - return os.path.join(this_dir, '../board_samples') - - def load_yaml_config_file(self, filename): - """ - Reads a config from a YAML file - """ - - cfg_file = os.path.join(self._get_text_cfg_dir(), filename) - pcb_file = os.path.join(self._get_board_cfg_dir(), - self.board_name + KICAD_PCB_EXT) - - cr = config_reader.CfgYamlReader(pcb_file) - - with open(cfg_file) as cf_file: - cfg = cr.read(cf_file) - - self.cfg = cfg - - def _load_board_file(self, filename=None): - """ - Load the named board. - - @param filename: a filename to load, or None to load the relevant - board name from the board sample dir - """ - - if filename is None: - self.board_file = os.path.join(self._get_board_cfg_dir(), - self.board_name + KICAD_PCB_EXT) - else: - self.board_file = filename - - assert os.path.isfile(self.board_file) - - def _set_up_output_dir(self): - - if not self.plot_dir: - # create a tmp dir - self.output_dir = tempfile.mkdtemp( - prefix='tmp_kiplot_{}'.format(self.test_name)) - - else: - self.output_dir = os.path.join(self.plot_dir, self.test_name) - # just create the dir - if os.path.isdir(self.output_dir): - # exists, that's OK - pass - else: - os.makedirs(self.output_dir) - - self.cfg.outdir = self.output_dir - logging.info('Output dir: '+self.output_dir) - - def clean_up(self): - - logging.debug('Clean-up') - if self._del_dir_after: - logging.debug('Removing dir') - shutil.rmtree(self.output_dir) - - def do_plot(self): - - self.cfg.validate() - - self._load_board_file(self.board_file) - - self._set_up_output_dir() - - plotter = kiplot.Plotter(self.cfg) - plotter.plot(self.board_file, '', False, ['all']) diff --git a/tests/test_plot/test_position.py b/tests/test_plot/test_position.py index 759a7b3f..7f7d4266 100644 --- a/tests/test_plot/test_position.py +++ b/tests/test_plot/test_position.py @@ -13,165 +13,78 @@ pytest-3 --log-cli-level debug """ -from . import plotting_test_utils - import os -import mmap -import re -import logging +import sys +# Look for the 'utils' module from where the script is running +script_dir = os.path.dirname(os.path.abspath(__file__)) +sys.path.insert(0, os.path.dirname(script_dir)) +# Utils import +from utils import context + +POS_DIR = 'positiondir' +positions = {'R1': (105, 35, 'top'), 'R2': (110, 35, 'bottom'), 'R3': (110, 45, 'top')} -def expect_file_at(filename): - - assert(os.path.isfile(filename)) - - -def get_pos_top_filename(board_name): - return board_name + '-top.pos' - - -def get_pos_bot_filename(board_name): - return board_name + '-bottom.pos' - - -def get_pos_both_filename(board_name): - return board_name + '-both.pos' - - -def expect_position(pos_data, side, ref, x, y, expected, inches=False): +def expect_position(ctx, file, comp, no_comp=[], inches=False): """ - Check if a component is or isn't in the file + Check if a list of components are or aren't in the file """ - - # expr = rb'^'+ref.encode()+rb'\s+\S+\s+\S+\s+([\d\.]+)\s+([\d\.]+)\s+([\d\.]+)\s+(\S+)$' - expr = rb'^'+ref.encode()+rb'\s+\S+\s+\S+\s+([-\d\.]+)\s+([-\d\.]+)\s+([-\d\.]+)\s+(\S+)\s*$' - - m = re.search(expr, pos_data, re.MULTILINE) - - if m: - logging.debug("Position found for " + ref) - else: - logging.debug("Position not found for " + ref) - - if expected: - assert(m) + # Components that must be found + texts = [] + for k in comp: + texts.append('^'+k+r'\s+\S+\s+\S+\s+([-\d\.]+)\s+([-\d\.]+)\s+([-\d\.]+)\s+(\S+)\s*$') + res = ctx.search_in_file(file, texts) + for k in comp: + x, y, side = positions[k] if inches: x = x/25.4 y = y/25.4 - assert(abs(float(x) - float(m.group(1))) < 0.001) - assert(abs(float(y) + float(m.group(2))) < 0.001) - assert(side == m.group(4).decode()) - # logging.debug(ref+' '+str(x)+' '+str(y)+' -> '+m.group(1).decode()+' '+m.group(2).decode()+' '+m.group(3).decode()+ - # ' '+m.group(4).decode()) - else: - assert(m is None) + matches = res.pop(0) + assert(abs(float(x) - float(matches[0])) < 0.001) + assert(abs(float(y) + float(matches[1])) < 0.001) + assert(side == matches[3]) - -def get_mmapped_data(filename): - - with open(filename) as fo: - return mmap.mmap(fo.fileno(), 0, access=mmap.ACCESS_READ) + # Components that must not be found + texts = [] + for k in no_comp: + expr = '^'+k+r'\s+\S+\s+\S+\s+([-\d\.]+)\s+([-\d\.]+)\s+([-\d\.]+)\s+(\S+)\s*$' + texts.append(expr) + ctx.search_not_in_file(file, texts) def test_3Rs_position(): - - ctx = plotting_test_utils.KiPlotTestContext('3Rs_position') - - ctx.board_name = '3Rs' - ctx.load_yaml_config_file('simple_position.yaml') - - ctx.do_plot() - - pos_dir = ctx.cfg.resolve_output_dir_for_name('position') - - pos_top = os.path.join(pos_dir, get_pos_top_filename(ctx.board_name)) - pos_bot = os.path.join(pos_dir, get_pos_bot_filename(ctx.board_name)) - - expect_file_at(pos_top) - expect_file_at(pos_bot) - - top = get_mmapped_data(pos_top) - bot = get_mmapped_data(pos_bot) - expect_position(top, 'top', 'R1', 105, 35, True) - expect_position(bot, 'bottom', 'R1', 105, 35, False) - expect_position(top, 'top', 'R2', 110, 35, False) - expect_position(bot, 'bottom', 'R2', 110, 35, True) - expect_position(top, 'top', 'R3', 110, 45, False) - expect_position(bot, 'bottom', 'R3', 110, 45, False) - + ctx = context.TestContext('3Rs_position', '3Rs', 'simple_position') + ctx.run() + pos_top = ctx.get_pos_top_filename(POS_DIR) + pos_bot = ctx.get_pos_bot_filename(POS_DIR) + ctx.expect_out_file(pos_top) + ctx.expect_out_file(pos_bot) + expect_position(ctx, pos_top, ['R1'], ['R2', 'R3']) + expect_position(ctx, pos_bot, ['R2'], ['R1', 'R3']) ctx.clean_up() def test_3Rs_position_unified(): - - ctx = plotting_test_utils.KiPlotTestContext('3Rs_position_unified') - - ctx.board_name = '3Rs' - ctx.load_yaml_config_file('simple_position_unified.yaml') - - ctx.do_plot() - - pos_dir = ctx.cfg.resolve_output_dir_for_name('position') - - pos_both = os.path.join(pos_dir, get_pos_both_filename(ctx.board_name)) - - expect_file_at(pos_both) - - both = get_mmapped_data(pos_both) - expect_position(both, 'top', 'R1', 105, 35, True) - expect_position(both, 'bottom', 'R2', 110, 35, True) - expect_position(both, '', 'R3', 110, 45, False) - + ctx = context.TestContext('3Rs_position_unified', '3Rs', 'simple_position_unified') + ctx.run() + expect_position(ctx, ctx.get_pos_both_filename(POS_DIR), ['R1', 'R2'], ['R3']) ctx.clean_up() def test_3Rs_position_unified_th(): - - ctx = plotting_test_utils.KiPlotTestContext('3Rs_position_unified_th') - - ctx.board_name = '3Rs' - ctx.load_yaml_config_file('simple_position_unified_th.yaml') - - ctx.do_plot() - - pos_dir = ctx.cfg.resolve_output_dir_for_name('position') - - pos_both = os.path.join(pos_dir, get_pos_both_filename(ctx.board_name)) - - expect_file_at(pos_both) - - both = get_mmapped_data(pos_both) - expect_position(both, 'top', 'R1', 105, 35, True) - expect_position(both, 'bottom', 'R2', 110, 35, True) - expect_position(both, 'top', 'R3', 110, 45, True) - + ctx = context.TestContext('3Rs_position_unified_th', '3Rs', 'simple_position_unified_th') + ctx.run() + expect_position(ctx, ctx.get_pos_both_filename(POS_DIR), ['R1', 'R2', 'R3']) ctx.clean_up() def test_3Rs_position_inches(): - - ctx = plotting_test_utils.KiPlotTestContext('3Rs_position_inches') - - ctx.board_name = '3Rs' - ctx.load_yaml_config_file('simple_position_inches.yaml') - - ctx.do_plot() - - pos_dir = ctx.cfg.resolve_output_dir_for_name('position') - - pos_top = os.path.join(pos_dir, get_pos_top_filename(ctx.board_name)) - pos_bot = os.path.join(pos_dir, get_pos_bot_filename(ctx.board_name)) - - expect_file_at(pos_top) - expect_file_at(pos_bot) - - top = get_mmapped_data(pos_top) - bot = get_mmapped_data(pos_bot) - expect_position(top, 'top', 'R1', 105, 35, True, True) - expect_position(bot, 'bottom', 'R1', 105, 35, False, True) - expect_position(top, 'top', 'R2', 110, 35, False, True) - expect_position(bot, 'bottom', 'R2', 110, 35, True, True) - expect_position(top, 'top', 'R3', 110, 45, False, True) - expect_position(bot, 'bottom', 'R3', 110, 45, False, True) - + ctx = context.TestContext('3Rs_position_inches', '3Rs', 'simple_position_inches') + ctx.run() + pos_top = ctx.get_pos_top_filename(POS_DIR) + pos_bot = ctx.get_pos_bot_filename(POS_DIR) + ctx.expect_out_file(pos_top) + ctx.expect_out_file(pos_bot) + expect_position(ctx, pos_top, ['R1'], ['R2', 'R3'], True) + expect_position(ctx, pos_bot, ['R2'], ['R1', 'R3'], True) ctx.clean_up() diff --git a/tests/test_plot/test_simple_2layer.py b/tests/test_plot/test_simple_2layer.py index d554023a..c782db33 100644 --- a/tests/test_plot/test_simple_2layer.py +++ b/tests/test_plot/test_simple_2layer.py @@ -1,112 +1,64 @@ """ -Tests of simple 2-layer PCBs -""" +Tests of simple 2-layer PCBs. +We generate the gerbers. -from . import plotting_test_utils +For debug information use: +pytest-3 --log-cli-level debug +""" import os -import mmap -import re +import sys import logging +# Look for the 'utils' module from where the script is running +script_dir = os.path.dirname(os.path.abspath(__file__)) +sys.path.insert(0, os.path.dirname(script_dir)) +# Utils import +from utils import context -def expect_file_at(filename): - - assert(os.path.isfile(filename)) - - -def get_gerber_filename(board_name, layer_slug, ext='.gbr'): - return board_name + '-' + layer_slug + ext - - -def get_gerber_job_filename(board_name): - return board_name + '-job.gbrjob' - - -def find_gerber_aperture(s, ap_desc): - - m = re.search(rb'%AD(.*)' + ap_desc + rb'\*%', s) - - if not m: - return None - - return m.group(1) - - -def expect_gerber_has_apertures(gbr_data, ap_list): - - aps = [] - +def expect_gerber_has_apertures(ctx, file, ap_list): + ap_matches = [] for ap in ap_list: - # find the circular aperture for the outline - ap_no = find_gerber_aperture(gbr_data, ap) - + ap_matches.append(r'%AD(.*)'+ap+r'\*%') + grps = ctx.search_in_file(file, ap_matches) + aps = [] + for grp in grps: + ap_no = grp[0] assert ap_no is not None - # apertures from D10 to D999 assert len(ap_no) in [2, 3] - aps.append(ap_no) - logging.debug("Found apertures {}".format(aps)) return aps -def expect_gerber_flash_at(gbr_data, pos): +def expect_gerber_flash_at(ctx, file, pos): """ Check for a gerber flash at a given point (it's hard to check that aperture is right without a real gerber parser """ - - repat = r'^X{x}Y{y}D03\*$'.format( - x=int(pos[0] * 100000), - y=int(pos[1] * 100000) - ) - - m = re.search(repat.encode(), gbr_data, re.MULTILINE) - - assert(m) - - logging.debug("Gerber flash found: " + repat) + repat = r'^X{x}Y{y}D03\*$'.format(x=int(pos[0]*100000), y=int(pos[1]*100000)) + ctx.search_in_file(file, [repat]) + logging.debug("Gerber flash found: "+repat) -def get_mmapped_data(filename): - - with open(filename) as fo: - return mmap.mmap(fo.fileno(), 0, access=mmap.ACCESS_READ) - - -# content of test_sample.py def test_2layer(): + prj = 'simple_2layer' + ctx = context.TestContext('Simple_2_layer', prj, prj) + ctx.run() - ctx = plotting_test_utils.KiPlotTestContext('simple_2layer') + g_dir = 'gerberdir' + f_cu = ctx.get_gerber_filename(g_dir, 'F_Cu') + ctx.expect_out_file(f_cu) + ctx.expect_out_file(ctx.get_gerber_job_filename(g_dir)) - ctx.board_name = 'simple_2layer' - ctx.load_yaml_config_file('simple_2layer.kiplot.yaml') - - ctx.do_plot() - - gbr_dir = ctx.cfg.resolve_output_dir_for_name('gerbers') - - f_cu_gbr = os.path.join(gbr_dir, - get_gerber_filename(ctx.board_name, "F_Cu")) - - expect_file_at(f_cu_gbr) - - # The gerber job file - job_file = os.path.join(gbr_dir, - get_gerber_job_filename(ctx.board_name)) - expect_file_at(job_file) - - f_cu_data = get_mmapped_data(f_cu_gbr) - - expect_gerber_has_apertures(f_cu_data, [ - rb"C,0.200000", - rb"R,2.000000X2.000000", - rb"C,1.000000"]) + expect_gerber_has_apertures(ctx, f_cu, [ + r"C,0.200000", + r"R,2.000000X2.000000", + r"C,1.000000"]) # expect a flash for the square pad - expect_gerber_flash_at(f_cu_data, (140, -100)) + expect_gerber_flash_at(ctx, f_cu, (140, -100)) ctx.clean_up() diff --git a/tests/utils/context.py b/tests/utils/context.py new file mode 100644 index 00000000..efca2e42 --- /dev/null +++ b/tests/utils/context.py @@ -0,0 +1,263 @@ +import os +import shutil +import tempfile +import logging +import subprocess +import re +import pytest +from glob import glob +from pty import openpty + +COVERAGE_SCRIPT = 'python3-coverage' +KICAD_PCB_EXT = '.kicad_pcb' +KICAD_SCH_EXT = '.sch' +REF_DIR = 'N/A' + +MODE_SCH = 1 +MODE_PCB = 0 + + +class TestContext(object): + + def __init__(self, test_name, board_name, yaml_name): + # We are using PCBs + self.mode = MODE_PCB + # The name used for the test output dirs and other logging + self.test_name = test_name + # The name of the PCB board file + self.board_name = board_name + # The actual board file that will be loaded + self._get_board_file() + # The YAML file we'll use + self._get_yaml_name(yaml_name) + # The actual output dir for this run + self._set_up_output_dir(pytest.config.getoption('test_dir')) + # stdout and stderr from the run + self.out = None + self.err = None + self.proc = None + + def _get_board_dir(self): + this_dir = os.path.dirname(os.path.realpath(__file__)) + return os.path.join(this_dir, '../board_samples') + + def _get_board_file(self): + self.board_file = os.path.abspath(os.path.join(self._get_board_dir(), + self.board_name + + (KICAD_PCB_EXT if self.mode == MODE_PCB else KICAD_SCH_EXT))) + logging.info('KiCad file: '+self.board_file) + assert os.path.isfile(self.board_file) + + def _get_yaml_dir(self): + this_dir = os.path.dirname(os.path.realpath(__file__)) + return os.path.join(this_dir, '../yaml_samples') + + def _get_yaml_name(self, name): + self.yaml_file = os.path.abspath(os.path.join(self._get_yaml_dir(), name+'.kiplot.yaml')) + logging.info('YAML file: '+self.yaml_file) + assert os.path.isfile(self.yaml_file) + + def _set_up_output_dir(self, test_dir): + if test_dir: + self.output_dir = os.path.join(test_dir, self.test_name) + os.makedirs(self.output_dir, exist_ok=True) + self._del_dir_after = False + else: + # create a tmp dir + self.output_dir = tempfile.mkdtemp(prefix='tmp-kiplot-'+self.test_name+'-') + self._del_dir_after = True + logging.info('Output dir: '+self.output_dir) + + def clean_up(self): + logging.debug('Clean-up') + if self._del_dir_after: + logging.debug('Removing dir') + shutil.rmtree(self.output_dir) + + def get_out_path(self, filename): + return os.path.join(self.output_dir, filename) + + def get_gerber_job_filename(self, dir): + return os.path.join(dir, self.board_name+'-job.gbrjob') + + def get_gerber_filename(self, dir, layer_slug, ext='.gbr'): + return os.path.join(dir, self.board_name+'-'+layer_slug+ext) + + def get_pos_top_filename(self, dir): + return os.path.join(dir, self.board_name+'-top.pos') + + def get_pos_bot_filename(self, dir): + return os.path.join(dir, self.board_name+'-bottom.pos') + + def get_pos_both_filename(self, dir): + return os.path.join(dir, self.board_name+'-both.pos') + + def expect_out_file(self, filename): + file = self.get_out_path(filename) + assert os.path.isfile(file) + assert os.path.getsize(file) > 0 + return file + + def dont_expect_out_file(self, filename): + file = self.get_out_path(filename) + assert not os.path.isfile(file) + + def create_dummy_out_file(self, filename): + file = self.get_out_path(filename) + with open(file, 'w') as f: + f.write('Dummy file\n') + + def run(self, ret_val=None, extra=None, use_a_tty=False, filename=None): + logging.debug('Running '+self.test_name) + # Change the command to be local and add the board and output arguments + cmd = [COVERAGE_SCRIPT, 'run', '-a'] + cmd.append(os.path.abspath(os.path.dirname(os.path.abspath(__file__))+'/../../src/kiplot')) + cmd = cmd+['-b', filename if filename else self.board_file] + cmd = cmd+['-c', self.yaml_file] + cmd = cmd+['-d', self.output_dir] + if extra is not None: + cmd = cmd+extra + logging.debug(cmd) + out_filename = self.get_out_path('output.txt') + err_filename = self.get_out_path('error.txt') + if use_a_tty: + # This is used to test the coloured logs, we need stderr to be a TTY + master, slave = openpty() + f_err = slave + f_out = slave + else: + # Redirect stdout and stderr to files + f_out = os.open(out_filename, os.O_RDWR | os.O_CREAT) + f_err = os.open(err_filename, os.O_RDWR | os.O_CREAT) + # Run the process + process = subprocess.Popen(cmd, stdout=f_out, stderr=f_err) + ret_code = process.wait() + logging.debug('ret_code '+str(ret_code)) + if use_a_tty: + self.err = os.read(master, 10000) + self.err = self.err.decode() + self.out = self.err + exp_ret = 0 if ret_val is None else ret_val + assert ret_code == exp_ret + if use_a_tty: + os.close(master) + os.close(slave) + with open(out_filename, 'w') as f: + f.write(self.out) + with open(err_filename, 'w') as f: + f.write(self.out) + else: + # Read stdout + os.lseek(f_out, 0, os.SEEK_SET) + self.out = os.read(f_out, 10000) + os.close(f_out) + self.out = self.out.decode() + # Read stderr + os.lseek(f_err, 0, os.SEEK_SET) + self.err = os.read(f_err, 10000) + os.close(f_err) + self.err = self.err.decode() + + def search_out(self, text): + m = re.search(text, self.out, re.MULTILINE) + return m + + def search_err(self, text): + m = re.search(text, self.err, re.MULTILINE) + return m + + def search_in_file(self, file, texts): + logging.debug('Searching in "'+file+'" output') + with open(self.get_out_path(file)) as f: + txt = f.read() + res = [] + for t in texts: + logging.debug('- r"'+t+'"') + m = re.search(t, txt, re.MULTILINE) + assert m + # logging.debug(' '+m.group(0)) + res.append(m.groups()) + return res + + def search_not_in_file(self, file, texts): + logging.debug('Searching not in "'+file+'" output') + with open(self.get_out_path(file)) as f: + txt = f.read() + for t in texts: + logging.debug('- r"'+t+'"') + m = re.search(t, txt, re.MULTILINE) + assert m is None + + def compare_image(self, image, reference=None, diff='diff.png'): + """ For images and single page PDFs """ + if reference is None: + reference = image + cmd = ['compare', '-metric', 'MSE', + self.get_out_path(image), + os.path.join(REF_DIR, reference), + self.get_out_path(diff)] + logging.debug('Comparing images with: '+str(cmd)) + res = subprocess.check_output(cmd, stderr=subprocess.STDOUT) + m = re.match(r'([\d\.]+) \(([\d\.]+)\)', res.decode()) + assert m + logging.debug('MSE={} ({})'.format(m.group(1), m.group(2))) + assert float(m.group(2)) == 0.0 + + def compare_pdf(self, gen, reference=None, diff='diff-{}.png'): + """ For multi-page PDFs """ + if reference is None: + reference = gen + logging.debug('Comparing PDFs: '+gen+' vs '+reference) + # Split the reference + logging.debug('Splitting '+reference) + cmd = ['convert', '-density', '150', + os.path.join(REF_DIR, reference), + self.get_out_path('ref-%d.png')] + subprocess.check_call(cmd) + # Split the generated + logging.debug('Splitting '+gen) + cmd = ['convert', '-density', '150', + self.get_out_path(gen), + self.get_out_path('gen-%d.png')] + subprocess.check_call(cmd) + # Chek number of pages + ref_pages = glob(self.get_out_path('ref-*.png')) + gen_pages = glob(self.get_out_path('gen-*.png')) + logging.debug('Pages {} vs {}'.format(len(gen_pages), len(ref_pages))) + assert len(ref_pages) == len(gen_pages) + # Compare each page + for page in range(len(ref_pages)): + cmd = ['compare', '-metric', 'MSE', + self.get_out_path('ref-'+str(page)+'.png'), + self.get_out_path('gen-'+str(page)+'.png'), + self.get_out_path(diff.format(page))] + logging.debug('Comparing images with: '+str(cmd)) + res = subprocess.check_output(cmd, stderr=subprocess.STDOUT) + m = re.match(r'([\d\.]+) \(([\d\.]+)\)', res.decode()) + assert m + logging.debug('MSE={} ({})'.format(m.group(1), m.group(2))) + assert float(m.group(2)) == 0.0 + + def compare_txt(self, text, reference=None, diff='diff.txt'): + if reference is None: + reference = text + cmd = ['/bin/sh', '-c', 'diff -ub '+os.path.join(REF_DIR, reference)+' ' + + self.get_out_path(text)+' > '+self.get_out_path(diff)] + logging.debug('Comparing texts with: '+str(cmd)) + res = subprocess.call(cmd) + assert res == 0 + + def filter_txt(self, file, pattern, repl): + fname = self.get_out_path(file) + with open(fname) as f: + txt = f.read() + with open(fname, 'w') as f: + f.write(re.sub(pattern, repl, txt)) + + +class TestContextSCH(TestContext): + + def __init__(self, test_name, board_name, yaml_name): + super().__init__(test_name, board_name, yaml_name) + self.mode = MODE_SCH + self._get_board_file() diff --git a/tests/yaml_samples/simple_position.yaml b/tests/yaml_samples/simple_position.kiplot.yaml similarity index 100% rename from tests/yaml_samples/simple_position.yaml rename to tests/yaml_samples/simple_position.kiplot.yaml diff --git a/tests/yaml_samples/simple_position_inches.yaml b/tests/yaml_samples/simple_position_inches.kiplot.yaml similarity index 100% rename from tests/yaml_samples/simple_position_inches.yaml rename to tests/yaml_samples/simple_position_inches.kiplot.yaml diff --git a/tests/yaml_samples/simple_position_unified.yaml b/tests/yaml_samples/simple_position_unified.kiplot.yaml similarity index 100% rename from tests/yaml_samples/simple_position_unified.yaml rename to tests/yaml_samples/simple_position_unified.kiplot.yaml diff --git a/tests/yaml_samples/simple_position_unified_th.yaml b/tests/yaml_samples/simple_position_unified_th.kiplot.yaml similarity index 100% rename from tests/yaml_samples/simple_position_unified_th.yaml rename to tests/yaml_samples/simple_position_unified_th.kiplot.yaml