summaryrefslogtreecommitdiff
path: root/gerbonara/gerber/tests/test_excellon.py
diff options
context:
space:
mode:
Diffstat (limited to 'gerbonara/gerber/tests/test_excellon.py')
-rw-r--r--gerbonara/gerber/tests/test_excellon.py404
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
- )