diff options
Diffstat (limited to 'gerbonara/gerber/tests/test_excellon.py')
-rw-r--r-- | gerbonara/gerber/tests/test_excellon.py | 366 |
1 files changed, 366 insertions, 0 deletions
diff --git a/gerbonara/gerber/tests/test_excellon.py b/gerbonara/gerber/tests/test_excellon.py new file mode 100644 index 0000000..d6e83cc --- /dev/null +++ b/gerbonara/gerber/tests/test_excellon.py @@ -0,0 +1,366 @@ +#! /usr/bin/env python +# -*- coding: utf-8 -*- + +# Author: Hamilton Kibbe <ham@hamiltonkib.be> +import os +import pytest + +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, "rU") 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, "rU") as src: + srclines = src.readlines() + with open("test.ncd", "rU") 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 + + +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 + ) |