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.
This commit is contained in:
parent
450b0ab671
commit
d500178c24
|
|
@ -0,0 +1,10 @@
|
|||
[run]
|
||||
source =
|
||||
kiplot
|
||||
src
|
||||
|
||||
[report]
|
||||
exclude_lines =
|
||||
pragma: no cover
|
||||
# raise RuntimeError
|
||||
|
||||
|
|
@ -30,3 +30,7 @@ MANIFEST
|
|||
|
||||
*.kicad_pcb-bak
|
||||
*.epr
|
||||
htmlcov/
|
||||
pp/
|
||||
output/
|
||||
.coverage
|
||||
|
|
|
|||
2
Makefile
2
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
|
||||
|
|
|
|||
|
|
@ -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.")
|
||||
|
|
|
|||
|
|
@ -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'])
|
||||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
Loading…
Reference in New Issue