diff options
Diffstat (limited to 'gerbonara/gerber/tests/test_excellon.py')
-rw-r--r-- | gerbonara/gerber/tests/test_excellon.py | 404 |
1 files changed, 47 insertions, 357 deletions
diff --git a/gerbonara/gerber/tests/test_excellon.py b/gerbonara/gerber/tests/test_excellon.py index ef34f67..545bec9 100644 --- a/gerbonara/gerber/tests/test_excellon.py +++ b/gerbonara/gerber/tests/test_excellon.py @@ -1,366 +1,56 @@ #! /usr/bin/env python # -*- coding: utf-8 -*- - -# Author: Hamilton Kibbe <ham@hamiltonkib.be> +# Author: Jan Götte <code@jaseg.de> import os +import re +import math +import functools +import tempfile +import shutil +from argparse import Namespace +from itertools import chain +from pathlib import Path +from contextlib import contextmanager +from PIL import Image + import pytest +from ..excellon import ExcellonFile from ..cam import FileSettings -from ..excellon import read, detect_excellon_format, ExcellonFile, ExcellonParser -from ..excellon import DrillHit, DrillSlot -from ..excellon_statements import ExcellonTool, RouteModeStmt - - -NCDRILL_FILE = os.path.join(os.path.dirname(__file__), "resources/ncdrill.DRD") - - -def test_format_detection(): - """ Test file type detection - """ - with open(NCDRILL_FILE, "r") as f: - data = f.read() - settings = detect_excellon_format(data) - assert settings["format"] == (2, 4) - assert settings["zeros"] == "trailing" - - settings = detect_excellon_format(filename=NCDRILL_FILE) - assert settings["format"] == (2, 4) - assert settings["zeros"] == "trailing" - - -def test_read(): - ncdrill = read(NCDRILL_FILE) - assert isinstance(ncdrill, ExcellonFile) - - -def test_write(): - ncdrill = read(NCDRILL_FILE) - ncdrill.write("test.ncd") - with open(NCDRILL_FILE, "r") as src: - srclines = src.readlines() - with open("test.ncd", "r") as res: - for idx, line in enumerate(res): - assert line.strip() == srclines[idx].strip() - os.remove("test.ncd") - - -def test_read_settings(): - ncdrill = read(NCDRILL_FILE) - assert ncdrill.settings["format"] == (2, 4) - assert ncdrill.settings["zeros"] == "trailing" - - -def test_bounding_box(): - ncdrill = read(NCDRILL_FILE) - xbound, ybound = ncdrill.bounding_box - pytest.approx(xbound, (0.1300, 2.1430)) - pytest.approx(ybound, (0.3946, 1.7164)) - - -def test_report(): - ncdrill = read(NCDRILL_FILE) - rprt = ncdrill.report() - - -def test_conversion(): - import copy - - ncdrill = read(NCDRILL_FILE) - assert ncdrill.settings.units == "inch" - ncdrill_inch = copy.deepcopy(ncdrill) - - ncdrill.to_metric() - assert ncdrill.settings.units == "metric" - for tool in iter(ncdrill_inch.tools.values()): - tool.to_metric() - - for statement in ncdrill_inch.statements: - statement.to_metric() - - for m_tool, i_tool in zip( - iter(ncdrill.tools.values()), iter(ncdrill_inch.tools.values()) - ): - assert i_tool == m_tool - - for m, i in zip(ncdrill.primitives, ncdrill_inch.primitives): - - assert m.position == i.position, "%s not equal to %s" % (m, i) - assert m.diameter == i.diameter, "%s not equal to %s" % (m, i) - - -def test_parser_hole_count(): - settings = FileSettings(**detect_excellon_format(NCDRILL_FILE)) - p = ExcellonParser(settings) - p.parse(NCDRILL_FILE) - assert p.hole_count == 36 - - -def test_parser_hole_sizes(): - settings = FileSettings(**detect_excellon_format(NCDRILL_FILE)) - p = ExcellonParser(settings) - p.parse(NCDRILL_FILE) - assert p.hole_sizes == [0.0236, 0.0354, 0.04, 0.126, 0.128] - - -def test_parse_whitespace(): - p = ExcellonParser(FileSettings()) - assert p._parse_line(" ") == None - - -def test_parse_comment(): - p = ExcellonParser(FileSettings()) - p._parse_line(";A comment") - assert p.statements[0].comment == "A comment" - - -def test_parse_format_comment(): - p = ExcellonParser(FileSettings()) - p._parse_line("; FILE_FORMAT=9:9 ") - assert p.format == (9, 9) - - -def test_parse_header(): - p = ExcellonParser(FileSettings()) - p._parse_line("M48 ") - assert p.state == "HEADER" - p._parse_line("M95 ") - assert p.state == "DRILL" - - -def test_parse_rout(): - p = ExcellonParser(FileSettings()) - p._parse_line("G00X040944Y019842") - assert p.state == "ROUT" - p._parse_line("G05 ") - assert p.state == "DRILL" - - -def test_parse_version(): - p = ExcellonParser(FileSettings()) - p._parse_line("VER,1 ") - assert p.statements[0].version == 1 - p._parse_line("VER,2 ") - assert p.statements[1].version == 2 - - -def test_parse_format(): - p = ExcellonParser(FileSettings()) - p._parse_line("FMAT,1 ") - assert p.statements[0].format == 1 - p._parse_line("FMAT,2 ") - assert p.statements[1].format == 2 - - -def test_parse_units(): - settings = FileSettings(units="inch", zeros="trailing") - p = ExcellonParser(settings) - p._parse_line(";METRIC,LZ") - assert p.units == "inch" - assert p.zeros == "trailing" - p._parse_line("METRIC,LZ") - assert p.units == "metric" - assert p.zeros == "leading" - - -def test_parse_incremental_mode(): - settings = FileSettings(units="inch", zeros="trailing") - p = ExcellonParser(settings) - assert p.notation == "absolute" - p._parse_line("ICI,ON ") - assert p.notation == "incremental" - p._parse_line("ICI,OFF ") - assert p.notation == "absolute" - - -def test_parse_absolute_mode(): - settings = FileSettings(units="inch", zeros="trailing") - p = ExcellonParser(settings) - assert p.notation == "absolute" - p._parse_line("ICI,ON ") - assert p.notation == "incremental" - p._parse_line("G90 ") - assert p.notation == "absolute" - - -def test_parse_repeat_hole(): - p = ExcellonParser(FileSettings()) - p.active_tool = ExcellonTool(FileSettings(), number=8) - p._parse_line("R03X1.5Y1.5") - assert p.statements[0].count == 3 - - -def test_parse_incremental_position(): - p = ExcellonParser(FileSettings(notation="incremental")) - p._parse_line("X01Y01") - p._parse_line("X01Y01") - assert p.pos == [2.0, 2.0] - - -def test_parse_unknown(): - p = ExcellonParser(FileSettings()) - p._parse_line("Not A Valid Statement") - assert p.statements[0].stmt == "Not A Valid Statement" - - -def test_drill_hit_units_conversion(): - """ Test unit conversion for drill hits - """ - # Inch hit - settings = FileSettings(units="inch") - tool = ExcellonTool(settings, diameter=1.0) - hit = DrillHit(tool, (1.0, 1.0)) - - assert hit.tool.settings.units == "inch" - assert hit.tool.diameter == 1.0 - assert hit.position == (1.0, 1.0) - - # No Effect - hit.to_inch() - - assert hit.tool.settings.units == "inch" - assert hit.tool.diameter == 1.0 - assert hit.position == (1.0, 1.0) - - # Should convert - hit.to_metric() - - assert hit.tool.settings.units == "metric" - assert hit.tool.diameter == 25.4 - assert hit.position == (25.4, 25.4) - - # No Effect - hit.to_metric() - - assert hit.tool.settings.units == "metric" - assert hit.tool.diameter == 25.4 - assert hit.position == (25.4, 25.4) - - # Convert back to inch - hit.to_inch() - - assert hit.tool.settings.units == "inch" - assert hit.tool.diameter == 1.0 - assert hit.position == (1.0, 1.0) - - -def test_drill_hit_offset(): - TEST_VECTORS = [ - ((0.0, 0.0), (0.0, 1.0), (0.0, 1.0)), - ((0.0, 0.0), (1.0, 1.0), (1.0, 1.0)), - ((1.0, 1.0), (0.0, -1.0), (1.0, 0.0)), - ((1.0, 1.0), (-1.0, -1.0), (0.0, 0.0)), - ] - for position, offset, expected in TEST_VECTORS: - settings = FileSettings(units="inch") - tool = ExcellonTool(settings, diameter=1.0) - hit = DrillHit(tool, position) - - assert hit.position == position - - hit.offset(offset[0], offset[1]) - - assert hit.position == expected - - -def test_drill_slot_units_conversion(): - """ Test unit conversion for drill hits - """ - # Inch hit - settings = FileSettings(units="inch") - tool = ExcellonTool(settings, diameter=1.0) - hit = DrillSlot(tool, (1.0, 1.0), (10.0, 10.0), DrillSlot.TYPE_ROUT) - - assert hit.tool.settings.units == "inch" - assert hit.tool.diameter == 1.0 - assert hit.start == (1.0, 1.0) - assert hit.end == (10.0, 10.0) - - # No Effect - hit.to_inch() - - assert hit.tool.settings.units == "inch" - assert hit.tool.diameter == 1.0 - assert hit.start == (1.0, 1.0) - assert hit.end == (10.0, 10.0) - - # Should convert - hit.to_metric() - - assert hit.tool.settings.units == "metric" - assert hit.tool.diameter == 25.4 - assert hit.start == (25.4, 25.4) - assert hit.end == (254.0, 254.0) - - # No Effect - hit.to_metric() - - assert hit.tool.settings.units == "metric" - assert hit.tool.diameter == 25.4 - assert hit.start == (25.4, 25.4) - assert hit.end == (254.0, 254.0) - - # Convert back to inch - hit.to_inch() - - assert hit.tool.settings.units == "inch" - assert hit.tool.diameter == 1.0 - assert hit.start == (1.0, 1.0) - assert hit.end == (10.0, 10.0) - - -def test_drill_slot_offset(): - TEST_VECTORS = [ - ((0.0, 0.0), (1.0, 1.0), (0.0, 0.0), (0.0, 0.0), (1.0, 1.0)), - ((0.0, 0.0), (1.0, 1.0), (1.0, 0.0), (1.0, 0.0), (2.0, 1.0)), - ((0.0, 0.0), (1.0, 1.0), (1.0, 1.0), (1.0, 1.0), (2.0, 2.0)), - ((0.0, 0.0), (1.0, 1.0), (-1.0, 1.0), (-1.0, 1.0), (0.0, 2.0)), - ] - for start, end, offset, expected_start, expected_end in TEST_VECTORS: - settings = FileSettings(units="inch") - tool = ExcellonTool(settings, diameter=1.0) - slot = DrillSlot(tool, start, end, DrillSlot.TYPE_ROUT) - - assert slot.start == start - assert slot.end == end - - slot.offset(offset[0], offset[1]) - - assert slot.start == expected_start - assert slot.end == expected_end - - -def test_drill_slot_bounds(): - TEST_VECTORS = [ - ((0.0, 0.0), (1.0, 1.0), 1.0, ((-0.5, 1.5), (-0.5, 1.5))), - ((0.0, 0.0), (1.0, 1.0), 0.5, ((-0.25, 1.25), (-0.25, 1.25))), - ] - for start, end, diameter, expected in TEST_VECTORS: - settings = FileSettings(units="inch") - tool = ExcellonTool(settings, diameter=diameter) - slot = DrillSlot(tool, start, end, DrillSlot.TYPE_ROUT) - assert slot.bounding_box == expected +from .image_support import * +from .utils import * + +REFERENCE_FILES = [ + 'easyeda/Gerber_Drill_NPTH.DRL', + 'easyeda/Gerber_Drill_PTH.DRL', + 'allegro-2/MinnowMax_RevA1_IPC356A.ipc', + 'altium-composite-drill/NC Drill/LimeSDR-QPCIe_1v2-SlotHoles.TXT', + 'altium-composite-drill/NC Drill/LimeSDR-QPCIe_1v2-RoundHoles.TXT', + 'pcb-rnd/power-art.xln', + 'siemens/80101_0125_F200_ThruHoleNonPlated.ncd', + 'siemens/80101_0125_F200_ThruHolePlated.ncd', + 'siemens/80101_0125_F200_ContourPlated.ncd', + 'Target3001/IRNASIoTbank1.2.Drill', + 'altium-old-composite-drill.txt', + 'fritzing/combined.txt', + 'ncdrill.DRD', + 'upverter/design_export.drl', + 'diptrace/mainboard.drl', + 'diptrace/panel.drl', + 'diptrace/keyboard.drl', + ] + +@filter_syntax_warnings +@pytest.mark.parametrize('reference', REFERENCE_FILES, indirect=True) +def test_round_trip(reference, tmpfile): + tmp = tmpfile('Output excellon', '.drl') + + ExcellonFile.open(reference).save(tmp) + + mean, _max, hist = excellon_difference(reference, tmp, diff_out=tmpfile('Difference', '.png')) + assert mean < 5e-5 + assert hist[9] == 0 + assert hist[3:].sum() < 5e-5*hist.size -def test_handling_multi_line_g00_and_g1(): - """Route Mode statements with coordinates on separate line are handled - """ - test_data = """ -% -M48 -M72 -T01C0.0236 -% -T01 -G00 -X040944Y019842 -M15 -G01 -X040944Y020708 -M16 -""" - uut = ExcellonParser() - uut.parse_raw(test_data) - assert ( - len([stmt for stmt in uut.statements if isinstance(stmt, RouteModeStmt)]) == 2 - ) |