Added a simple test for drills

This commit is contained in:
Salvador E. Tropea 2020-05-14 19:50:04 -03:00
parent d500178c24
commit 6873652433
7 changed files with 200 additions and 63 deletions

View File

@ -5,7 +5,7 @@
(drawings 4)
(tracks 0)
(zones 0)
(modules 3)
(modules 4)
(nets 1)
)
@ -101,6 +101,25 @@
(uvia_drill 0.1)
)
(module MountingHole:MountingHole_2.1mm (layer F.Cu) (tedit 5B924765) (tstamp 5EBE1AA4)
(at 120 29)
(descr "Mounting Hole 2.1mm, no annular")
(tags "mounting hole 2.1mm no annular")
(attr virtual)
(fp_text reference REF** (at 0 -3.2) (layer F.SilkS)
(effects (font (size 1 1) (thickness 0.15)))
)
(fp_text value MountingHole_2.1mm (at 0 3.2) (layer F.Fab)
(effects (font (size 1 1) (thickness 0.15)))
)
(fp_circle (center 0 0) (end 2.35 0) (layer F.CrtYd) (width 0.05))
(fp_circle (center 0 0) (end 2.1 0) (layer Cmts.User) (width 0.15))
(fp_text user %R (at 0.3 0) (layer F.Fab)
(effects (font (size 1 1) (thickness 0.15)))
)
(pad "" np_thru_hole circle (at 0 0) (size 2.1 2.1) (drill 2.1) (layers *.Cu *.Mask))
)
(module Resistor_THT:R_Box_L13.0mm_W4.0mm_P9.00mm (layer F.Cu) (tedit 5AE5139B) (tstamp 5EA76EC0)
(at 110 45)
(descr "Resistor, Box series, Radial, pin pitch=9.00mm, 2W, length*width=13.0*4.0mm^2, http://www.produktinfo.conrad.com/datenblaetter/425000-449999/443860-da-01-de-METALLBAND_WIDERSTAND_0_1_OHM_5W_5Pr.pdf")

View File

@ -0,0 +1,76 @@
"""
Tests of drill files
The 3Rs.kicad_pcb has R1 on top, R2 on bottom and a thru-hole component R3 on top.
We test:
For debug information use:
pytest-3 --log-cli-level debug
"""
import os
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
DRILL_DIR = 'Drill'
positions = {'R1': (105, 35, 'top'), 'R2': (110, 35, 'bottom'), 'R3': (110, 45, 'top')}
def expect_position(ctx, file, comp, no_comp=[], inches=False):
"""
Check if a list of components are or aren't in the file
"""
# 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
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])
# 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_drill():
ctx = context.TestContext('3Rs_drill', '3Rs', 'drill', DRILL_DIR)
ctx.run()
# Check all outputs are there
ctx.expect_out_file(os.path.join(DRILL_DIR, 'report.rpt'))
pth_drl = ctx.get_pth_drl_filename()
ctx.expect_out_file(pth_drl)
npth_drl = ctx.get_npth_drl_filename()
ctx.expect_out_file(npth_drl)
pth_gbr_drl = ctx.get_pth_gbr_drl_filename()
ctx.expect_out_file(pth_gbr_drl)
npth_gbr_drl = ctx.get_npth_gbr_drl_filename()
ctx.expect_out_file(npth_gbr_drl)
pth_pdf_drl = ctx.get_pth_pdf_drl_filename()
ctx.expect_out_file(pth_pdf_drl)
npth_pdf_drl = ctx.get_npth_pdf_drl_filename()
ctx.expect_out_file(npth_pdf_drl)
# We have R3 at (110, 45) length is 9 mm on X, drill 1 mm
ctx.search_in_file(pth_drl, ['X110.0Y-45.0', 'X119.0Y-45.0'])
ctx.expect_gerber_flash_at(pth_gbr_drl, 6, (110, -45))
ctx.expect_gerber_has_apertures(pth_gbr_drl, ['C,1.000000'])
# We have a mounting hole at (120, 29) is 2.1 mm in diameter
ctx.search_in_file(npth_drl, ['X120.0Y-29.0', 'T1C2.100'])
ctx.expect_gerber_flash_at(npth_gbr_drl, 6, (120, -29))
ctx.expect_gerber_has_apertures(npth_gbr_drl, ['C,2.100000'])
ctx.clean_up()

