Fix tests
This commit is contained in:
parent
9a6bc691cb
commit
6231f67139
9 changed files with 68 additions and 34 deletions
|
|
@ -67,6 +67,21 @@ class Coordinate(click.ParamType):
|
|||
except ValueError:
|
||||
self.fail(f'{value!r} is not a valid coordinate. A coordinate consists of exactly {self.dimension} comma-separate floating-point numbers.')
|
||||
|
||||
class Rotation(click.ParamType):
|
||||
name = 'rotation'
|
||||
|
||||
def convert(self, value, param, ctx):
|
||||
try:
|
||||
coords = map(float, value.split(','))
|
||||
if len(coords) not in (1, 3):
|
||||
raise ValueError()
|
||||
|
||||
theta, x, y, *_rest = *coords, 0, 0
|
||||
return theta, x, y
|
||||
|
||||
except ValueError:
|
||||
self.fail(f'{value!r} is not a valid rotation. A rotation is either a floating point angle ("[theta]"), or the same angle followed by comma-separated X and Y coordinates of the rotation center ("[theta],[cx],[cy]").')
|
||||
|
||||
|
||||
class Unit(click.Choice):
|
||||
name = 'unit'
|
||||
|
|
@ -109,7 +124,8 @@ def cli():
|
|||
@click.option('--force-zip', is_flag=True, help='''Force treating input path as a zip file (default: guess file type
|
||||
from extension and contents)''')
|
||||
@click.option('--top/--bottom', default=True, help='Which side of the board to render')
|
||||
@click.option('--command-line-units', type=Unit(), default=MM, help='Units for values given in other options. Default: millimeter')
|
||||
@click.option('--command-line-units', type=Unit(), default=MM, help='''Units for values given in other options. Default:
|
||||
millimeter''')
|
||||
@click.option('--margin', type=float, default=0.0, help='Add space around the board inside the viewport')
|
||||
@click.option('--force-bounds', help='Force SVG bounding box to value given as "min_x,min_y,max_x,max_y"')
|
||||
@click.option('--inkscape/--standard-svg', default=True, help='Export in Inkscape SVG format with layers and stuff.')
|
||||
|
|
@ -147,24 +163,31 @@ def render(inpath, outfile, format_warnings, input_map, use_builtin_name_rules,
|
|||
@click.option('--version', is_flag=True, callback=print_version, expose_value=False, is_eager=True)
|
||||
@click.option('--warnings', 'format_warnings', type=click.Choice(['default', 'ignore', 'once']), default='default',
|
||||
help='''Enable or disable file format warnings during parsing (default: on)''')
|
||||
@click.option('-t', '--transform', help='''Execute python transformation script on input. You have access to the functions
|
||||
translate(x, y), scale(factor) and rotate(angle, center_x?, center_y?), the bounding box variables x_min,
|
||||
y_min, x_max, y_max, width and height, and everything from python\'s built-in math module (e.g. pi, sqrt,
|
||||
sin). As convenience methods, center() and origin() are provided to center the board resp. move its
|
||||
bottom-left corner to the origin. Coordinates are given in --command-line-units, angles in degrees, and
|
||||
scale as a scale factor (as opposed to a percentage). Example: "translate(-10, 0); rotate(45, 0, 5)"''')
|
||||
@click.option('--command-line-units', type=Unit(), default=MM, help='Units for values given in other options. Default: millimeter')
|
||||
@click.option('-n', '--number-format', help='Override number format to use during export in "[integer digits].[decimal digits]" notation, e.g. "2.6".')
|
||||
@click.option('-t', '--transform', help='''Execute python transformation script on input. You have access to the
|
||||
functions translate(x, y), scale(factor) and rotate(angle, center_x?, center_y?), the bounding box
|
||||
variables x_min, y_min, x_max, y_max, width and height, and everything from python\'s built-in math module
|
||||
(e.g. pi, sqrt, sin). As convenience methods, center() and origin() are provided to center the board resp.
|
||||
move its bottom-left corner to the origin. Coordinates are given in --command-line-units, angles in
|
||||
degrees, and scale as a scale factor (as opposed to a percentage). Example: "translate(-10, 0); rotate(45,
|
||||
0, 5)"''')
|
||||
@click.option('--command-line-units', type=Unit(), default=MM, help='''Units for values given in other options. Default:
|
||||
millimeter''')
|
||||
@click.option('-n', '--number-format', help='''Override number format to use during export in "[integer digits].[decimal
|
||||
digits]" notation, e.g. "2.6".''')
|
||||
@click.option('-u', '--units', type=Unit(), help='Override export file units')
|
||||
@click.option('-z', '--zero-suppression', type=click.Choice(['off', 'leading', 'trailing']), help='Override export zero suppression setting. Note: The meaning of this value is like in the Gerber spec for both Gerber and Excellon files!')
|
||||
@click.option('--keep-comments/--drop-comments', help='Keep gerber comments. Note: Comments will be prepended to the start of file, and will not occur in their old position.')
|
||||
@click.option('-z', '--zero-suppression', type=click.Choice(['off', 'leading', 'trailing']), help='''Override export
|
||||
zero suppression setting. Note: The meaning of this value is like in the Gerber spec for both Gerber and
|
||||
Excellon files!''')
|
||||
@click.option('--keep-comments/--drop-comments', help='''Keep gerber comments. Note: Comments will be prepended to the
|
||||
start of file, and will not occur in their old position.''')
|
||||
@click.option('--reuse-input-settings', 'output_format', flag_value='reuse', help='''Use the same export settings as the
|
||||
input file instead of sensible defaults.''')
|
||||
@click.option('--default-settings', 'output_format', default=True, flag_value='defaults', help='''Use sensible defaults
|
||||
for the output file format settings (default).''')
|
||||
@click.option('--input-number-format', help='Override number format of input file (mostly useful for Excellon files)')
|
||||
@click.option('--input-units', type=Unit(), help='Override units of input file')
|
||||
@click.option('--input-zero-suppression', type=click.Choice(['off', 'leading', 'trailing']), help='Override zero suppression setting of input file')
|
||||
@click.option('--input-zero-suppression', type=click.Choice(['off', 'leading', 'trailing']), help='''Override zero
|
||||
suppression setting of input file''')
|
||||
@click.argument('infile')
|
||||
@click.argument('outfile')
|
||||
def rewrite(transform, command_line_units, number_format, units, zero_suppression, keep_comments, output_format,
|
||||
|
|
@ -258,15 +281,16 @@ def transform(transform, units, output_format, inpath, outpath,
|
|||
|
||||
|
||||
@cli.command()
|
||||
@click.option('--command-line-units', type=click.Choice(['metric', 'us-customary']), default='metric', help='Units for values given in --transform. Default: millimeter')
|
||||
@click.option('--command-line-units', type=Unit(), default=MM, help='Units for values given in --transform. Default:
|
||||
millimeter')
|
||||
@click.option('--warnings', 'format_warnings', type=click.Choice(['default', 'ignore', 'once']), default='default',
|
||||
help='''Enable or disable file format warnings during parsing (default: on)''')
|
||||
@click.option('--offset', multiple=True, type=Coordinate(), help="""Offset for the n'th file as a "x,y" string in unit
|
||||
given by --command-line-units (default: millimeter). Can be given multiple times, and the first option
|
||||
affects the first input, the second option affects the second input, and so on.""")
|
||||
@click.option('--rotation', multiple=True, type=int, help="""Rotation for the n'th file in degrees clockwise. Can be
|
||||
given multiple times, and the first option affects the first input, the second option affects the second
|
||||
input, and so on.""")
|
||||
@click.option('--rotation', multiple=True, type=Rotation(), help="""Rotation for the n'th file in degrees clockwise,
|
||||
optionally followed by comma-separated rotation center X and Y coordinates. Can be given multiple times,
|
||||
and the first option affects the first input, the second option affects the second input, and so on.""")
|
||||
@click.option('-m', '--input-map', type=click.Path(exists=True, path_type=Path), multiple=True, help='''Extend or
|
||||
override layer name mapping with name map from JSON file. This option can be given multiple times, in
|
||||
which case the n'th option affects only the n'th input, like with --offset and --rotation. The JSON file
|
||||
|
|
@ -300,13 +324,20 @@ def merge(inpath, outpath, offset, rotation, input_map, command_line_units, outp
|
|||
raise click.UsageError('More --offset, --rotation or --input-map options than input files')
|
||||
|
||||
offset = offset or (0, 0)
|
||||
rotation = rotation or 0
|
||||
theta, cx, cy = rotation or 0, 0, 0
|
||||
|
||||
overrides = json.loads(input_map.read_bytes()) if input_map else None
|
||||
with warnings.catch_warnings():
|
||||
warnings.simplefilter(format_warnings)
|
||||
|
||||
stack = LayerStack.open(p, overrides=overrides, autoguess=use_builtin_name_rules)
|
||||
|
||||
if not math.isclose(offset[0], 0, abs_tol=1e-3) and math.isclose(offset[1], 0, abs_tol=1e-3):
|
||||
stack.offset(*offset, command_line_units)
|
||||
|
||||
if not math.isclose(theta, 0, abs_tol=1e-2):
|
||||
stack.rotate(theta, cx, cy)
|
||||
|
||||
if target is None:
|
||||
target = stack
|
||||
else:
|
||||
|
|
|
|||
|
|
@ -326,17 +326,17 @@ class NetlistParser(object):
|
|||
|
||||
if name == 'UNITS':
|
||||
if value in ('CUST', 'CUST 0'):
|
||||
self.settings.units = Inch
|
||||
self.settings.unit = Inch
|
||||
self.settings.angle_unit = 'degree'
|
||||
self.has_unit = True
|
||||
|
||||
elif value == 'CUST 1':
|
||||
self.settings.units = MM
|
||||
self.settings.unit = MM
|
||||
self.settings.angle_unit = 'degree'
|
||||
self.has_unit = True
|
||||
|
||||
elif value == 'CUST 2':
|
||||
self.settings.units = Inch
|
||||
self.settings.unit = Inch
|
||||
self.settings.angle_unit = 'radian'
|
||||
self.has_unit = True
|
||||
|
||||
|
|
|
|||
|
|
@ -154,7 +154,7 @@ def common_prefix(l):
|
|||
|
||||
return sorted(out, key=len)[-1]
|
||||
|
||||
def autoguess(filenames):
|
||||
def do_autoguess(filenames):
|
||||
prefix = common_prefix([f.name for f in filenames])
|
||||
|
||||
matches = {}
|
||||
|
|
@ -313,7 +313,7 @@ class LayerStack:
|
|||
if sum(len(files) for files in filemap.values()) < 6:
|
||||
warnings.warn('Ambiguous gerber filenames. Trying last-resort autoguesser.')
|
||||
generator = None
|
||||
filemap = autoguess(files)
|
||||
filemap = do_autoguess(files)
|
||||
if len(filemap) < 6:
|
||||
raise ValueError('Cannot figure out gerber file mapping. Partial map is: ', filemap)
|
||||
|
||||
|
|
@ -338,13 +338,13 @@ class LayerStack:
|
|||
# Ignore if we can't find the param file -- maybe the user has convinced Allegro to actually put this
|
||||
# information into a comment, or maybe they have made Allegro just use decimal points like XNC does.
|
||||
|
||||
filemap = autoguess([ f for files in filemap.values() for f in files ])
|
||||
filemap = do_autoguess([ f for files in filemap.values() for f in files ])
|
||||
if len(filemap) < 6:
|
||||
raise SystemError('Cannot figure out gerber file mapping')
|
||||
# FIXME use layer metadata from comments and ipc file if available
|
||||
|
||||
elif generator == 'zuken':
|
||||
filemap = autoguess([ f for files in filemap.values() for f in files ])
|
||||
filemap = do_autoguess([ f for files in filemap.values() for f in files ])
|
||||
if len(filemap) < 6:
|
||||
raise SystemError('Cannot figure out gerber file mapping')
|
||||
# FIXME use layer metadata from comments and ipc file if available
|
||||
|
|
|
|||
|
|
@ -88,7 +88,7 @@ class GerberFile(CamFile):
|
|||
# dedup apertures
|
||||
new_apertures = {}
|
||||
replace_apertures = {}
|
||||
mock_settings = FileSettings()
|
||||
mock_settings = FileSettings.defaults()
|
||||
for ap in self.apertures + other.apertures:
|
||||
gbr = ap.to_gerber(mock_settings)
|
||||
if gbr not in new_apertures:
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ from pathlib import Path
|
|||
|
||||
import pytest
|
||||
|
||||
from .image_support import ImageDifference
|
||||
from .image_support import ImageDifference, run_cargo_cmd
|
||||
|
||||
def pytest_assertrepr_compare(op, left, right):
|
||||
if isinstance(left, ImageDifference) or isinstance(right, ImageDifference):
|
||||
|
|
@ -28,3 +28,8 @@ def pytest_sessionstart(session):
|
|||
# on coordinator
|
||||
for f in chain(fail_dir.glob('*.gbr'), fail_dir.glob('*.png')):
|
||||
f.unlink()
|
||||
|
||||
try:
|
||||
run_cargo_cmd('resvg', '--help')
|
||||
except FileNotFoundError:
|
||||
pytest.exit('resvg binary not found, aborting test.', 2)
|
||||
|
|
|
|||
|
|
@ -104,6 +104,7 @@ def gerbv_export(in_gbr, out_svg, export_format='svg', origin=(0, 0), size=(6, 6
|
|||
cachefile = cachedir / f'{digest}.svg'
|
||||
|
||||
if not cachefile.is_file():
|
||||
print(f'Building cache for {Path(in_gbr).name}')
|
||||
# NOTE: gerbv seems to always export 'clear' polarity apertures as white, irrespective of --foreground, --background
|
||||
# and project file color settings.
|
||||
# TODO: File issue upstream.
|
||||
|
|
@ -138,6 +139,8 @@ def gerbv_export(in_gbr, out_svg, export_format='svg', origin=(0, 0), size=(6, 6
|
|||
f'--foreground={fg}',
|
||||
'-o', str(cachefile), '-p', f.name]
|
||||
subprocess.run(cmd, check=True, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
|
||||
else:
|
||||
print(f'Re-using cache for {Path(in_gbr).name}')
|
||||
shutil.copy(cachefile, out_svg)
|
||||
|
||||
@contextmanager
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
G04 ex2: overlapping*
|
||||
%FSLAX24Y24*%
|
||||
%MOMM*%
|
||||
%SRX1Y1I0.000J0.000*%
|
||||
%ADD10C,1.00000*%
|
||||
G01*
|
||||
%LPD*%
|
||||
|
|
@ -21,4 +20,4 @@ X10000Y50000D01*
|
|||
G04 second fully coincident linear segment*
|
||||
X0D01*
|
||||
G37*
|
||||
M02*
|
||||
M02*
|
||||
|
|
|
|||
|
|
@ -31,8 +31,8 @@ from .utils import *
|
|||
from ..utils import Inch, MM
|
||||
|
||||
REFERENCE_FILES = {
|
||||
'easyeda/Gerber_Drill_NPTH.DRL': (None, None),
|
||||
'easyeda/Gerber_Drill_PTH.DRL': (None, 'easyeda/Gerber_TopLayer.GTL'),
|
||||
'easyeda/Gerber_Drill_NPTH.DRL': (('inch', 'leading', 4), None),
|
||||
'easyeda/Gerber_Drill_PTH.DRL': (('inch', 'leading', 4), 'easyeda/Gerber_TopLayer.GTL'),
|
||||
# Altium uses an excellon format specification format that gerbv doesn't understand, so we have to fix that.
|
||||
'altium-composite-drill/NC Drill/LimeSDR-QPCIe_1v2-SlotHoles.TXT': (('mm', 'trailing', 4), None),
|
||||
'altium-composite-drill/NC Drill/LimeSDR-QPCIe_1v2-RoundHoles.TXT': (('mm', 'trailing', 4), 'altium-composite-drill/Gerber/LimeSDR-QPCIe_1v2.GTL'),
|
||||
|
|
@ -57,7 +57,6 @@ REFERENCE_FILES = {
|
|||
def test_round_trip(reference, tmpfile):
|
||||
reference, (unit_spec, _) = reference
|
||||
tmp = tmpfile('Output excellon', '.drl')
|
||||
print('unit spec', unit_spec)
|
||||
|
||||
f = ExcellonFile.open(reference)
|
||||
f.save(tmp)
|
||||
|
|
@ -79,7 +78,6 @@ def test_first_level_idempotence_svg(reference, tmpfile):
|
|||
tmp = tmpfile('Output excellon', '.drl')
|
||||
ref_svg = tmpfile('Reference SVG render', '.svg')
|
||||
out_svg = tmpfile('Output SVG render', '.svg')
|
||||
print('unit spec', unit_spec)
|
||||
|
||||
a = ExcellonFile.open(reference)
|
||||
a.save(tmp)
|
||||
|
|
|
|||
|
|
@ -150,7 +150,6 @@ REFERENCE_FILES = [ l.strip() for l in '''
|
|||
pcb-rnd/power-art.gko
|
||||
pcb-rnd/power-art.ast
|
||||
pcb-rnd/power-art.gtl
|
||||
pcb-rnd/power-art.lht
|
||||
pcb-rnd/power-art.gto
|
||||
pcb-rnd/power-art.gtp
|
||||
pcb-rnd/power-art.asb
|
||||
|
|
@ -206,7 +205,6 @@ REFERENCE_FILES = [ l.strip() for l in '''
|
|||
siemens-2/Gerber/SolderPasteBottom.gdo
|
||||
siemens-2/Gerber/SolderPasteTop.gdo
|
||||
siemens-2/Gerber/EtchLayerBottom.gdo
|
||||
siemens-2/Gerber/GerberPlot.gpf
|
||||
siemens-2/Gerber/BoardOutlline.gdo
|
||||
upverter/design_export.gko
|
||||
upverter/design_export.gtl
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue