summaryrefslogtreecommitdiff
path: root/gerbonara/gerber/tests/test_rs274x.py
diff options
context:
space:
mode:
Diffstat (limited to 'gerbonara/gerber/tests/test_rs274x.py')
-rw-r--r--gerbonara/gerber/tests/test_rs274x.py211
1 files changed, 163 insertions, 48 deletions
diff --git a/gerbonara/gerber/tests/test_rs274x.py b/gerbonara/gerber/tests/test_rs274x.py
index 28ee891..d91609f 100644
--- a/gerbonara/gerber/tests/test_rs274x.py
+++ b/gerbonara/gerber/tests/test_rs274x.py
@@ -4,22 +4,28 @@
# Author: Hamilton Kibbe <ham@hamiltonkib.be>
import os
import re
-import pytest
+import math
import functools
import tempfile
import shutil
from argparse import Namespace
+from itertools import chain
from pathlib import Path
+import pytest
+
from ..rs274x import GerberFile
+from ..cam import FileSettings
from .image_support import gerber_difference
+deg_to_rad = lambda a: a/180 * math.pi
+
fail_dir = Path('gerbonara_test_failures')
@pytest.fixture(scope='session', autouse=True)
def clear_failure_dir(request):
- for f in fail_dir.glob('*.gbr'):
+ for f in chain(fail_dir.glob('*.gbr'), fail_dir.glob('*.png')):
f.unlink()
reference_path = lambda reference: Path(__file__).parent / 'resources' / reference
@@ -42,61 +48,170 @@ def temp_files(request):
shutil.copy(tmp_out_gbr.name, perm_path_gbr)
shutil.copy(tmp_out_png.name, perm_path_png)
print(f'Failing output saved to {perm_path_gbr}')
- print(f'Difference image saved to {perm_path_png}')
print(f'Reference file is {reference_path(request.node.funcargs["reference"])}')
+ print(f'Difference image saved to {perm_path_png}')
+ print(f'gerbv command line:')
+ print(f'gerbv {perm_path_gbr} {reference_path(request.node.funcargs["reference"])}')
+
+to_gerbv_svg_units = lambda val, unit='mm': val*72 if unit == 'inch' else val/25.4*72
+
+REFERENCE_FILES = [ l.strip() for l in '''
+ board_outline.GKO
+ example_outline_with_arcs.gbr
+ example_two_square_boxes.gbr
+ example_coincident_hole.gbr
+ example_cutin.gbr
+ example_cutin_multiple.gbr
+ example_flash_circle.gbr
+ example_flash_obround.gbr
+ example_flash_polygon.gbr
+ example_flash_rectangle.gbr
+ example_fully_coincident.gbr
+ example_guess_by_content.g0
+ example_holes_dont_clear.gbr
+ example_level_holes.gbr
+ example_not_overlapping_contour.gbr
+ example_not_overlapping_touching.gbr
+ example_overlapping_contour.gbr
+ example_overlapping_touching.gbr
+ example_simple_contour.gbr
+ example_single_contour_1.gbr
+ example_single_contour_2.gbr
+ example_single_contour_3.gbr
+ example_am_exposure_modifier.gbr
+ bottom_copper.GBL
+ bottom_mask.GBS
+ bottom_silk.GBO
+ eagle_files/copper_bottom_l4.gbr
+ eagle_files/copper_inner_l2.gbr
+ eagle_files/copper_inner_l3.gbr
+ eagle_files/copper_top_l1.gbr
+ eagle_files/profile.gbr
+ eagle_files/silkscreen_bottom.gbr
+ eagle_files/silkscreen_top.gbr
+ eagle_files/soldermask_bottom.gbr
+ eagle_files/soldermask_top.gbr
+ eagle_files/solderpaste_bottom.gbr
+ eagle_files/solderpaste_top.gbr
+ multiline_read.ger
+ test_fine_lines_x.gbr
+ test_fine_lines_y.gbr
+ top_copper.GTL
+ top_mask.GTS
+ top_silk.GTO
+'''.splitlines() if l ]
+
+MIN_REFERENCE_FILES = [
+ 'example_two_square_boxes.gbr',
+ 'example_outline_with_arcs.gbr',
+ 'example_flash_circle.gbr',
+ 'example_flash_polygon.gbr',
+ 'example_flash_rectangle.gbr',
+ 'example_simple_contour.gbr',
+ 'example_am_exposure_modifier.gbr',
+ 'bottom_copper.GBL',
+ 'bottom_silk.GBO',
+ 'eagle_files/copper_bottom_l4.gbr'
+ ]
@pytest.mark.filterwarnings('ignore:Deprecated.*statement found.*:DeprecationWarning')
@pytest.mark.filterwarnings('ignore::SyntaxWarning')
-@pytest.mark.parametrize('reference', [ l.strip() for l in '''
-board_outline.GKO
-example_outline_with_arcs.gbr
-example_two_square_boxes.gbr
-example_coincident_hole.gbr
-example_cutin.gbr
-example_cutin_multiple.gbr
-example_flash_circle.gbr
-example_flash_obround.gbr
-example_flash_polygon.gbr
-example_flash_rectangle.gbr
-example_fully_coincident.gbr
-example_guess_by_content.g0
-example_holes_dont_clear.gbr
-example_level_holes.gbr
-example_not_overlapping_contour.gbr
-example_not_overlapping_touching.gbr
-example_overlapping_contour.gbr
-example_overlapping_touching.gbr
-example_simple_contour.gbr
-example_single_contour_1.gbr
-example_single_contour_2.gbr
-example_single_contour_3.gbr
-example_am_exposure_modifier.gbr
-bottom_copper.GBL
-bottom_mask.GBS
-bottom_silk.GBO
-eagle_files/copper_bottom_l4.gbr
-eagle_files/copper_inner_l2.gbr
-eagle_files/copper_inner_l3.gbr
-eagle_files/copper_top_l1.gbr
-eagle_files/profile.gbr
-eagle_files/silkscreen_bottom.gbr
-eagle_files/silkscreen_top.gbr
-eagle_files/soldermask_bottom.gbr
-eagle_files/soldermask_top.gbr
-eagle_files/solderpaste_bottom.gbr
-eagle_files/solderpaste_top.gbr
-multiline_read.ger
-test_fine_lines_x.gbr
-test_fine_lines_y.gbr
-top_copper.GTL
-top_mask.GTS
-top_silk.GTO
-'''.splitlines() if l ])
+@pytest.mark.parametrize('reference', REFERENCE_FILES)
def test_round_trip(temp_files, reference):
tmp_gbr, tmp_png = temp_files
ref = reference_path(reference)
+
GerberFile.open(ref).save(tmp_gbr)
+
mean, max = gerber_difference(ref, tmp_gbr, diff_out=tmp_png)
assert mean < 1e-6
assert max < 0.1
+TEST_ANGLES = [90, 180, 270, 30, 1.5, 10, 360, 1024, -30, -90]
+TEST_OFFSETS = [(0, 0), (100, 0), (0, 100), (2, 0), (10, 100)]
+
+@pytest.mark.filterwarnings('ignore:Deprecated.*statement found.*:DeprecationWarning')
+@pytest.mark.filterwarnings('ignore::SyntaxWarning')
+@pytest.mark.parametrize('reference', MIN_REFERENCE_FILES)
+@pytest.mark.parametrize('angle', TEST_ANGLES)
+def test_rotation(temp_files, reference, angle):
+ if 'flash_rectangle' in reference and angle == 1024:
+ # gerbv's rendering of this is broken, the hole is missing.
+ return
+
+ tmp_gbr, tmp_png = temp_files
+ ref = reference_path(reference)
+
+ f = GerberFile.open(ref)
+ f.rotate(deg_to_rad(angle))
+ f.save(tmp_gbr)
+
+ cx, cy = 0, to_gerbv_svg_units(10, unit='inch')
+ mean, max = gerber_difference(ref, tmp_gbr, diff_out=tmp_png, svg_transform=f'rotate({angle} {cx} {cy})')
+ assert mean < 1e-3 # relax mean criterion compared to above.
+ assert max < 0.9
+
+@pytest.mark.filterwarnings('ignore:Deprecated.*statement found.*:DeprecationWarning')
+@pytest.mark.filterwarnings('ignore::SyntaxWarning')
+@pytest.mark.parametrize('reference', MIN_REFERENCE_FILES)
+@pytest.mark.parametrize('angle', TEST_ANGLES)
+@pytest.mark.parametrize('center', [(0, 0), (-10, -10), (10, 10), (10, 0), (0, -10), (-10, 10), (10, 20)])
+def test_rotation_center(temp_files, reference, angle, center):
+ tmp_gbr, tmp_png = temp_files
+ ref = reference_path(reference)
+
+ f = GerberFile.open(ref)
+ f.rotate(deg_to_rad(angle), center=center)
+ f.save(tmp_gbr)
+
+ # calculate circle center in SVG coordinates
+ size = (10, 10) # inches
+ cx, cy = to_gerbv_svg_units(center[0]), to_gerbv_svg_units(10, 'inch')-to_gerbv_svg_units(center[1], 'mm')
+ mean, max = gerber_difference(ref, tmp_gbr, diff_out=tmp_png,
+ svg_transform=f'rotate({angle} {cx} {cy})',
+ size=size)
+ assert mean < 1e-3
+ assert max < 0.9
+
+@pytest.mark.filterwarnings('ignore:Deprecated.*statement found.*:DeprecationWarning')
+@pytest.mark.filterwarnings('ignore::SyntaxWarning')
+@pytest.mark.parametrize('reference', MIN_REFERENCE_FILES)
+@pytest.mark.parametrize('offset', TEST_OFFSETS)
+def test_offset(temp_files, reference, offset):
+ tmp_gbr, tmp_png = temp_files
+ ref = reference_path(reference)
+
+ f = GerberFile.open(ref)
+ f.offset(*offset)
+ f.save(tmp_gbr, settings=FileSettings(unit=f.unit, number_format=(4,7)))
+
+ # flip y offset since svg's y axis is flipped compared to that of gerber
+ dx, dy = to_gerbv_svg_units(offset[0]), -to_gerbv_svg_units(offset[1])
+ mean, max = gerber_difference(ref, tmp_gbr, diff_out=tmp_png, svg_transform=f'translate({dx} {dy})')
+ assert mean < 1e-4
+ assert max < 0.9
+
+@pytest.mark.filterwarnings('ignore:Deprecated.*statement found.*:DeprecationWarning')
+@pytest.mark.filterwarnings('ignore::SyntaxWarning')
+@pytest.mark.parametrize('reference', MIN_REFERENCE_FILES)
+@pytest.mark.parametrize('angle', TEST_ANGLES)
+@pytest.mark.parametrize('center', [(0, 0), (10, 0), (0, -10), (10, 20)])
+@pytest.mark.parametrize('offset', [(0, 0), (100, 0), (0, 100), (100, 100), (100, 10)])
+def test_combined(temp_files, reference, angle, center, offset):
+ tmp_gbr, tmp_png = temp_files
+ ref = reference_path(reference)
+
+ f = GerberFile.open(ref)
+ f.rotate(deg_to_rad(angle), center=center)
+ f.offset(*offset)
+ f.save(tmp_gbr)
+
+ size = (10, 10) # inches
+ cx, cy = to_gerbv_svg_units(center[0]), to_gerbv_svg_units(10, 'inch')-to_gerbv_svg_units(center[1], 'mm')
+ dx, dy = to_gerbv_svg_units(offset[0]), -to_gerbv_svg_units(offset[1])
+ mean, max = gerber_difference(ref, tmp_gbr, diff_out=tmp_png,
+ svg_transform=f'rotate({anlge} {cx} {cy}) translate({dx} {dy})',
+ size=size)
+ assert mean < 1e-4
+ assert max < 0.9
+