View File

@ -53,10 +53,10 @@ def expect_position(ctx, file, comp, no_comp=[], inches=False):
def test_3Rs_position():
ctx = context.TestContext('3Rs_position', '3Rs', 'simple_position')
ctx = context.TestContext('3Rs_position', '3Rs', 'simple_position', POS_DIR)
ctx.run()
pos_top = ctx.get_pos_top_filename(POS_DIR)
pos_bot = ctx.get_pos_bot_filename(POS_DIR)
pos_top = ctx.get_pos_top_filename()
pos_bot = ctx.get_pos_bot_filename()
ctx.expect_out_file(pos_top)
ctx.expect_out_file(pos_bot)
expect_position(ctx, pos_top, ['R1'], ['R2', 'R3'])
@ -65,24 +65,24 @@ def test_3Rs_position():
def test_3Rs_position_unified():
ctx = context.TestContext('3Rs_position_unified', '3Rs', 'simple_position_unified')
ctx = context.TestContext('3Rs_position_unified', '3Rs', 'simple_position_unified', POS_DIR)
ctx.run()
expect_position(ctx, ctx.get_pos_both_filename(POS_DIR), ['R1', 'R2'], ['R3'])
expect_position(ctx, ctx.get_pos_both_filename(), ['R1', 'R2'], ['R3'])
ctx.clean_up()
def test_3Rs_position_unified_th():
ctx = context.TestContext('3Rs_position_unified_th', '3Rs', 'simple_position_unified_th')
ctx = context.TestContext('3Rs_position_unified_th', '3Rs', 'simple_position_unified_th', POS_DIR)
ctx.run()
expect_position(ctx, ctx.get_pos_both_filename(POS_DIR), ['R1', 'R2', 'R3'])
expect_position(ctx, ctx.get_pos_both_filename(), ['R1', 'R2', 'R3'])
ctx.clean_up()
def test_3Rs_position_inches():
ctx = context.TestContext('3Rs_position_inches', '3Rs', 'simple_position_inches')
ctx = context.TestContext('3Rs_position_inches', '3Rs', 'simple_position_inches', POS_DIR)
ctx.run()
pos_top = ctx.get_pos_top_filename(POS_DIR)
pos_bot = ctx.get_pos_bot_filename(POS_DIR)
pos_top = ctx.get_pos_top_filename()
pos_bot = ctx.get_pos_bot_filename()
ctx.expect_out_file(pos_top)
ctx.expect_out_file(pos_bot)
expect_position(ctx, pos_top, ['R1'], ['R2', 'R3'], True)

View File

@ -8,7 +8,6 @@ pytest-3 --log-cli-level debug
import os
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))
@ -16,49 +15,24 @@ sys.path.insert(0, os.path.dirname(script_dir))
from utils import context
def expect_gerber_has_apertures(ctx, file, ap_list):
ap_matches = []
for ap in ap_list:
# find the circular aperture for the outline
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(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))
ctx.search_in_file(file, [repat])
logging.debug("Gerber flash found: "+repat)
GERBER_DIR = 'gerberdir'
def test_2layer():
prj = 'simple_2layer'
ctx = context.TestContext('Simple_2_layer', prj, prj)
ctx = context.TestContext('Simple_2_layer', prj, prj, GERBER_DIR)
ctx.run()
g_dir = 'gerberdir'
f_cu = ctx.get_gerber_filename(g_dir, 'F_Cu')
f_cu = ctx.get_gerber_filename('F_Cu')
ctx.expect_out_file(f_cu)
ctx.expect_out_file(ctx.get_gerber_job_filename(g_dir))
ctx.expect_out_file(ctx.get_gerber_job_filename())
expect_gerber_has_apertures(ctx, f_cu, [
ctx.expect_gerber_has_apertures(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(ctx, f_cu, (140, -100))
ctx.expect_gerber_flash_at(f_cu, 5, (140, -100))
ctx.clean_up()

View File

@ -19,7 +19,7 @@ MODE_PCB = 0
class TestContext(object):
def __init__(self, test_name, board_name, yaml_name):
def __init__(self, test_name, board_name, yaml_name, sub_dir):
# We are using PCBs
self.mode = MODE_PCB
# The name used for the test output dirs and other logging
@ -32,6 +32,8 @@ class TestContext(object):
self._get_yaml_name(yaml_name)
# The actual output dir for this run
self._set_up_output_dir(pytest.config.getoption('test_dir'))
# Where are we expecting to get the outputs (inside test_name)
self.sub_dir = sub_dir
# stdout and stderr from the run
self.out = None
self.err = None
@ -77,20 +79,38 @@ class TestContext(object):
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_job_filename(self):
return os.path.join(self.sub_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_gerber_filename(self, layer_slug, ext='.gbr'):
return os.path.join(self.sub_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_top_filename(self):
return os.path.join(self.sub_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_bot_filename(self):
return os.path.join(self.sub_dir, self.board_name+'-bottom.pos')
def get_pos_both_filename(self, dir):
return os.path.join(dir, self.board_name+'-both.pos')
def get_pos_both_filename(self):
return os.path.join(self.sub_dir, self.board_name+'-both.pos')
def get_pth_drl_filename(self):
return os.path.join(self.sub_dir, self.board_name+'-PTH.drl')
def get_pth_gbr_drl_filename(self):
return os.path.join(self.sub_dir, self.board_name+'-PTH-drl.gbr')
def get_pth_pdf_drl_filename(self):
return os.path.join(self.sub_dir, self.board_name+'-PTH-drl_map.pdf')
def get_npth_drl_filename(self):
return os.path.join(self.sub_dir, self.board_name+'-NPTH.drl')
def get_npth_gbr_drl_filename(self):
return os.path.join(self.sub_dir, self.board_name+'-NPTH-drl.gbr')
def get_npth_pdf_drl_filename(self):
return os.path.join(self.sub_dir, self.board_name+'-NPTH-drl_map.pdf')
def expect_out_file(self, filename):
file = self.get_out_path(filename)
@ -254,6 +274,35 @@ class TestContext(object):
with open(fname, 'w') as f:
f.write(re.sub(pattern, repl, txt))
def expect_gerber_flash_at(self, file, res, pos):
"""
Check for a gerber flash at a given point
(it's hard to check that aperture is right without a real gerber parser
"""
if res == 6: # 4.6
mult = 1000000
else: # 4.5
mult = 100000
repat = r'^X{x}Y{y}D03\*$'.format(x=int(pos[0]*mult), y=int(pos[1]*mult))
self.search_in_file(file, [repat])
logging.debug("Gerber flash found: "+repat)
def expect_gerber_has_apertures(self, file, ap_list):
ap_matches = []
for ap in ap_list:
# find the circular aperture for the outline
ap_matches.append(r'%AD(.*)'+ap+r'\*%')
grps = self.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
class TestContextSCH(TestContext):

View File

@ -0,0 +1,28 @@
# Drills and Gerber drills
kiplot:
version: 1
outputs:
- name: excellon_drill
comment: "Excellon drill files"
type: excellon
dir: Drill
options:
metric_units: true
pth_and_npth_single_file: false
use_aux_axis_as_origin: false
minimal_header: false
mirror_y_axis: false
report:
filename: 'report.rpt'
map:
type: 'pdf'
- name: gerber_drills
comment: "Gerber drill files"
type: gerb_drill
dir: Drill
options:
use_aux_axis_as_origin: false

View File

@ -35,12 +35,3 @@ outputs:
- layer: F.SilkS
suffix: F_SilkS
- name: 'position'
comment: "Pick and place file"
type: position
dir: positiondir
options:
format: ASCII # CSV or ASCII format
units: millimeters # millimeters or inches
separate_files_for_front_and_back: true
only_smd: true