Manual merge resolution

This commit is contained in:
Chintalagiri Shashank 2019-11-27 15:11:27 +05:30
commit 5288a8314b
23 changed files with 2431 additions and 2110 deletions

View file

@ -1,4 +1,8 @@
[run]
branch = True
source = gerber
[report]
ignore_errors = True
omit =
*/python?.?/*
*/site-packages/nose/*
gerber/tests/*

View file

@ -1,6 +1,6 @@
name: pcb-tools
on: [push, pull_request]
on: [push]
jobs:
test:
@ -11,16 +11,34 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v1
with:
python-version: ${{ matrix.python-version }}
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -r requirements-dev.txt
- name: Test with pytest
run: |
pip install pytest
pytest
- uses: actions/checkout@v1
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v1
with:
python-version: ${{ matrix.python-version }}
- name: Install dependencies
run: |
pip install -r requirements-dev.txt
- name: Test with pytest
run: |
pytest
coverage:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- name: Set up Python 3.8
uses: actions/setup-python@v1
with:
python-version: 3.8
- name: Install dependencies
run: |
pip install -r requirements-dev.txt
- name: Run coverage
run: |
make test-coverage
- uses: codecov/codecov-action@v1
with:
token: ${{ secrets.CODECOV_TOKEN }}
file: ./coverage.xml
flags: unittest

1
.gitignore vendored
View file

@ -48,3 +48,4 @@ Thumbs.db
# Virtual environment
venv
coverage.xml

View file

@ -1,6 +1,6 @@
PYTHON ?= python
NOSETESTS ?= nosetests
PYTEST ?= pytest
DOC_ROOT = doc
EXAMPLES = examples
@ -8,17 +8,19 @@ EXAMPLES = examples
.PHONY: clean
clean: doc-clean
find . -name '*.pyc' -delete
rm -rf coverage .coverage
rm -rf *.egg-info
rm -f .coverage
rm -f coverage.xml
.PHONY: test
test:
$(NOSETESTS) -s -v gerber
$(PYTEST)
.PHONY: test-coverage
test-coverage:
rm -rf coverage .coverage
$(NOSETESTS) -s -v --with-coverage --cover-package=gerber
rm -f .coverage
rm -f coverage.xml
$(PYTEST) --cov=./ --cov-report=xml
.PHONY: install
install:

View file

@ -1,8 +1,8 @@
pcb-tools
============
[![Travis CI Build Status](https://travis-ci.org/curtacircuitos/pcb-tools.svg?branch=master)](https://travis-ci.org/curtacircuitos/pcb-tools)
[![Coverage Status](https://coveralls.io/repos/curtacircuitos/pcb-tools/badge.png?branch=master)](https://coveralls.io/r/curtacircuitos/pcb-tools?branch=master)
[![Documentation Status](https://readthedocs.org/projects/pcb-tools/badge/?version=latest)](https://readthedocs.org/projects/pcb-tools/?badge=latest)
[![CI](https://github.com/curtacircuitos/pcb-tools/workflows/pcb-tools/badge.svg)](https://github.com/curtacircuitos/pcb-tools/actions)
[![Coverage](https://codecov.io/gh/curtacircuitos/pcb-tools/branch/master/graph/badge.svg)](https://codecov.io/gh/curtacircuitos/pcb-tools)
[![Docs](https://readthedocs.org/projects/pcb-tools/badge/?version=latest)](https://readthedocs.org/projects/pcb-tools/?badge=latest)
Tools to handle Gerber and Excellon files in Python.
@ -55,6 +55,6 @@ Dependencies for developing and testing pcb-tools are listed in requirements-dev
(venv)$ pip install -r requirements-dev.txt
(venv)$ pip install -e .
We use nose to run pcb-tools's suite of unittests and doctests.
We use [pytest](https://docs.pytest.org/en/latest/) to run pcb-tools's suite of unittests and doctests.
(venv)$ nosetests
(venv)$ pytest

View file

@ -119,3 +119,4 @@ def main():
if __name__ == '__main__':
main()

View file

@ -94,6 +94,12 @@ class PCB(object):
layer.layer_class in
('top', 'bottom', 'internal')]))
@property
def outline_layer(self):
for layer in self.layers:
if layer.layer_class == 'outline':
return layer
@property
def layer_count(self):
""" Number of *COPPER* layers

View file

@ -23,6 +23,7 @@ COLORS = {
'white': (1.0, 1.0, 1.0),
'red': (1.0, 0.0, 0.0),
'green': (0.0, 1.0, 0.0),
'yellow': (1.0, 1.0, 0),
'blue': (0.0, 0.0, 1.0),
'fr-4': (0.290, 0.345, 0.0),
'green soldermask': (0.0, 0.412, 0.278),
@ -35,6 +36,18 @@ COLORS = {
}
SPECTRUM = [
(0.804, 0.216, 0),
(0.78, 0.776, 0.251),
(0.545, 0.451, 0.333),
(0.545, 0.137, 0.137),
(0.329, 0.545, 0.329),
(0.133, 0.545, 0.133),
(0, 0.525, 0.545),
(0.227, 0.373, 0.804),
]
class Theme(object):
def __init__(self, name=None, **kwargs):
@ -48,10 +61,22 @@ class Theme(object):
self.bottom = kwargs.get('bottom', RenderSettings(COLORS['hasl copper'], mirror=True))
self.drill = kwargs.get('drill', RenderSettings(COLORS['black']))
self.ipc_netlist = kwargs.get('ipc_netlist', RenderSettings(COLORS['red']))
self._internal = kwargs.get('internal', [RenderSettings(x) for x in SPECTRUM])
self._internal_gen = None
def __getitem__(self, key):
return getattr(self, key)
@property
def internal(self):
if not self._internal_gen:
self._internal_gen = self._internal_gen_func()
return next(self._internal_gen)
def _internal_gen_func(self):
for setting in self._internal:
yield setting
def get(self, key, noneval=None):
val = getattr(self, key, None)
return val if val is not None else noneval
@ -77,4 +102,11 @@ THEMES = {
top=RenderSettings(COLORS['red'], alpha=0.5),
bottom=RenderSettings(COLORS['blue'], alpha=0.5),
drill=RenderSettings((0.3, 0.3, 0.3))),
'Transparent Multilayer': Theme(name='Transparent Multilayer',
background=RenderSettings((0, 0, 0)),
top=RenderSettings(SPECTRUM[0], alpha=0.8),
bottom=RenderSettings(SPECTRUM[-1], alpha=0.8),
drill=RenderSettings((0.3, 0.3, 0.3)),
internal=[RenderSettings(x, alpha=0.5) for x in SPECTRUM[1:-1]]),
}

View file

@ -3,385 +3,393 @@
# Author: Hamilton Kibbe <ham@hamiltonkib.be>
from .tests import *
import pytest
from ..am_statements import *
from ..am_statements import inch, metric
def test_AMPrimitive_ctor():
for exposure in ('on', 'off', 'ON', 'OFF'):
for exposure in ("on", "off", "ON", "OFF"):
for code in (0, 1, 2, 4, 5, 6, 7, 20, 21, 22):
p = AMPrimitive(code, exposure)
assert_equal(p.code, code)
assert_equal(p.exposure, exposure.lower())
assert p.code == code
assert p.exposure == exposure.lower()
def test_AMPrimitive_validation():
assert_raises(TypeError, AMPrimitive, '1', 'off')
assert_raises(ValueError, AMPrimitive, 0, 'exposed')
assert_raises(ValueError, AMPrimitive, 3, 'off')
pytest.raises(TypeError, AMPrimitive, "1", "off")
pytest.raises(ValueError, AMPrimitive, 0, "exposed")
pytest.raises(ValueError, AMPrimitive, 3, "off")
def test_AMPrimitive_conversion():
p = AMPrimitive(4, 'on')
assert_raises(NotImplementedError, p.to_inch)
assert_raises(NotImplementedError, p.to_metric)
p = AMPrimitive(4, "on")
pytest.raises(NotImplementedError, p.to_inch)
pytest.raises(NotImplementedError, p.to_metric)
def test_AMCommentPrimitive_ctor():
c = AMCommentPrimitive(0, ' This is a comment *')
assert_equal(c.code, 0)
assert_equal(c.comment, 'This is a comment')
c = AMCommentPrimitive(0, " This is a comment *")
assert c.code == 0
assert c.comment == "This is a comment"
def test_AMCommentPrimitive_validation():
assert_raises(ValueError, AMCommentPrimitive, 1, 'This is a comment')
pytest.raises(ValueError, AMCommentPrimitive, 1, "This is a comment")
def test_AMCommentPrimitive_factory():
c = AMCommentPrimitive.from_gerber('0 Rectangle with rounded corners. *')
assert_equal(c.code, 0)
assert_equal(c.comment, 'Rectangle with rounded corners.')
c = AMCommentPrimitive.from_gerber("0 Rectangle with rounded corners. *")
assert c.code == 0
assert c.comment == "Rectangle with rounded corners."
def test_AMCommentPrimitive_dump():
c = AMCommentPrimitive(0, 'Rectangle with rounded corners.')
assert_equal(c.to_gerber(), '0 Rectangle with rounded corners. *')
c = AMCommentPrimitive(0, "Rectangle with rounded corners.")
assert c.to_gerber() == "0 Rectangle with rounded corners. *"
def test_AMCommentPrimitive_conversion():
c = AMCommentPrimitive(0, 'Rectangle with rounded corners.')
c = AMCommentPrimitive(0, "Rectangle with rounded corners.")
ci = c
cm = c
ci.to_inch()
cm.to_metric()
assert_equal(c, ci)
assert_equal(c, cm)
assert c == ci
assert c == cm
def test_AMCommentPrimitive_string():
c = AMCommentPrimitive(0, 'Test Comment')
assert_equal(str(c), '<Aperture Macro Comment: Test Comment>')
c = AMCommentPrimitive(0, "Test Comment")
assert str(c) == "<Aperture Macro Comment: Test Comment>"
def test_AMCirclePrimitive_ctor():
test_cases = ((1, 'on', 0, (0, 0)),
(1, 'off', 1, (0, 1)),
(1, 'on', 2.5, (0, 2)),
(1, 'off', 5.0, (3, 3)))
test_cases = (
(1, "on", 0, (0, 0)),
(1, "off", 1, (0, 1)),
(1, "on", 2.5, (0, 2)),
(1, "off", 5.0, (3, 3)),
)
for code, exposure, diameter, position in test_cases:
c = AMCirclePrimitive(code, exposure, diameter, position)
assert_equal(c.code, code)
assert_equal(c.exposure, exposure)
assert_equal(c.diameter, diameter)
assert_equal(c.position, position)
assert c.code == code
assert c.exposure == exposure
assert c.diameter == diameter
assert c.position == position
def test_AMCirclePrimitive_validation():
assert_raises(ValueError, AMCirclePrimitive, 2, 'on', 0, (0, 0))
pytest.raises(ValueError, AMCirclePrimitive, 2, "on", 0, (0, 0))
def test_AMCirclePrimitive_factory():
c = AMCirclePrimitive.from_gerber('1,0,5,0,0*')
assert_equal(c.code, 1)
assert_equal(c.exposure, 'off')
assert_equal(c.diameter, 5)
assert_equal(c.position, (0, 0))
c = AMCirclePrimitive.from_gerber("1,0,5,0,0*")
assert c.code == 1
assert c.exposure == "off"
assert c.diameter == 5
assert c.position == (0, 0)
def test_AMCirclePrimitive_dump():
c = AMCirclePrimitive(1, 'off', 5, (0, 0))
assert_equal(c.to_gerber(), '1,0,5,0,0*')
c = AMCirclePrimitive(1, 'on', 5, (0, 0))
assert_equal(c.to_gerber(), '1,1,5,0,0*')
c = AMCirclePrimitive(1, "off", 5, (0, 0))
assert c.to_gerber() == "1,0,5,0,0*"
c = AMCirclePrimitive(1, "on", 5, (0, 0))
assert c.to_gerber() == "1,1,5,0,0*"
def test_AMCirclePrimitive_conversion():
c = AMCirclePrimitive(1, 'off', 25.4, (25.4, 0))
c = AMCirclePrimitive(1, "off", 25.4, (25.4, 0))
c.to_inch()
assert_equal(c.diameter, 1)
assert_equal(c.position, (1, 0))
assert c.diameter == 1
assert c.position == (1, 0)
c = AMCirclePrimitive(1, 'off', 1, (1, 0))
c = AMCirclePrimitive(1, "off", 1, (1, 0))
c.to_metric()
assert_equal(c.diameter, 25.4)
assert_equal(c.position, (25.4, 0))
assert c.diameter == 25.4
assert c.position == (25.4, 0)
def test_AMVectorLinePrimitive_validation():
assert_raises(ValueError, AMVectorLinePrimitive,
3, 'on', 0.1, (0, 0), (3.3, 5.4), 0)
pytest.raises(
ValueError, AMVectorLinePrimitive, 3, "on", 0.1, (0, 0), (3.3, 5.4), 0
)
def test_AMVectorLinePrimitive_factory():
l = AMVectorLinePrimitive.from_gerber('20,1,0.9,0,0.45,12,0.45,0*')
assert_equal(l.code, 20)
assert_equal(l.exposure, 'on')
assert_equal(l.width, 0.9)
assert_equal(l.start, (0, 0.45))
assert_equal(l.end, (12, 0.45))
assert_equal(l.rotation, 0)
l = AMVectorLinePrimitive.from_gerber("20,1,0.9,0,0.45,12,0.45,0*")
assert l.code == 20
assert l.exposure == "on"
assert l.width == 0.9
assert l.start == (0, 0.45)
assert l.end == (12, 0.45)
assert l.rotation == 0
def test_AMVectorLinePrimitive_dump():
l = AMVectorLinePrimitive.from_gerber('20,1,0.9,0,0.45,12,0.45,0*')
assert_equal(l.to_gerber(), '20,1,0.9,0.0,0.45,12.0,0.45,0.0*')
l = AMVectorLinePrimitive.from_gerber("20,1,0.9,0,0.45,12,0.45,0*")
assert l.to_gerber() == "20,1,0.9,0.0,0.45,12.0,0.45,0.0*"
def test_AMVectorLinePrimtive_conversion():
l = AMVectorLinePrimitive(20, 'on', 25.4, (0, 0), (25.4, 25.4), 0)
l = AMVectorLinePrimitive(20, "on", 25.4, (0, 0), (25.4, 25.4), 0)
l.to_inch()
assert_equal(l.width, 1)
assert_equal(l.start, (0, 0))
assert_equal(l.end, (1, 1))
assert l.width == 1
assert l.start == (0, 0)
assert l.end == (1, 1)
l = AMVectorLinePrimitive(20, 'on', 1, (0, 0), (1, 1), 0)
l = AMVectorLinePrimitive(20, "on", 1, (0, 0), (1, 1), 0)
l.to_metric()
assert_equal(l.width, 25.4)
assert_equal(l.start, (0, 0))
assert_equal(l.end, (25.4, 25.4))
assert l.width == 25.4
assert l.start == (0, 0)
assert l.end == (25.4, 25.4)
def test_AMOutlinePrimitive_validation():
assert_raises(ValueError, AMOutlinePrimitive, 7, 'on',
(0, 0), [(3.3, 5.4), (4.0, 5.4), (0, 0)], 0)
assert_raises(ValueError, AMOutlinePrimitive, 4, 'on',
(0, 0), [(3.3, 5.4), (4.0, 5.4), (0, 1)], 0)
pytest.raises(
ValueError,
AMOutlinePrimitive,
7,
"on",
(0, 0),
[(3.3, 5.4), (4.0, 5.4), (0, 0)],
0,
)
pytest.raises(
ValueError,
AMOutlinePrimitive,
4,
"on",
(0, 0),
[(3.3, 5.4), (4.0, 5.4), (0, 1)],
0,
)
def test_AMOutlinePrimitive_factory():
o = AMOutlinePrimitive.from_gerber('4,1,3,0,0,3,3,3,0,0,0,0*')
assert_equal(o.code, 4)
assert_equal(o.exposure, 'on')
assert_equal(o.start_point, (0, 0))
assert_equal(o.points, [(3, 3), (3, 0), (0, 0)])
assert_equal(o.rotation, 0)
o = AMOutlinePrimitive.from_gerber("4,1,3,0,0,3,3,3,0,0,0,0*")
assert o.code == 4
assert o.exposure == "on"
assert o.start_point == (0, 0)
assert o.points == [(3, 3), (3, 0), (0, 0)]
assert o.rotation == 0
def test_AMOUtlinePrimitive_dump():
o = AMOutlinePrimitive(4, 'on', (0, 0), [(3, 3), (3, 0), (0, 0)], 0)
o = AMOutlinePrimitive(4, "on", (0, 0), [(3, 3), (3, 0), (0, 0)], 0)
# New lines don't matter for Gerber, but we insert them to make it easier to remove
# For test purposes we can ignore them
assert_equal(o.to_gerber().replace('\n', ''), '4,1,3,0,0,3,3,3,0,0,0,0*')
assert o.to_gerber().replace("\n", "") == "4,1,3,0,0,3,3,3,0,0,0,0*"
def test_AMOutlinePrimitive_conversion():
o = AMOutlinePrimitive(
4, 'on', (0, 0), [(25.4, 25.4), (25.4, 0), (0, 0)], 0)
o = AMOutlinePrimitive(4, "on", (0, 0), [(25.4, 25.4), (25.4, 0), (0, 0)], 0)
o.to_inch()
assert_equal(o.start_point, (0, 0))
assert_equal(o.points, ((1., 1.), (1., 0.), (0., 0.)))
assert o.start_point == (0, 0)
assert o.points == ((1.0, 1.0), (1.0, 0.0), (0.0, 0.0))
o = AMOutlinePrimitive(4, 'on', (0, 0), [(1, 1), (1, 0), (0, 0)], 0)
o = AMOutlinePrimitive(4, "on", (0, 0), [(1, 1), (1, 0), (0, 0)], 0)
o.to_metric()
assert_equal(o.start_point, (0, 0))
assert_equal(o.points, ((25.4, 25.4), (25.4, 0), (0, 0)))
assert o.start_point == (0, 0)
assert o.points == ((25.4, 25.4), (25.4, 0), (0, 0))
def test_AMPolygonPrimitive_validation():
assert_raises(ValueError, AMPolygonPrimitive, 6, 'on', 3, (3.3, 5.4), 3, 0)
assert_raises(ValueError, AMPolygonPrimitive, 5, 'on', 2, (3.3, 5.4), 3, 0)
assert_raises(ValueError, AMPolygonPrimitive, 5, 'on', 13, (3.3, 5.4), 3, 0)
pytest.raises(ValueError, AMPolygonPrimitive, 6, "on", 3, (3.3, 5.4), 3, 0)
pytest.raises(ValueError, AMPolygonPrimitive, 5, "on", 2, (3.3, 5.4), 3, 0)
pytest.raises(ValueError, AMPolygonPrimitive, 5, "on", 13, (3.3, 5.4), 3, 0)
def test_AMPolygonPrimitive_factory():
p = AMPolygonPrimitive.from_gerber('5,1,3,3.3,5.4,3,0')
assert_equal(p.code, 5)
assert_equal(p.exposure, 'on')
assert_equal(p.vertices, 3)
assert_equal(p.position, (3.3, 5.4))
assert_equal(p.diameter, 3)
assert_equal(p.rotation, 0)
p = AMPolygonPrimitive.from_gerber("5,1,3,3.3,5.4,3,0")
assert p.code == 5
assert p.exposure == "on"
assert p.vertices == 3
assert p.position == (3.3, 5.4)
assert p.diameter == 3
assert p.rotation == 0
def test_AMPolygonPrimitive_dump():
p = AMPolygonPrimitive(5, 'on', 3, (3.3, 5.4), 3, 0)
assert_equal(p.to_gerber(), '5,1,3,3.3,5.4,3,0*')
p = AMPolygonPrimitive(5, "on", 3, (3.3, 5.4), 3, 0)
assert p.to_gerber() == "5,1,3,3.3,5.4,3,0*"
def test_AMPolygonPrimitive_conversion():
p = AMPolygonPrimitive(5, 'off', 3, (25.4, 0), 25.4, 0)
p = AMPolygonPrimitive(5, "off", 3, (25.4, 0), 25.4, 0)
p.to_inch()
assert_equal(p.diameter, 1)
assert_equal(p.position, (1, 0))
assert p.diameter == 1
assert p.position == (1, 0)
p = AMPolygonPrimitive(5, 'off', 3, (1, 0), 1, 0)
p = AMPolygonPrimitive(5, "off", 3, (1, 0), 1, 0)
p.to_metric()
assert_equal(p.diameter, 25.4)
assert_equal(p.position, (25.4, 0))
assert p.diameter == 25.4
assert p.position == (25.4, 0)
def test_AMMoirePrimitive_validation():
assert_raises(ValueError, AMMoirePrimitive, 7,
(0, 0), 5.1, 0.2, 0.4, 6, 0.1, 6.1, 0)
pytest.raises(
ValueError, AMMoirePrimitive, 7, (0, 0), 5.1, 0.2, 0.4, 6, 0.1, 6.1, 0
)
def test_AMMoirePrimitive_factory():
m = AMMoirePrimitive.from_gerber('6,0,0,5,0.5,0.5,2,0.1,6,0*')
assert_equal(m.code, 6)
assert_equal(m.position, (0, 0))
assert_equal(m.diameter, 5)
assert_equal(m.ring_thickness, 0.5)
assert_equal(m.gap, 0.5)
assert_equal(m.max_rings, 2)
assert_equal(m.crosshair_thickness, 0.1)
assert_equal(m.crosshair_length, 6)
assert_equal(m.rotation, 0)
m = AMMoirePrimitive.from_gerber("6,0,0,5,0.5,0.5,2,0.1,6,0*")
assert m.code == 6
assert m.position == (0, 0)
assert m.diameter == 5
assert m.ring_thickness == 0.5
assert m.gap == 0.5
assert m.max_rings == 2
assert m.crosshair_thickness == 0.1
assert m.crosshair_length == 6
assert m.rotation == 0
def test_AMMoirePrimitive_dump():
m = AMMoirePrimitive.from_gerber('6,0,0,5,0.5,0.5,2,0.1,6,0*')
assert_equal(m.to_gerber(), '6,0,0,5.0,0.5,0.5,2,0.1,6.0,0.0*')
m = AMMoirePrimitive.from_gerber("6,0,0,5,0.5,0.5,2,0.1,6,0*")
assert m.to_gerber() == "6,0,0,5.0,0.5,0.5,2,0.1,6.0,0.0*"
def test_AMMoirePrimitive_conversion():
m = AMMoirePrimitive(6, (25.4, 25.4), 25.4, 25.4, 25.4, 6, 25.4, 25.4, 0)
m.to_inch()
assert_equal(m.position, (1., 1.))
assert_equal(m.diameter, 1.)
assert_equal(m.ring_thickness, 1.)
assert_equal(m.gap, 1.)
assert_equal(m.crosshair_thickness, 1.)
assert_equal(m.crosshair_length, 1.)
assert m.position == (1.0, 1.0)
assert m.diameter == 1.0
assert m.ring_thickness == 1.0
assert m.gap == 1.0
assert m.crosshair_thickness == 1.0
assert m.crosshair_length == 1.0
m = AMMoirePrimitive(6, (1, 1), 1, 1, 1, 6, 1, 1, 0)
m.to_metric()
assert_equal(m.position, (25.4, 25.4))
assert_equal(m.diameter, 25.4)
assert_equal(m.ring_thickness, 25.4)
assert_equal(m.gap, 25.4)
assert_equal(m.crosshair_thickness, 25.4)
assert_equal(m.crosshair_length, 25.4)
assert m.position == (25.4, 25.4)
assert m.diameter == 25.4
assert m.ring_thickness == 25.4
assert m.gap == 25.4
assert m.crosshair_thickness == 25.4
assert m.crosshair_length == 25.4
def test_AMThermalPrimitive_validation():
assert_raises(ValueError, AMThermalPrimitive, 8, (0.0, 0.0), 7, 5, 0.2, 0.0)
assert_raises(TypeError, AMThermalPrimitive, 7, (0.0, '0'), 7, 5, 0.2, 0.0)
pytest.raises(ValueError, AMThermalPrimitive, 8, (0.0, 0.0), 7, 5, 0.2, 0.0)
pytest.raises(TypeError, AMThermalPrimitive, 7, (0.0, "0"), 7, 5, 0.2, 0.0)
def test_AMThermalPrimitive_factory():
t = AMThermalPrimitive.from_gerber('7,0,0,7,6,0.2,45*')
assert_equal(t.code, 7)
assert_equal(t.position, (0, 0))
assert_equal(t.outer_diameter, 7)
assert_equal(t.inner_diameter, 6)
assert_equal(t.gap, 0.2)
assert_equal(t.rotation, 45)
t = AMThermalPrimitive.from_gerber("7,0,0,7,6,0.2,45*")
assert t.code == 7
assert t.position == (0, 0)
assert t.outer_diameter == 7
assert t.inner_diameter == 6
assert t.gap == 0.2
assert t.rotation == 45
def test_AMThermalPrimitive_dump():
t = AMThermalPrimitive.from_gerber('7,0,0,7,6,0.2,30*')
assert_equal(t.to_gerber(), '7,0,0,7.0,6.0,0.2,30.0*')
t = AMThermalPrimitive.from_gerber("7,0,0,7,6,0.2,30*")
assert t.to_gerber() == "7,0,0,7.0,6.0,0.2,30.0*"
def test_AMThermalPrimitive_conversion():
t = AMThermalPrimitive(7, (25.4, 25.4), 25.4, 25.4, 25.4, 0.0)
t.to_inch()
assert_equal(t.position, (1., 1.))
assert_equal(t.outer_diameter, 1.)
assert_equal(t.inner_diameter, 1.)
assert_equal(t.gap, 1.)
assert t.position == (1.0, 1.0)
assert t.outer_diameter == 1.0
assert t.inner_diameter == 1.0
assert t.gap == 1.0
t = AMThermalPrimitive(7, (1, 1), 1, 1, 1, 0)
t.to_metric()
assert_equal(t.position, (25.4, 25.4))
assert_equal(t.outer_diameter, 25.4)
assert_equal(t.inner_diameter, 25.4)
assert_equal(t.gap, 25.4)
assert t.position == (25.4, 25.4)
assert t.outer_diameter == 25.4
assert t.inner_diameter == 25.4
assert t.gap == 25.4
def test_AMCenterLinePrimitive_validation():
assert_raises(ValueError, AMCenterLinePrimitive,
22, 1, 0.2, 0.5, (0, 0), 0)
pytest.raises(ValueError, AMCenterLinePrimitive, 22, 1, 0.2, 0.5, (0, 0), 0)
def test_AMCenterLinePrimtive_factory():
l = AMCenterLinePrimitive.from_gerber('21,1,6.8,1.2,3.4,0.6,0*')
assert_equal(l.code, 21)
assert_equal(l.exposure, 'on')
assert_equal(l.width, 6.8)
assert_equal(l.height, 1.2)
assert_equal(l.center, (3.4, 0.6))
assert_equal(l.rotation, 0)
l = AMCenterLinePrimitive.from_gerber("21,1,6.8,1.2,3.4,0.6,0*")
assert l.code == 21
assert l.exposure == "on"
assert l.width == 6.8
assert l.height == 1.2
assert l.center == (3.4, 0.6)
assert l.rotation == 0
def test_AMCenterLinePrimitive_dump():
l = AMCenterLinePrimitive.from_gerber('21,1,6.8,1.2,3.4,0.6,0*')
assert_equal(l.to_gerber(), '21,1,6.8,1.2,3.4,0.6,0.0*')
l = AMCenterLinePrimitive.from_gerber("21,1,6.8,1.2,3.4,0.6,0*")
assert l.to_gerber() == "21,1,6.8,1.2,3.4,0.6,0.0*"
def test_AMCenterLinePrimitive_conversion():
l = AMCenterLinePrimitive(21, 'on', 25.4, 25.4, (25.4, 25.4), 0)
l = AMCenterLinePrimitive(21, "on", 25.4, 25.4, (25.4, 25.4), 0)
l.to_inch()
assert_equal(l.width, 1.)
assert_equal(l.height, 1.)
assert_equal(l.center, (1., 1.))
assert l.width == 1.0
assert l.height == 1.0
assert l.center == (1.0, 1.0)
l = AMCenterLinePrimitive(21, 'on', 1, 1, (1, 1), 0)
l = AMCenterLinePrimitive(21, "on", 1, 1, (1, 1), 0)
l.to_metric()
assert_equal(l.width, 25.4)
assert_equal(l.height, 25.4)
assert_equal(l.center, (25.4, 25.4))
assert l.width == 25.4
assert l.height == 25.4
assert l.center == (25.4, 25.4)
def test_AMLowerLeftLinePrimitive_validation():
assert_raises(ValueError, AMLowerLeftLinePrimitive,
23, 1, 0.2, 0.5, (0, 0), 0)
pytest.raises(ValueError, AMLowerLeftLinePrimitive, 23, 1, 0.2, 0.5, (0, 0), 0)
def test_AMLowerLeftLinePrimtive_factory():
l = AMLowerLeftLinePrimitive.from_gerber('22,1,6.8,1.2,3.4,0.6,0*')
assert_equal(l.code, 22)
assert_equal(l.exposure, 'on')
assert_equal(l.width, 6.8)
assert_equal(l.height, 1.2)
assert_equal(l.lower_left, (3.4, 0.6))
assert_equal(l.rotation, 0)
l = AMLowerLeftLinePrimitive.from_gerber("22,1,6.8,1.2,3.4,0.6,0*")
assert l.code == 22
assert l.exposure == "on"
assert l.width == 6.8
assert l.height == 1.2
assert l.lower_left == (3.4, 0.6)
assert l.rotation == 0
def test_AMLowerLeftLinePrimitive_dump():
l = AMLowerLeftLinePrimitive.from_gerber('22,1,6.8,1.2,3.4,0.6,0*')
assert_equal(l.to_gerber(), '22,1,6.8,1.2,3.4,0.6,0.0*')
l = AMLowerLeftLinePrimitive.from_gerber("22,1,6.8,1.2,3.4,0.6,0*")
assert l.to_gerber() == "22,1,6.8,1.2,3.4,0.6,0.0*"
def test_AMLowerLeftLinePrimitive_conversion():
l = AMLowerLeftLinePrimitive(22, 'on', 25.4, 25.4, (25.4, 25.4), 0)
l = AMLowerLeftLinePrimitive(22, "on", 25.4, 25.4, (25.4, 25.4), 0)
l.to_inch()
assert_equal(l.width, 1.)
assert_equal(l.height, 1.)
assert_equal(l.lower_left, (1., 1.))
assert l.width == 1.0
assert l.height == 1.0
assert l.lower_left == (1.0, 1.0)
l = AMLowerLeftLinePrimitive(22, 'on', 1, 1, (1, 1), 0)
l = AMLowerLeftLinePrimitive(22, "on", 1, 1, (1, 1), 0)
l.to_metric()
assert_equal(l.width, 25.4)
assert_equal(l.height, 25.4)
assert_equal(l.lower_left, (25.4, 25.4))
assert l.width == 25.4
assert l.height == 25.4
assert l.lower_left == (25.4, 25.4)
def test_AMUnsupportPrimitive():
u = AMUnsupportPrimitive.from_gerber('Test')
assert_equal(u.primitive, 'Test')
u = AMUnsupportPrimitive('Test')
assert_equal(u.to_gerber(), 'Test')
u = AMUnsupportPrimitive.from_gerber("Test")
assert u.primitive == "Test"
u = AMUnsupportPrimitive("Test")
assert u.to_gerber() == "Test"
def test_AMUnsupportPrimitive_smoketest():
u = AMUnsupportPrimitive.from_gerber('Test')
u = AMUnsupportPrimitive.from_gerber("Test")
u.to_inch()
u.to_metric()
def test_inch():
assert_equal(inch(25.4), 1)
assert inch(25.4) == 1
def test_metric():
assert_equal(metric(1), 25.4)
assert metric(1) == 25.4

View file

@ -8,63 +8,87 @@ import tempfile
from ..render.cairo_backend import GerberCairoContext
from ..rs274x import read
from .tests import *
from nose.tools import assert_tuple_equal
def _DISABLED_test_render_two_boxes():
"""Umaco exapmle of two boxes"""
_test_render('resources/example_two_square_boxes.gbr', 'golden/example_two_square_boxes.png')
_test_render(
"resources/example_two_square_boxes.gbr", "golden/example_two_square_boxes.png"
)
def _DISABLED_test_render_single_quadrant():
"""Umaco exapmle of a single quadrant arc"""
_test_render('resources/example_single_quadrant.gbr', 'golden/example_single_quadrant.png')
_test_render(
"resources/example_single_quadrant.gbr", "golden/example_single_quadrant.png"
)
def _DISABLED_test_render_simple_contour():
def _DISABLED_test_render_simple_contour():
"""Umaco exapmle of a simple arrow-shaped contour"""
gerber = _test_render('resources/example_simple_contour.gbr', 'golden/example_simple_contour.png')
gerber = _test_render(
"resources/example_simple_contour.gbr", "golden/example_simple_contour.png"
)
# Check the resulting dimensions
assert_tuple_equal(((2.0, 11.0), (1.0, 9.0)), gerber.bounding_box)
assert ((2.0, 11.0), (1.0, 9.0)) == gerber.bounding_box
def _DISABLED_test_render_single_contour_1():
"""Umaco example of a single contour
The resulting image for this test is used by other tests because they must generate the same output."""
_test_render('resources/example_single_contour_1.gbr', 'golden/example_single_contour.png')
_test_render(
"resources/example_single_contour_1.gbr", "golden/example_single_contour.png"
)
def _DISABLED_test_render_single_contour_2():
"""Umaco exapmle of a single contour, alternate contour end order
The resulting image for this test is used by other tests because they must generate the same output."""
_test_render('resources/example_single_contour_2.gbr', 'golden/example_single_contour.png')
_test_render(
"resources/example_single_contour_2.gbr", "golden/example_single_contour.png"
)
def _DISABLED_test_render_single_contour_3():
"""Umaco exapmle of a single contour with extra line"""
_test_render('resources/example_single_contour_3.gbr', 'golden/example_single_contour_3.png')
_test_render(
"resources/example_single_contour_3.gbr", "golden/example_single_contour_3.png"
)
def _DISABLED_test_render_not_overlapping_contour():
def _DISABLED_test_render_not_overlapping_contour():
"""Umaco example of D02 staring a second contour"""
_test_render('resources/example_not_overlapping_contour.gbr', 'golden/example_not_overlapping_contour.png')
_test_render(
"resources/example_not_overlapping_contour.gbr",
"golden/example_not_overlapping_contour.png",
)
def _DISABLED_test_render_not_overlapping_touching():
def _DISABLED_test_render_not_overlapping_touching():
"""Umaco example of D02 staring a second contour"""
_test_render('resources/example_not_overlapping_touching.gbr', 'golden/example_not_overlapping_touching.png')
_test_render(
"resources/example_not_overlapping_touching.gbr",
"golden/example_not_overlapping_touching.png",
)
def test_render_overlapping_touching():
"""Umaco example of D02 staring a second contour"""
_test_render('resources/example_overlapping_touching.gbr', 'golden/example_overlapping_touching.png')
_test_render(
"resources/example_overlapping_touching.gbr",
"golden/example_overlapping_touching.png",
)
def test_render_overlapping_contour():
"""Umaco example of D02 staring a second contour"""
_test_render('resources/example_overlapping_contour.gbr', 'golden/example_overlapping_contour.png')
_test_render(
"resources/example_overlapping_contour.gbr",
"golden/example_overlapping_contour.png",
)
def _DISABLED_test_render_level_holes():
@ -72,82 +96,107 @@ def _DISABLED_test_render_level_holes():
# TODO This is clearly rendering wrong. I'm temporarily checking this in because there are more
# rendering fixes in the related repository that may resolve these.
_test_render('resources/example_level_holes.gbr', 'golden/example_overlapping_contour.png')
_test_render(
"resources/example_level_holes.gbr", "golden/example_overlapping_contour.png"
)
def _DISABLED_test_render_cutin():
"""Umaco example of using a cutin"""
# TODO This is clearly rendering wrong.
_test_render('resources/example_cutin.gbr', 'golden/example_cutin.png', '/Users/ham/Desktop/cutin.png')
_test_render(
"resources/example_cutin.gbr",
"golden/example_cutin.png",
"/Users/ham/Desktop/cutin.png",
)
def _DISABLED_test_render_fully_coincident():
def _DISABLED_test_render_fully_coincident():
"""Umaco example of coincident lines rendering two contours"""
_test_render('resources/example_fully_coincident.gbr', 'golden/example_fully_coincident.png')
_test_render(
"resources/example_fully_coincident.gbr", "golden/example_fully_coincident.png"
)
def _DISABLED_test_render_coincident_hole():
def _DISABLED_test_render_coincident_hole():
"""Umaco example of coincident lines rendering a hole in the contour"""
_test_render('resources/example_coincident_hole.gbr', 'golden/example_coincident_hole.png')
_test_render(
"resources/example_coincident_hole.gbr", "golden/example_coincident_hole.png"
)
def test_render_cutin_multiple():
"""Umaco example of a region with multiple cutins"""
_test_render('resources/example_cutin_multiple.gbr', 'golden/example_cutin_multiple.png')
_test_render(
"resources/example_cutin_multiple.gbr", "golden/example_cutin_multiple.png"
)
def _DISABLED_test_flash_circle():
"""Umaco example a simple circular flash with and without a hole"""
_test_render('resources/example_flash_circle.gbr', 'golden/example_flash_circle.png',
'/Users/ham/Desktop/flashcircle.png')
_test_render(
"resources/example_flash_circle.gbr",
"golden/example_flash_circle.png",
"/Users/ham/Desktop/flashcircle.png",
)
def _DISABLED_test_flash_rectangle():
"""Umaco example a simple rectangular flash with and without a hole"""
_test_render('resources/example_flash_rectangle.gbr', 'golden/example_flash_rectangle.png')
_test_render(
"resources/example_flash_rectangle.gbr", "golden/example_flash_rectangle.png"
)
def _DISABLED_test_flash_obround():
"""Umaco example a simple obround flash with and without a hole"""
_test_render('resources/example_flash_obround.gbr', 'golden/example_flash_obround.png')
_test_render(
"resources/example_flash_obround.gbr", "golden/example_flash_obround.png"
)
def _DISABLED_test_flash_polygon():
"""Umaco example a simple polygon flash with and without a hole"""
_test_render('resources/example_flash_polygon.gbr', 'golden/example_flash_polygon.png')
_test_render(
"resources/example_flash_polygon.gbr", "golden/example_flash_polygon.png"
)
def _DISABLED_test_holes_dont_clear():
"""Umaco example that an aperture with a hole does not clear the area"""
_test_render('resources/example_holes_dont_clear.gbr', 'golden/example_holes_dont_clear.png')
_test_render(
"resources/example_holes_dont_clear.gbr", "golden/example_holes_dont_clear.png"
)
def _DISABLED_test_render_am_exposure_modifier():
"""Umaco example that an aperture macro with a hole does not clear the area"""
_test_render('resources/example_am_exposure_modifier.gbr', 'golden/example_am_exposure_modifier.png')
_test_render(
"resources/example_am_exposure_modifier.gbr",
"golden/example_am_exposure_modifier.png",
)
def test_render_svg_simple_contour():
"""Example of rendering to an SVG file"""
_test_simple_render_svg('resources/example_simple_contour.gbr')
_test_simple_render_svg("resources/example_simple_contour.gbr")
def _resolve_path(path):
return os.path.join(os.path.dirname(__file__),
path)
return os.path.join(os.path.dirname(__file__), path)
def _test_render(gerber_path, png_expected_path, create_output_path = None):
def _test_render(gerber_path, png_expected_path, create_output_path=None):
"""Render the gerber file and compare to the expected PNG output.
Parameters
@ -176,21 +225,24 @@ def _test_render(gerber_path, png_expected_path, create_output_path = None):
# If we want to write the file bytes, do it now. This happens
if create_output_path:
with open(create_output_path, 'wb') as out_file:
with open(create_output_path, "wb") as out_file:
out_file.write(actual_bytes)
# Creating the output is dangerous - it could overwrite the expected result.
# So if we are creating the output, we make the test fail on purpose so you
# won't forget to disable this
assert_false(True, 'Test created the output %s. This needs to be disabled to make sure the test behaves correctly' % (create_output_path,))
assert not True, (
"Test created the output %s. This needs to be disabled to make sure the test behaves correctly"
% (create_output_path,)
)
# Read the expected PNG file
with open(png_expected_path, 'rb') as expected_file:
with open(png_expected_path, "rb") as expected_file:
expected_bytes = expected_file.read()
# Don't directly use assert_equal otherwise any failure pollutes the test results
equal = (expected_bytes == actual_bytes)
assert_true(equal)
equal = expected_bytes == actual_bytes
assert equal
return gerber
@ -214,14 +266,14 @@ def _test_simple_render_svg(gerber_path):
gerber.render(ctx)
temp_dir = tempfile.mkdtemp()
svg_temp_path = os.path.join(temp_dir, 'output.svg')
svg_temp_path = os.path.join(temp_dir, "output.svg")
assert_false(os.path.exists(svg_temp_path))
assert not os.path.exists(svg_temp_path)
ctx.dump(svg_temp_path)
assert_true(os.path.exists(svg_temp_path))
assert os.path.exists(svg_temp_path)
with open(svg_temp_path, 'r') as expected_file:
with open(svg_temp_path, "r") as expected_file:
expected_bytes = expected_file.read()
assert_equal(expected_bytes[:38], '<?xml version="1.0" encoding="UTF-8"?>')
assert expected_bytes[:38] == '<?xml version="1.0" encoding="UTF-8"?>'
shutil.rmtree(temp_dir)

View file

@ -3,56 +3,57 @@
# Author: Hamilton Kibbe <ham@hamiltonkib.be>
import pytest
from ..cam import CamFile, FileSettings
from .tests import *
def test_filesettings_defaults():
""" Test FileSettings default values
"""
fs = FileSettings()
assert_equal(fs.format, (2, 5))
assert_equal(fs.notation, 'absolute')
assert_equal(fs.zero_suppression, 'trailing')
assert_equal(fs.units, 'inch')
assert fs.format == (2, 5)
assert fs.notation == "absolute"
assert fs.zero_suppression == "trailing"
assert fs.units == "inch"
def test_filesettings_dict():
""" Test FileSettings Dict
"""
fs = FileSettings()
assert_equal(fs['format'], (2, 5))
assert_equal(fs['notation'], 'absolute')
assert_equal(fs['zero_suppression'], 'trailing')
assert_equal(fs['units'], 'inch')
assert fs["format"] == (2, 5)
assert fs["notation"] == "absolute"
assert fs["zero_suppression"] == "trailing"
assert fs["units"] == "inch"
def test_filesettings_assign():
""" Test FileSettings attribute assignment
"""
fs = FileSettings()
fs.units = 'test1'
fs.notation = 'test2'
fs.zero_suppression = 'test3'
fs.format = 'test4'
assert_equal(fs.units, 'test1')
assert_equal(fs.notation, 'test2')
assert_equal(fs.zero_suppression, 'test3')
assert_equal(fs.format, 'test4')
fs.units = "test1"
fs.notation = "test2"
fs.zero_suppression = "test3"
fs.format = "test4"
assert fs.units == "test1"
assert fs.notation == "test2"
assert fs.zero_suppression == "test3"
assert fs.format == "test4"
def test_filesettings_dict_assign():
""" Test FileSettings dict-style attribute assignment
"""
fs = FileSettings()
fs['units'] = 'metric'
fs['notation'] = 'incremental'
fs['zero_suppression'] = 'leading'
fs['format'] = (1, 2)
assert_equal(fs.units, 'metric')
assert_equal(fs.notation, 'incremental')
assert_equal(fs.zero_suppression, 'leading')
assert_equal(fs.format, (1, 2))
fs["units"] = "metric"
fs["notation"] = "incremental"
fs["zero_suppression"] = "leading"
fs["format"] = (1, 2)
assert fs.units == "metric"
assert fs.notation == "incremental"
assert fs.zero_suppression == "leading"
assert fs.format == (1, 2)
def test_camfile_init():
@ -65,7 +66,7 @@ def test_camfile_settings():
""" Test CamFile Default Settings
"""
cf = CamFile()
assert_equal(cf.settings, FileSettings())
assert cf.settings == FileSettings()
def test_bounds_override_smoketest():
@ -77,73 +78,74 @@ def test_zeros():
""" Test zero/zero_suppression interaction
"""
fs = FileSettings()
assert_equal(fs.zero_suppression, 'trailing')
assert_equal(fs.zeros, 'leading')
assert fs.zero_suppression == "trailing"
assert fs.zeros == "leading"
fs['zero_suppression'] = 'leading'
assert_equal(fs.zero_suppression, 'leading')
assert_equal(fs.zeros, 'trailing')
fs["zero_suppression"] = "leading"
assert fs.zero_suppression == "leading"
assert fs.zeros == "trailing"
fs.zero_suppression = 'trailing'
assert_equal(fs.zero_suppression, 'trailing')
assert_equal(fs.zeros, 'leading')
fs.zero_suppression = "trailing"
assert fs.zero_suppression == "trailing"
assert fs.zeros == "leading"
fs['zeros'] = 'trailing'
assert_equal(fs.zeros, 'trailing')
assert_equal(fs.zero_suppression, 'leading')
fs["zeros"] = "trailing"
assert fs.zeros == "trailing"
assert fs.zero_suppression == "leading"
fs.zeros = 'leading'
assert_equal(fs.zeros, 'leading')
assert_equal(fs.zero_suppression, 'trailing')
fs.zeros = "leading"
assert fs.zeros == "leading"
assert fs.zero_suppression == "trailing"
fs = FileSettings(zeros='leading')
assert_equal(fs.zeros, 'leading')
assert_equal(fs.zero_suppression, 'trailing')
fs = FileSettings(zeros="leading")
assert fs.zeros == "leading"
assert fs.zero_suppression == "trailing"
fs = FileSettings(zero_suppression='leading')
assert_equal(fs.zeros, 'trailing')
assert_equal(fs.zero_suppression, 'leading')
fs = FileSettings(zero_suppression="leading")
assert fs.zeros == "trailing"
assert fs.zero_suppression == "leading"
fs = FileSettings(zeros='leading', zero_suppression='trailing')
assert_equal(fs.zeros, 'leading')
assert_equal(fs.zero_suppression, 'trailing')
fs = FileSettings(zeros="leading", zero_suppression="trailing")
assert fs.zeros == "leading"
assert fs.zero_suppression == "trailing"
fs = FileSettings(zeros='trailing', zero_suppression='leading')
assert_equal(fs.zeros, 'trailing')
assert_equal(fs.zero_suppression, 'leading')
fs = FileSettings(zeros="trailing", zero_suppression="leading")
assert fs.zeros == "trailing"
assert fs.zero_suppression == "leading"
def test_filesettings_validation():
""" Test FileSettings constructor argument validation
"""
# absolute-ish is not a valid notation
assert_raises(ValueError, FileSettings, 'absolute-ish',
'inch', None, (2, 5), None)
pytest.raises(ValueError, FileSettings, "absolute-ish", "inch", None, (2, 5), None)
# degrees kelvin isn't a valid unit for a CAM file
assert_raises(ValueError, FileSettings, 'absolute',
'degrees kelvin', None, (2, 5), None)
pytest.raises(
ValueError, FileSettings, "absolute", "degrees kelvin", None, (2, 5), None
)
assert_raises(ValueError, FileSettings, 'absolute',
'inch', 'leading', (2, 5), 'leading')
pytest.raises(
ValueError, FileSettings, "absolute", "inch", "leading", (2, 5), "leading"
)
# Technnically this should be an error, but Eangle files often do this incorrectly so we
# allow it
#assert_raises(ValueError, FileSettings, 'absolute',
# pytest.raises(ValueError, FileSettings, 'absolute',
# 'inch', 'following', (2, 5), None)
assert_raises(ValueError, FileSettings, 'absolute',
'inch', None, (2, 5), 'following')
assert_raises(ValueError, FileSettings, 'absolute',
'inch', None, (2, 5, 6), None)
pytest.raises(
ValueError, FileSettings, "absolute", "inch", None, (2, 5), "following"
)
pytest.raises(ValueError, FileSettings, "absolute", "inch", None, (2, 5, 6), None)
def test_key_validation():
fs = FileSettings()
assert_raises(KeyError, fs.__getitem__, 'octopus')
assert_raises(KeyError, fs.__setitem__, 'octopus', 'do not care')
assert_raises(ValueError, fs.__setitem__, 'notation', 'absolute-ish')
assert_raises(ValueError, fs.__setitem__, 'units', 'degrees kelvin')
assert_raises(ValueError, fs.__setitem__, 'zero_suppression', 'following')
assert_raises(ValueError, fs.__setitem__, 'zeros', 'following')
assert_raises(ValueError, fs.__setitem__, 'format', (2, 5, 6))
pytest.raises(KeyError, fs.__getitem__, "octopus")
pytest.raises(KeyError, fs.__setitem__, "octopus", "do not care")
pytest.raises(ValueError, fs.__setitem__, "notation", "absolute-ish")
pytest.raises(ValueError, fs.__setitem__, "units", "degrees kelvin")
pytest.raises(ValueError, fs.__setitem__, "zero_suppression", "following")
pytest.raises(ValueError, fs.__setitem__, "zeros", "following")
pytest.raises(ValueError, fs.__setitem__, "format", (2, 5, 6))

View file

@ -6,15 +6,12 @@ from ..exceptions import ParseError
from ..common import read, loads
from ..excellon import ExcellonFile
from ..rs274x import GerberFile
from .tests import *
import os
import pytest
NCDRILL_FILE = os.path.join(os.path.dirname(__file__),
'resources/ncdrill.DRD')
TOP_COPPER_FILE = os.path.join(os.path.dirname(__file__),
'resources/top_copper.GTL')
NCDRILL_FILE = os.path.join(os.path.dirname(__file__), "resources/ncdrill.DRD")
TOP_COPPER_FILE = os.path.join(os.path.dirname(__file__), "resources/top_copper.GTL")
def test_file_type_detection():
@ -22,20 +19,20 @@ def test_file_type_detection():
"""
ncdrill = read(NCDRILL_FILE)
top_copper = read(TOP_COPPER_FILE)
assert_true(isinstance(ncdrill, ExcellonFile))
assert_true(isinstance(top_copper, GerberFile))
assert isinstance(ncdrill, ExcellonFile)
assert isinstance(top_copper, GerberFile)
def test_load_from_string():
with open(NCDRILL_FILE, 'rU') as f:
with open(NCDRILL_FILE, "rU") as f:
ncdrill = loads(f.read())
with open(TOP_COPPER_FILE, 'rU') as f:
with open(TOP_COPPER_FILE, "rU") as f:
top_copper = loads(f.read())
assert_true(isinstance(ncdrill, ExcellonFile))
assert_true(isinstance(top_copper, GerberFile))
assert isinstance(ncdrill, ExcellonFile)
assert isinstance(top_copper, GerberFile)
def test_file_type_validation():
""" Test file format validation
"""
assert_raises(ParseError, read, __file__)
pytest.raises(ParseError, read, __file__)

View file

@ -3,16 +3,15 @@
# 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
from .tests import *
NCDRILL_FILE = os.path.join(os.path.dirname(__file__),
'resources/ncdrill.DRD')
NCDRILL_FILE = os.path.join(os.path.dirname(__file__), "resources/ncdrill.DRD")
def test_format_detection():
@ -21,320 +20,327 @@ def test_format_detection():
with open(NCDRILL_FILE, "rU") as f:
data = f.read()
settings = detect_excellon_format(data)
assert_equal(settings['format'], (2, 4))
assert_equal(settings['zeros'], 'trailing')
assert settings["format"] == (2, 4)
assert settings["zeros"] == "trailing"
settings = detect_excellon_format(filename=NCDRILL_FILE)
assert_equal(settings['format'], (2, 4))
assert_equal(settings['zeros'], 'trailing')
assert settings["format"] == (2, 4)
assert settings["zeros"] == "trailing"
def test_read():
ncdrill = read(NCDRILL_FILE)
assert(isinstance(ncdrill, ExcellonFile))
assert isinstance(ncdrill, ExcellonFile)
def test_write():
ncdrill = read(NCDRILL_FILE)
ncdrill.write('test.ncd')
ncdrill.write("test.ncd")
with open(NCDRILL_FILE, "rU") as src:
srclines = src.readlines()
with open('test.ncd', "rU") as res:
with open("test.ncd", "rU") as res:
for idx, line in enumerate(res):
assert_equal(line.strip(), srclines[idx].strip())
os.remove('test.ncd')
assert line.strip() == srclines[idx].strip()
os.remove("test.ncd")
def test_read_settings():
ncdrill = read(NCDRILL_FILE)
assert_equal(ncdrill.settings['format'], (2, 4))
assert_equal(ncdrill.settings['zeros'], 'trailing')
assert ncdrill.settings["format"] == (2, 4)
assert ncdrill.settings["zeros"] == "trailing"
def test_bounding_box():
ncdrill = read(NCDRILL_FILE)
xbound, ybound = ncdrill.bounding_box
assert_array_almost_equal(xbound, (0.1300, 2.1430))
assert_array_almost_equal(ybound, (0.3946, 1.7164))
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_equal(ncdrill.settings.units, 'inch')
assert ncdrill.settings.units == "inch"
ncdrill_inch = copy.deepcopy(ncdrill)
ncdrill.to_metric()
assert_equal(ncdrill.settings.units, '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_equal(i_tool, m_tool)
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_equal(m.position, i.position, '%s not equal to %s' % (m, i))
assert_equal(m.diameter, i.diameter, '%s not equal to %s' % (m, i))
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_equal(p.hole_count, 36)
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_equal(p.hole_sizes, [0.0236, 0.0354, 0.04, 0.126, 0.128])
assert p.hole_sizes == [0.0236, 0.0354, 0.04, 0.126, 0.128]
def test_parse_whitespace():
p = ExcellonParser(FileSettings())
assert_equal(p._parse_line(' '), None)
assert p._parse_line(" ") == None
def test_parse_comment():
p = ExcellonParser(FileSettings())
p._parse_line(';A comment')
assert_equal(p.statements[0].comment, 'A comment')
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_equal(p.format, (9, 9))
p._parse_line("; FILE_FORMAT=9:9 ")
assert p.format == (9, 9)
def test_parse_header():
p = ExcellonParser(FileSettings())
p._parse_line('M48 ')
assert_equal(p.state, 'HEADER')
p._parse_line('M95 ')
assert_equal(p.state, 'DRILL')
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_equal(p.state, 'ROUT')
p._parse_line('G05 ')
assert_equal(p.state, 'DRILL')
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_equal(p.statements[0].version, 1)
p._parse_line('VER,2 ')
assert_equal(p.statements[1].version, 2)
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_equal(p.statements[0].format, 1)
p._parse_line('FMAT,2 ')
assert_equal(p.statements[1].format, 2)
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')
settings = FileSettings(units="inch", zeros="trailing")
p = ExcellonParser(settings)
p._parse_line(';METRIC,LZ')
assert_equal(p.units, 'inch')
assert_equal(p.zeros, 'trailing')
p._parse_line('METRIC,LZ')
assert_equal(p.units, 'metric')
assert_equal(p.zeros, 'leading')
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')
settings = FileSettings(units="inch", zeros="trailing")
p = ExcellonParser(settings)
assert_equal(p.notation, 'absolute')
p._parse_line('ICI,ON ')
assert_equal(p.notation, 'incremental')
p._parse_line('ICI,OFF ')
assert_equal(p.notation, 'absolute')
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')
settings = FileSettings(units="inch", zeros="trailing")
p = ExcellonParser(settings)
assert_equal(p.notation, 'absolute')
p._parse_line('ICI,ON ')
assert_equal(p.notation, 'incremental')
p._parse_line('G90 ')
assert_equal(p.notation, 'absolute')
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_equal(p.statements[0].count, 3)
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_equal(p.pos, [2., 2.])
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_equal(p.statements[0].stmt, 'Not A Valid Statement')
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')
settings = FileSettings(units="inch")
tool = ExcellonTool(settings, diameter=1.0)
hit = DrillHit(tool, (1.0, 1.0))
assert_equal(hit.tool.settings.units, 'inch')
assert_equal(hit.tool.diameter, 1.0)
assert_equal(hit.position, (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_equal(hit.tool.settings.units, 'inch')
assert_equal(hit.tool.diameter, 1.0)
assert_equal(hit.position, (1.0, 1.0))
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_equal(hit.tool.settings.units, 'metric')
assert_equal(hit.tool.diameter, 25.4)
assert_equal(hit.position, (25.4, 25.4))
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_equal(hit.tool.settings.units, 'metric')
assert_equal(hit.tool.diameter, 25.4)
assert_equal(hit.position, (25.4, 25.4))
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_equal(hit.tool.settings.units, 'inch')
assert_equal(hit.tool.diameter, 1.0)
assert_equal(hit.position, (1.0, 1.0))
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), (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')
settings = FileSettings(units="inch")
tool = ExcellonTool(settings, diameter=1.0)
hit = DrillHit(tool, position)
assert_equal(hit.position, position)
assert hit.position == position
hit.offset(offset[0], offset[1])
assert_equal(hit.position, expected)
assert hit.position == expected
def test_drill_slot_units_conversion():
""" Test unit conversion for drill hits
"""
# Inch hit
settings = FileSettings(units='inch')
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_equal(hit.tool.settings.units, 'inch')
assert_equal(hit.tool.diameter, 1.0)
assert_equal(hit.start, (1.0, 1.0))
assert_equal(hit.end, (10.0, 10.0))
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_equal(hit.tool.settings.units, 'inch')
assert_equal(hit.tool.diameter, 1.0)
assert_equal(hit.start, (1.0, 1.0))
assert_equal(hit.end, (10.0, 10.0))
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_equal(hit.tool.settings.units, 'metric')
assert_equal(hit.tool.diameter, 25.4)
assert_equal(hit.start, (25.4, 25.4))
assert_equal(hit.end, (254.0, 254.0))
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_equal(hit.tool.settings.units, 'metric')
assert_equal(hit.tool.diameter, 25.4)
assert_equal(hit.start, (25.4, 25.4))
assert_equal(hit.end, (254.0, 254.0))
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_equal(hit.tool.settings.units, 'inch')
assert_equal(hit.tool.diameter, 1.0)
assert_equal(hit.start, (1.0, 1.0))
assert_equal(hit.end, (10.0, 10.0))
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), (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')
settings = FileSettings(units="inch")
tool = ExcellonTool(settings, diameter=1.0)
slot = DrillSlot(tool, start, end, DrillSlot.TYPE_ROUT)
assert_equal(slot.start, start)
assert_equal(slot.end, end)
assert slot.start == start
assert slot.end == end
slot.offset(offset[0], offset[1])
assert_equal(slot.start, expected_start)
assert_equal(slot.end, expected_end)
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')
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_equal(slot.bounding_box, expected)
assert slot.bounding_box == expected
def test_handling_multi_line_g00_and_g1():
"""Route Mode statements with coordinates on separate line are handled
@ -355,6 +361,6 @@ M16
"""
uut = ExcellonParser()
uut.parse_raw(test_data)
assert_equal(len([stmt for stmt in uut.statements
if isinstance(stmt, RouteModeStmt)]), 2)
assert (
len([stmt for stmt in uut.statements if isinstance(stmt, RouteModeStmt)]) == 2
)

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -2,139 +2,147 @@
# -*- coding: utf-8 -*-
# Author: Hamilton Kibbe <ham@hamiltonkib.be>
import pytest
from ..ipc356 import *
from ..cam import FileSettings
from .tests import *
import os
IPC_D_356_FILE = os.path.join(os.path.dirname(__file__),
'resources/ipc-d-356.ipc')
IPC_D_356_FILE = os.path.join(os.path.dirname(__file__), "resources/ipc-d-356.ipc")
def test_read():
ipcfile = read(IPC_D_356_FILE)
assert(isinstance(ipcfile, IPCNetlist))
assert isinstance(ipcfile, IPCNetlist)
def test_parser():
ipcfile = read(IPC_D_356_FILE)
assert_equal(ipcfile.settings.units, 'inch')
assert_equal(ipcfile.settings.angle_units, 'degrees')
assert_equal(len(ipcfile.comments), 3)
assert_equal(len(ipcfile.parameters), 4)
assert_equal(len(ipcfile.test_records), 105)
assert_equal(len(ipcfile.components), 21)
assert_equal(len(ipcfile.vias), 14)
assert_equal(ipcfile.test_records[-1].net_name, 'A_REALLY_LONG_NET_NAME')
assert_equal(ipcfile.outlines[0].type, 'BOARD_EDGE')
assert_equal(set(ipcfile.outlines[0].points),
{(0., 0.), (2.25, 0.), (2.25, 1.5), (0., 1.5), (0.13, 0.024)})
assert ipcfile.settings.units == "inch"
assert ipcfile.settings.angle_units == "degrees"
assert len(ipcfile.comments) == 3
assert len(ipcfile.parameters) == 4
assert len(ipcfile.test_records) == 105
assert len(ipcfile.components) == 21
assert len(ipcfile.vias) == 14
assert ipcfile.test_records[-1].net_name == "A_REALLY_LONG_NET_NAME"
assert ipcfile.outlines[0].type == "BOARD_EDGE"
assert set(ipcfile.outlines[0].points) == {
(0.0, 0.0),
(2.25, 0.0),
(2.25, 1.5),
(0.0, 1.5),
(0.13, 0.024),
}
def test_comment():
c = IPC356_Comment('Layer Stackup:')
assert_equal(c.comment, 'Layer Stackup:')
c = IPC356_Comment.from_line('C Layer Stackup: ')
assert_equal(c.comment, 'Layer Stackup:')
assert_raises(ValueError, IPC356_Comment.from_line, 'P JOB')
assert_equal(str(c), '<IPC-D-356 Comment: Layer Stackup:>')
c = IPC356_Comment("Layer Stackup:")
assert c.comment == "Layer Stackup:"
c = IPC356_Comment.from_line("C Layer Stackup: ")
assert c.comment == "Layer Stackup:"
pytest.raises(ValueError, IPC356_Comment.from_line, "P JOB")
assert str(c) == "<IPC-D-356 Comment: Layer Stackup:>"
def test_parameter():
p = IPC356_Parameter('VER', 'IPC-D-356A')
assert_equal(p.parameter, 'VER')
assert_equal(p.value, 'IPC-D-356A')
p = IPC356_Parameter.from_line('P VER IPC-D-356A ')
assert_equal(p.parameter, 'VER')
assert_equal(p.value, 'IPC-D-356A')
assert_raises(ValueError, IPC356_Parameter.from_line,
'C Layer Stackup: ')
assert_equal(str(p), '<IPC-D-356 Parameter: VER=IPC-D-356A>')
p = IPC356_Parameter("VER", "IPC-D-356A")
assert p.parameter == "VER"
assert p.value == "IPC-D-356A"
p = IPC356_Parameter.from_line("P VER IPC-D-356A ")
assert p.parameter == "VER"
assert p.value == "IPC-D-356A"
pytest.raises(ValueError, IPC356_Parameter.from_line, "C Layer Stackup: ")
assert str(p) == "<IPC-D-356 Parameter: VER=IPC-D-356A>"
def test_eof():
e = IPC356_EndOfFile()
assert_equal(e.to_netlist(), '999')
assert_equal(str(e), '<IPC-D-356 EOF>')
assert e.to_netlist() == "999"
assert str(e) == "<IPC-D-356 EOF>"
def test_outline():
type = 'BOARD_EDGE'
points = [(0.01, 0.01), (2., 2.), (4., 2.), (4., 6.)]
type = "BOARD_EDGE"
points = [(0.01, 0.01), (2.0, 2.0), (4.0, 2.0), (4.0, 6.0)]
b = IPC356_Outline(type, points)
assert_equal(b.type, type)
assert_equal(b.points, points)
b = IPC356_Outline.from_line('389BOARD_EDGE X100Y100 X20000Y20000 X40000 Y60000',
FileSettings(units='inch'))
assert_equal(b.type, 'BOARD_EDGE')
assert_equal(b.points, points)
assert b.type == type
assert b.points == points
b = IPC356_Outline.from_line(
"389BOARD_EDGE X100Y100 X20000Y20000 X40000 Y60000",
FileSettings(units="inch"),
)
assert b.type == "BOARD_EDGE"
assert b.points == points
def test_test_record():
assert_raises(ValueError, IPC356_TestRecord.from_line,
'P JOB', FileSettings())
record_string = '317+5VDC VIA - D0150PA00X 006647Y 012900X0000 S3'
r = IPC356_TestRecord.from_line(record_string, FileSettings(units='inch'))
assert_equal(r.feature_type, 'through-hole')
assert_equal(r.net_name, '+5VDC')
assert_equal(r.id, 'VIA')
assert_almost_equal(r.hole_diameter, 0.015)
assert_true(r.plated)
assert_equal(r.access, 'both')
assert_almost_equal(r.x_coord, 0.6647)
assert_almost_equal(r.y_coord, 1.29)
assert_equal(r.rect_x, 0.)
assert_equal(r.soldermask_info, 'both')
r = IPC356_TestRecord.from_line(record_string, FileSettings(units='metric'))
assert_almost_equal(r.hole_diameter, 0.15)
assert_almost_equal(r.x_coord, 6.647)
assert_almost_equal(r.y_coord, 12.9)
assert_equal(r.rect_x, 0.)
assert_equal(str(r), '<IPC-D-356 +5VDC Test Record: through-hole>')
pytest.raises(ValueError, IPC356_TestRecord.from_line, "P JOB", FileSettings())
record_string = (
"317+5VDC VIA - D0150PA00X 006647Y 012900X0000 S3"
)
r = IPC356_TestRecord.from_line(record_string, FileSettings(units="inch"))
assert r.feature_type == "through-hole"
assert r.net_name == "+5VDC"
assert r.id == "VIA"
pytest.approx(r.hole_diameter, 0.015)
assert r.plated
assert r.access == "both"
pytest.approx(r.x_coord, 0.6647)
pytest.approx(r.y_coord, 1.29)
assert r.rect_x == 0.0
assert r.soldermask_info == "both"
r = IPC356_TestRecord.from_line(record_string, FileSettings(units="metric"))
pytest.approx(r.hole_diameter, 0.15)
pytest.approx(r.x_coord, 6.647)
pytest.approx(r.y_coord, 12.9)
assert r.rect_x == 0.0
assert str(r) == "<IPC-D-356 +5VDC Test Record: through-hole>"
record_string = '327+3.3VDC R40 -1 PA01X 032100Y 007124X0236Y0315R180 S0'
r = IPC356_TestRecord.from_line(record_string, FileSettings(units='inch'))
assert_equal(r.feature_type, 'smt')
assert_equal(r.net_name, '+3.3VDC')
assert_equal(r.id, 'R40')
assert_equal(r.pin, '1')
assert_true(r.plated)
assert_equal(r.access, 'top')
assert_almost_equal(r.x_coord, 3.21)
assert_almost_equal(r.y_coord, 0.7124)
assert_almost_equal(r.rect_x, 0.0236)
assert_almost_equal(r.rect_y, 0.0315)
assert_equal(r.rect_rotation, 180)
assert_equal(r.soldermask_info, 'none')
r = IPC356_TestRecord.from_line(
record_string, FileSettings(units='metric'))
assert_almost_equal(r.x_coord, 32.1)
assert_almost_equal(r.y_coord, 7.124)
assert_almost_equal(r.rect_x, 0.236)
assert_almost_equal(r.rect_y, 0.315)
record_string = (
"327+3.3VDC R40 -1 PA01X 032100Y 007124X0236Y0315R180 S0"
)
r = IPC356_TestRecord.from_line(record_string, FileSettings(units="inch"))
assert r.feature_type == "smt"
assert r.net_name == "+3.3VDC"
assert r.id == "R40"
assert r.pin == "1"
assert r.plated
assert r.access == "top"
pytest.approx(r.x_coord, 3.21)
pytest.approx(r.y_coord, 0.7124)
pytest.approx(r.rect_x, 0.0236)
pytest.approx(r.rect_y, 0.0315)
assert r.rect_rotation == 180
assert r.soldermask_info == "none"
r = IPC356_TestRecord.from_line(record_string, FileSettings(units="metric"))
pytest.approx(r.x_coord, 32.1)
pytest.approx(r.y_coord, 7.124)
pytest.approx(r.rect_x, 0.236)
pytest.approx(r.rect_y, 0.315)
record_string = '317 J4 -M2 D0330PA00X 012447Y 008030X0000 S1'
r = IPC356_TestRecord.from_line(record_string, FileSettings(units='inch'))
assert_equal(r.feature_type, 'through-hole')
assert_equal(r.id, 'J4')
assert_equal(r.pin, 'M2')
assert_almost_equal(r.hole_diameter, 0.033)
assert_true(r.plated)
assert_equal(r.access, 'both')
assert_almost_equal(r.x_coord, 1.2447)
assert_almost_equal(r.y_coord, 0.8030)
assert_almost_equal(r.rect_x, 0.)
assert_equal(r.soldermask_info, 'primary side')
record_string = (
"317 J4 -M2 D0330PA00X 012447Y 008030X0000 S1"
)
r = IPC356_TestRecord.from_line(record_string, FileSettings(units="inch"))
assert r.feature_type == "through-hole"
assert r.id == "J4"
assert r.pin == "M2"
pytest.approx(r.hole_diameter, 0.033)
assert r.plated
assert r.access == "both"
pytest.approx(r.x_coord, 1.2447)
pytest.approx(r.y_coord, 0.8030)
pytest.approx(r.rect_x, 0.0)
assert r.soldermask_info == "primary side"
record_string = '317SCL COMMUNICATION-1 D 40PA00X 34000Y 20000X 600Y1200R270 '
r = IPC356_TestRecord.from_line(record_string, FileSettings(units='inch'))
assert_equal(r.feature_type, 'through-hole')
assert_equal(r.net_name, 'SCL')
assert_equal(r.id, 'COMMUNICATION')
assert_equal(r.pin, '1')
assert_almost_equal(r.hole_diameter, 0.004)
assert_true(r.plated)
assert_almost_equal(r.x_coord, 3.4)
assert_almost_equal(r.y_coord, 2.0)
record_string = "317SCL COMMUNICATION-1 D 40PA00X 34000Y 20000X 600Y1200R270 "
r = IPC356_TestRecord.from_line(record_string, FileSettings(units="inch"))
assert r.feature_type == "through-hole"
assert r.net_name == "SCL"
assert r.id == "COMMUNICATION"
assert r.pin == "1"
pytest.approx(r.hole_diameter, 0.004)
assert r.plated
pytest.approx(r.x_coord, 3.4)
pytest.approx(r.y_coord, 2.0)

View file

@ -18,128 +18,141 @@
import os
from .tests import *
from ..layers import *
from ..common import read
NCDRILL_FILE = os.path.join(os.path.dirname(__file__),
'resources/ncdrill.DRD')
NETLIST_FILE = os.path.join(os.path.dirname(__file__),
'resources/ipc-d-356.ipc')
COPPER_FILE = os.path.join(os.path.dirname(__file__),
'resources/top_copper.GTL')
NCDRILL_FILE = os.path.join(os.path.dirname(__file__), "resources/ncdrill.DRD")
NETLIST_FILE = os.path.join(os.path.dirname(__file__), "resources/ipc-d-356.ipc")
COPPER_FILE = os.path.join(os.path.dirname(__file__), "resources/top_copper.GTL")
def test_guess_layer_class():
""" Test layer type inferred correctly from filename
"""
# Add any specific test cases here (filename, layer_class)
test_vectors = [(None, 'unknown'), ('NCDRILL.TXT', 'unknown'),
('example_board.gtl', 'top'),
('exampmle_board.sst', 'topsilk'),
('ipc-d-356.ipc', 'ipc_netlist'), ]
test_vectors = [
(None, "unknown"),
("NCDRILL.TXT", "unknown"),
("example_board.gtl", "top"),
("exampmle_board.sst", "topsilk"),
("ipc-d-356.ipc", "ipc_netlist"),
]
for hint in hints:
for ext in hint.ext:
assert_equal(hint.layer, guess_layer_class('board.{}'.format(ext)))
assert hint.layer == guess_layer_class("board.{}".format(ext))
for name in hint.name:
assert_equal(hint.layer, guess_layer_class('{}.pho'.format(name)))
assert hint.layer == guess_layer_class("{}.pho".format(name))
for filename, layer_class in test_vectors:
assert_equal(layer_class, guess_layer_class(filename))
assert layer_class == guess_layer_class(filename)
def test_guess_layer_class_regex():
""" Test regular expressions for layer matching
"""
# Add any specific test case (filename, layer_class)
test_vectors = [('test - top copper.gbr', 'top'),
('test - copper top.gbr', 'top'), ]
test_vectors = [("test - top copper.gbr", "top"), ("test - copper top.gbr", "top")]
# Add custom regular expressions
layer_hints = [
Hint(layer='top',
ext=[],
name=[],
regex=r'(.*)(\scopper top|\stop copper).gbr',
content=[]
),
Hint(
layer="top",
ext=[],
name=[],
regex=r"(.*)(\scopper top|\stop copper).gbr",
content=[],
)
]
hints.extend(layer_hints)
for filename, layer_class in test_vectors:
assert_equal(layer_class, guess_layer_class(filename))
assert layer_class == guess_layer_class(filename)
def test_guess_layer_class_by_content():
""" Test layer class by checking content
"""
expected_layer_class = 'bottom'
filename = os.path.join(os.path.dirname(__file__),
'resources/example_guess_by_content.g0')
expected_layer_class = "bottom"
filename = os.path.join(
os.path.dirname(__file__), "resources/example_guess_by_content.g0"
)
layer_hints = [
Hint(layer='bottom',
ext=[],
name=[],
regex='',
content=['G04 Layer name: Bottom']
)
Hint(
layer="bottom",
ext=[],
name=[],
regex="",
content=["G04 Layer name: Bottom"],
)
]
hints.extend(layer_hints)
assert_equal(expected_layer_class, guess_layer_class_by_content(filename))
assert expected_layer_class == guess_layer_class_by_content(filename)
def test_sort_layers():
""" Test layer ordering
"""
layers = [
PCBLayer(layer_class='drawing'),
PCBLayer(layer_class='drill'),
PCBLayer(layer_class='bottompaste'),
PCBLayer(layer_class='bottomsilk'),
PCBLayer(layer_class='bottommask'),
PCBLayer(layer_class='bottom'),
PCBLayer(layer_class='internal'),
PCBLayer(layer_class='top'),
PCBLayer(layer_class='topmask'),
PCBLayer(layer_class='topsilk'),
PCBLayer(layer_class='toppaste'),
PCBLayer(layer_class='outline'),
PCBLayer(layer_class="drawing"),
PCBLayer(layer_class="drill"),
PCBLayer(layer_class="bottompaste"),
PCBLayer(layer_class="bottomsilk"),
PCBLayer(layer_class="bottommask"),
PCBLayer(layer_class="bottom"),
PCBLayer(layer_class="internal"),
PCBLayer(layer_class="top"),
PCBLayer(layer_class="topmask"),
PCBLayer(layer_class="topsilk"),
PCBLayer(layer_class="toppaste"),
PCBLayer(layer_class="outline"),
]
layer_order = ['outline', 'toppaste', 'topsilk', 'topmask', 'top',
'internal', 'bottom', 'bottommask', 'bottomsilk',
'bottompaste', 'drill', 'drawing']
layer_order = [
"outline",
"toppaste",
"topsilk",
"topmask",
"top",
"internal",
"bottom",
"bottommask",
"bottomsilk",
"bottompaste",
"drill",
"drawing",
]
bottom_order = list(reversed(layer_order[:10])) + layer_order[10:]
assert_equal([l.layer_class for l in sort_layers(layers)], layer_order)
assert_equal([l.layer_class for l in sort_layers(layers, from_top=False)],
bottom_order)
assert [l.layer_class for l in sort_layers(layers)] == layer_order
assert [l.layer_class for l in sort_layers(layers, from_top=False)] == bottom_order
def test_PCBLayer_from_file():
layer = PCBLayer.from_cam(read(COPPER_FILE))
assert_true(isinstance(layer, PCBLayer))
assert isinstance(layer, PCBLayer)
layer = PCBLayer.from_cam(read(NCDRILL_FILE))
assert_true(isinstance(layer, DrillLayer))
assert isinstance(layer, DrillLayer)
layer = PCBLayer.from_cam(read(NETLIST_FILE))
assert_true(isinstance(layer, PCBLayer))
assert_equal(layer.layer_class, 'ipc_netlist')
assert isinstance(layer, PCBLayer)
assert layer.layer_class == "ipc_netlist"
def test_PCBLayer_bounds():
source = read(COPPER_FILE)
layer = PCBLayer.from_cam(source)
assert_equal(source.bounds, layer.bounds)
assert source.bounds == layer.bounds
def test_DrillLayer_from_cam():
no_exceptions = True
try:
layer = DrillLayer.from_cam(read(NCDRILL_FILE))
assert_true(isinstance(layer, DrillLayer))
assert isinstance(layer, DrillLayer)
except:
no_exceptions = False
assert_true(no_exceptions)
assert no_exceptions

File diff suppressed because it is too large Load diff

View file

@ -3,53 +3,53 @@
# Author: Hamilton Kibbe <ham@hamiltonkib.be>
import os
import pytest
from ..rs274x import read, GerberFile
from .tests import *
TOP_COPPER_FILE = os.path.join(os.path.dirname(__file__),
'resources/top_copper.GTL')
TOP_COPPER_FILE = os.path.join(os.path.dirname(__file__), "resources/top_copper.GTL")
MULTILINE_READ_FILE = os.path.join(os.path.dirname(__file__),
'resources/multiline_read.ger')
MULTILINE_READ_FILE = os.path.join(
os.path.dirname(__file__), "resources/multiline_read.ger"
)
def test_read():
top_copper = read(TOP_COPPER_FILE)
assert(isinstance(top_copper, GerberFile))
assert isinstance(top_copper, GerberFile)
def test_multiline_read():
multiline = read(MULTILINE_READ_FILE)
assert(isinstance(multiline, GerberFile))
assert_equal(10, len(multiline.statements))
assert isinstance(multiline, GerberFile)
assert 10 == len(multiline.statements)
def test_comments_parameter():
top_copper = read(TOP_COPPER_FILE)
assert_equal(top_copper.comments[0], 'This is a comment,:')
assert top_copper.comments[0] == "This is a comment,:"
def test_size_parameter():
top_copper = read(TOP_COPPER_FILE)
size = top_copper.size
assert_almost_equal(size[0], 2.256900, 6)
assert_almost_equal(size[1], 1.500000, 6)
pytest.approx(size[0], 2.256900, 6)
pytest.approx(size[1], 1.500000, 6)
def test_conversion():
top_copper = read(TOP_COPPER_FILE)
assert_equal(top_copper.units, 'inch')
assert top_copper.units == "inch"
top_copper_inch = read(TOP_COPPER_FILE)
top_copper.to_metric()
for statement in top_copper_inch.statements:
statement.to_metric()
for primitive in top_copper_inch.primitives:
primitive.to_metric()
assert_equal(top_copper.units, 'metric')
assert top_copper.units == "metric"
for i, m in zip(top_copper.statements, top_copper_inch.statements):
assert_equal(i, m)
assert i == m
for i, m in zip(top_copper.primitives, top_copper_inch.primitives):
assert_equal(i, m)
assert i == m

View file

@ -7,62 +7,86 @@ import os
from ..render.rs274x_backend import Rs274xContext
from ..rs274x import read
from .tests import *
def test_render_two_boxes():
"""Umaco exapmle of two boxes"""
_test_render('resources/example_two_square_boxes.gbr', 'golden/example_two_square_boxes.gbr')
_test_render(
"resources/example_two_square_boxes.gbr", "golden/example_two_square_boxes.gbr"
)
def _test_render_single_quadrant():
"""Umaco exapmle of a single quadrant arc"""
# TODO there is probably a bug here
_test_render('resources/example_single_quadrant.gbr', 'golden/example_single_quadrant.gbr')
_test_render(
"resources/example_single_quadrant.gbr", "golden/example_single_quadrant.gbr"
)
def _test_render_simple_contour():
"""Umaco exapmle of a simple arrow-shaped contour"""
_test_render('resources/example_simple_contour.gbr', 'golden/example_simple_contour.gbr')
_test_render(
"resources/example_simple_contour.gbr", "golden/example_simple_contour.gbr"
)
def _test_render_single_contour_1():
"""Umaco example of a single contour
The resulting image for this test is used by other tests because they must generate the same output."""
_test_render('resources/example_single_contour_1.gbr', 'golden/example_single_contour.gbr')
_test_render(
"resources/example_single_contour_1.gbr", "golden/example_single_contour.gbr"
)
def _test_render_single_contour_2():
"""Umaco exapmle of a single contour, alternate contour end order
The resulting image for this test is used by other tests because they must generate the same output."""
_test_render('resources/example_single_contour_2.gbr', 'golden/example_single_contour.gbr')
_test_render(
"resources/example_single_contour_2.gbr", "golden/example_single_contour.gbr"
)
def _test_render_single_contour_3():
"""Umaco exapmle of a single contour with extra line"""
_test_render('resources/example_single_contour_3.gbr', 'golden/example_single_contour_3.gbr')
_test_render(
"resources/example_single_contour_3.gbr", "golden/example_single_contour_3.gbr"
)
def _test_render_not_overlapping_contour():
"""Umaco example of D02 staring a second contour"""
_test_render('resources/example_not_overlapping_contour.gbr', 'golden/example_not_overlapping_contour.gbr')
_test_render(
"resources/example_not_overlapping_contour.gbr",
"golden/example_not_overlapping_contour.gbr",
)
def _test_render_not_overlapping_touching():
"""Umaco example of D02 staring a second contour"""
_test_render('resources/example_not_overlapping_touching.gbr', 'golden/example_not_overlapping_touching.gbr')
_test_render(
"resources/example_not_overlapping_touching.gbr",
"golden/example_not_overlapping_touching.gbr",
)
def _test_render_overlapping_touching():
"""Umaco example of D02 staring a second contour"""
_test_render('resources/example_overlapping_touching.gbr', 'golden/example_overlapping_touching.gbr')
_test_render(
"resources/example_overlapping_touching.gbr",
"golden/example_overlapping_touching.gbr",
)
def _test_render_overlapping_contour():
"""Umaco example of D02 staring a second contour"""
_test_render('resources/example_overlapping_contour.gbr', 'golden/example_overlapping_contour.gbr')
_test_render(
"resources/example_overlapping_contour.gbr",
"golden/example_overlapping_contour.gbr",
)
def _DISABLED_test_render_level_holes():
@ -70,76 +94,96 @@ def _DISABLED_test_render_level_holes():
# TODO This is clearly rendering wrong. I'm temporarily checking this in because there are more
# rendering fixes in the related repository that may resolve these.
_test_render('resources/example_level_holes.gbr', 'golden/example_overlapping_contour.gbr')
_test_render(
"resources/example_level_holes.gbr", "golden/example_overlapping_contour.gbr"
)
def _DISABLED_test_render_cutin():
"""Umaco example of using a cutin"""
# TODO This is clearly rendering wrong.
_test_render('resources/example_cutin.gbr', 'golden/example_cutin.gbr')
_test_render("resources/example_cutin.gbr", "golden/example_cutin.gbr")
def _test_render_fully_coincident():
"""Umaco example of coincident lines rendering two contours"""
_test_render('resources/example_fully_coincident.gbr', 'golden/example_fully_coincident.gbr')
_test_render(
"resources/example_fully_coincident.gbr", "golden/example_fully_coincident.gbr"
)
def _test_render_coincident_hole():
"""Umaco example of coincident lines rendering a hole in the contour"""
_test_render('resources/example_coincident_hole.gbr', 'golden/example_coincident_hole.gbr')
_test_render(
"resources/example_coincident_hole.gbr", "golden/example_coincident_hole.gbr"
)
def _test_render_cutin_multiple():
"""Umaco example of a region with multiple cutins"""
_test_render('resources/example_cutin_multiple.gbr', 'golden/example_cutin_multiple.gbr')
_test_render(
"resources/example_cutin_multiple.gbr", "golden/example_cutin_multiple.gbr"
)
def _test_flash_circle():
"""Umaco example a simple circular flash with and without a hole"""
_test_render('resources/example_flash_circle.gbr', 'golden/example_flash_circle.gbr')
_test_render(
"resources/example_flash_circle.gbr", "golden/example_flash_circle.gbr"
)
def _test_flash_rectangle():
"""Umaco example a simple rectangular flash with and without a hole"""
_test_render('resources/example_flash_rectangle.gbr', 'golden/example_flash_rectangle.gbr')
_test_render(
"resources/example_flash_rectangle.gbr", "golden/example_flash_rectangle.gbr"
)
def _test_flash_obround():
"""Umaco example a simple obround flash with and without a hole"""
_test_render('resources/example_flash_obround.gbr', 'golden/example_flash_obround.gbr')
_test_render(
"resources/example_flash_obround.gbr", "golden/example_flash_obround.gbr"
)
def _test_flash_polygon():
"""Umaco example a simple polygon flash with and without a hole"""
_test_render('resources/example_flash_polygon.gbr', 'golden/example_flash_polygon.gbr')
_test_render(
"resources/example_flash_polygon.gbr", "golden/example_flash_polygon.gbr"
)
def _test_holes_dont_clear():
"""Umaco example that an aperture with a hole does not clear the area"""
_test_render('resources/example_holes_dont_clear.gbr', 'golden/example_holes_dont_clear.gbr')
_test_render(
"resources/example_holes_dont_clear.gbr", "golden/example_holes_dont_clear.gbr"
)
def _test_render_am_exposure_modifier():
"""Umaco example that an aperture macro with a hole does not clear the area"""
_test_render('resources/example_am_exposure_modifier.gbr', 'golden/example_am_exposure_modifier.gbr')
_test_render(
"resources/example_am_exposure_modifier.gbr",
"golden/example_am_exposure_modifier.gbr",
)
def _resolve_path(path):
return os.path.join(os.path.dirname(__file__),
path)
return os.path.join(os.path.dirname(__file__), path)
def _test_render(gerber_path, png_expected_path, create_output_path = None):
def _test_render(gerber_path, png_expected_path, create_output_path=None):
"""Render the gerber file and compare to the expected PNG output.
Parameters
@ -168,18 +212,21 @@ def _test_render(gerber_path, png_expected_path, create_output_path = None):
# If we want to write the file bytes, do it now. This happens
if create_output_path:
with open(create_output_path, 'wb') as out_file:
with open(create_output_path, "wb") as out_file:
out_file.write(actual_contents.getvalue())
# Creating the output is dangerous - it could overwrite the expected result.
# So if we are creating the output, we make the test fail on purpose so you
# won't forget to disable this
assert_false(True, 'Test created the output %s. This needs to be disabled to make sure the test behaves correctly' % (create_output_path,))
assert not True, (
"Test created the output %s. This needs to be disabled to make sure the test behaves correctly"
% (create_output_path,)
)
# Read the expected PNG file
with open(png_expected_path, 'r') as expected_file:
with open(png_expected_path, "r") as expected_file:
expected_contents = expected_file.read()
assert_equal(expected_contents, actual_contents.getvalue())
assert expected_contents == actual_contents.getvalue()
return gerber

View file

@ -3,7 +3,7 @@
# Author: Hamilton Kibbe <ham@hamiltonkib.be>
from .tests import assert_equal, assert_raises
import pytest
from ..utils import *
@ -14,60 +14,99 @@ def test_zero_suppression():
fmt = (2, 5)
# Test leading zero suppression
zero_suppression = 'leading'
test_cases = [('1', 0.00001), ('10', 0.0001), ('100', 0.001),
('1000', 0.01), ('10000', 0.1), ('100000', 1.0),
('1000000', 10.0), ('-1', -0.00001), ('-10', -0.0001),
('-100', -0.001), ('-1000', -0.01), ('-10000', -0.1),
('-100000', -1.0), ('-1000000', -10.0),
('0', 0.0)]
zero_suppression = "leading"
test_cases = [
("1", 0.00001),
("10", 0.0001),
("100", 0.001),
("1000", 0.01),
("10000", 0.1),
("100000", 1.0),
("1000000", 10.0),
("-1", -0.00001),
("-10", -0.0001),
("-100", -0.001),
("-1000", -0.01),
("-10000", -0.1),
("-100000", -1.0),
("-1000000", -10.0),
("0", 0.0),
]
for string, value in test_cases:
assert_equal(value, parse_gerber_value(string, fmt, zero_suppression))
assert_equal(string, write_gerber_value(value, fmt, zero_suppression))
assert value == parse_gerber_value(string, fmt, zero_suppression)
assert string == write_gerber_value(value, fmt, zero_suppression)
# Test trailing zero suppression
zero_suppression = 'trailing'
test_cases = [('1', 10.0), ('01', 1.0), ('001', 0.1), ('0001', 0.01),
('00001', 0.001), ('000001', 0.0001),
('0000001', 0.00001), ('-1', -10.0), ('-01', -1.0),
('-001', -0.1), ('-0001', -0.01), ('-00001', -0.001),
('-000001', -0.0001), ('-0000001', -0.00001),
('0', 0.0)]
zero_suppression = "trailing"
test_cases = [
("1", 10.0),
("01", 1.0),
("001", 0.1),
("0001", 0.01),
("00001", 0.001),
("000001", 0.0001),
("0000001", 0.00001),
("-1", -10.0),
("-01", -1.0),
("-001", -0.1),
("-0001", -0.01),
("-00001", -0.001),
("-000001", -0.0001),
("-0000001", -0.00001),
("0", 0.0),
]
for string, value in test_cases:
assert_equal(value, parse_gerber_value(string, fmt, zero_suppression))
assert_equal(string, write_gerber_value(value, fmt, zero_suppression))
assert value == parse_gerber_value(string, fmt, zero_suppression)
assert string == write_gerber_value(value, fmt, zero_suppression)
assert_equal(write_gerber_value(0.000000001, fmt, 'leading'), '0')
assert_equal(write_gerber_value(0.000000001, fmt, 'trailing'), '0')
assert write_gerber_value(0.000000001, fmt, "leading") == "0"
assert write_gerber_value(0.000000001, fmt, "trailing") == "0"
def test_format():
""" Test gerber value parser and writer handle format correctly
"""
zero_suppression = 'leading'
test_cases = [((2, 7), '1', 0.0000001), ((2, 6), '1', 0.000001),
((2, 5), '1', 0.00001), ((2, 4), '1', 0.0001),
((2, 3), '1', 0.001), ((2, 2), '1', 0.01),
((2, 1), '1', 0.1), ((2, 7), '-1', -0.0000001),
((2, 6), '-1', -0.000001), ((2, 5), '-1', -0.00001),
((2, 4), '-1', -0.0001), ((2, 3), '-1', -0.001),
((2, 2), '-1', -0.01), ((2, 1), '-1', -0.1),
((2, 6), '0', 0)]
zero_suppression = "leading"
test_cases = [
((2, 7), "1", 0.0000001),
((2, 6), "1", 0.000001),
((2, 5), "1", 0.00001),
((2, 4), "1", 0.0001),
((2, 3), "1", 0.001),
((2, 2), "1", 0.01),
((2, 1), "1", 0.1),
((2, 7), "-1", -0.0000001),
((2, 6), "-1", -0.000001),
((2, 5), "-1", -0.00001),
((2, 4), "-1", -0.0001),
((2, 3), "-1", -0.001),
((2, 2), "-1", -0.01),
((2, 1), "-1", -0.1),
((2, 6), "0", 0),
]
for fmt, string, value in test_cases:
assert_equal(value, parse_gerber_value(string, fmt, zero_suppression))
assert_equal(string, write_gerber_value(value, fmt, zero_suppression))
assert value == parse_gerber_value(string, fmt, zero_suppression)
assert string == write_gerber_value(value, fmt, zero_suppression)
zero_suppression = 'trailing'
test_cases = [((6, 5), '1', 100000.0), ((5, 5), '1', 10000.0),
((4, 5), '1', 1000.0), ((3, 5), '1', 100.0),
((2, 5), '1', 10.0), ((1, 5), '1', 1.0),
((6, 5), '-1', -100000.0), ((5, 5), '-1', -10000.0),
((4, 5), '-1', -1000.0), ((3, 5), '-1', -100.0),
((2, 5), '-1', -10.0), ((1, 5), '-1', -1.0),
((2, 5), '0', 0)]
zero_suppression = "trailing"
test_cases = [
((6, 5), "1", 100000.0),
((5, 5), "1", 10000.0),
((4, 5), "1", 1000.0),
((3, 5), "1", 100.0),
((2, 5), "1", 10.0),
((1, 5), "1", 1.0),
((6, 5), "-1", -100000.0),
((5, 5), "-1", -10000.0),
((4, 5), "-1", -1000.0),
((3, 5), "-1", -100.0),
((2, 5), "-1", -10.0),
((1, 5), "-1", -1.0),
((2, 5), "0", 0),
]
for fmt, string, value in test_cases:
assert_equal(value, parse_gerber_value(string, fmt, zero_suppression))
assert_equal(string, write_gerber_value(value, fmt, zero_suppression))
assert value == parse_gerber_value(string, fmt, zero_suppression)
assert string == write_gerber_value(value, fmt, zero_suppression)
def test_decimal_truncation():
@ -76,54 +115,53 @@ def test_decimal_truncation():
value = 1.123456789
for x in range(10):
result = decimal_string(value, precision=x)
calculated = '1.' + ''.join(str(y) for y in range(1, x + 1))
assert_equal(result, calculated)
calculated = "1." + "".join(str(y) for y in range(1, x + 1))
assert result == calculated
def test_decimal_padding():
""" Test decimal_string padding
"""
value = 1.123
assert_equal(decimal_string(value, precision=3, padding=True), '1.123')
assert_equal(decimal_string(value, precision=4, padding=True), '1.1230')
assert_equal(decimal_string(value, precision=5, padding=True), '1.12300')
assert_equal(decimal_string(value, precision=6, padding=True), '1.123000')
assert_equal(decimal_string(0, precision=6, padding=True), '0.000000')
assert decimal_string(value, precision=3, padding=True) == "1.123"
assert decimal_string(value, precision=4, padding=True) == "1.1230"
assert decimal_string(value, precision=5, padding=True) == "1.12300"
assert decimal_string(value, precision=6, padding=True) == "1.123000"
assert decimal_string(0, precision=6, padding=True) == "0.000000"
def test_parse_format_validation():
""" Test parse_gerber_value() format validation
"""
assert_raises(ValueError, parse_gerber_value, '00001111', (7, 5))
assert_raises(ValueError, parse_gerber_value, '00001111', (5, 8))
assert_raises(ValueError, parse_gerber_value, '00001111', (13, 1))
pytest.raises(ValueError, parse_gerber_value, "00001111", (7, 5))
pytest.raises(ValueError, parse_gerber_value, "00001111", (5, 8))
pytest.raises(ValueError, parse_gerber_value, "00001111", (13, 1))
def test_write_format_validation():
""" Test write_gerber_value() format validation
"""
assert_raises(ValueError, write_gerber_value, 69.0, (7, 5))
assert_raises(ValueError, write_gerber_value, 69.0, (5, 8))
assert_raises(ValueError, write_gerber_value, 69.0, (13, 1))
pytest.raises(ValueError, write_gerber_value, 69.0, (7, 5))
pytest.raises(ValueError, write_gerber_value, 69.0, (5, 8))
pytest.raises(ValueError, write_gerber_value, 69.0, (13, 1))
def test_detect_format_with_short_file():
""" Verify file format detection works with short files
"""
assert_equal('unknown', detect_file_format('gerber/tests/__init__.py'))
assert "unknown" == detect_file_format("gerber/tests/__init__.py")
def test_validate_coordinates():
assert_raises(TypeError, validate_coordinates, 3)
assert_raises(TypeError, validate_coordinates, 3.1)
assert_raises(TypeError, validate_coordinates, '14')
assert_raises(TypeError, validate_coordinates, (0,))
assert_raises(TypeError, validate_coordinates, (0, 1, 2))
assert_raises(TypeError, validate_coordinates, (0, 'string'))
pytest.raises(TypeError, validate_coordinates, 3)
pytest.raises(TypeError, validate_coordinates, 3.1)
pytest.raises(TypeError, validate_coordinates, "14")
pytest.raises(TypeError, validate_coordinates, (0,))
pytest.raises(TypeError, validate_coordinates, (0, 1, 2))
pytest.raises(TypeError, validate_coordinates, (0, "string"))
def test_convex_hull():
points = [(0, 0), (1, 0), (1, 1), (0.5, 0.5), (0, 1), (0, 0)]
expected = [(0, 0), (1, 0), (1, 1), (0, 1), (0, 0)]
assert_equal(set(convex_hull(points)), set(expected))
assert set(convex_hull(points)) == set(expected)

View file

@ -1,25 +0,0 @@
#! /usr/bin/env python
# -*- coding: utf-8 -*-
# Author: Hamilton Kibbe <ham@hamiltonkib.be>
from nose.tools import assert_in
from nose.tools import assert_not_in
from nose.tools import assert_equal
from nose.tools import assert_not_equal
from nose.tools import assert_almost_equal
from nose.tools import assert_true
from nose.tools import assert_false
from nose.tools import assert_raises
from nose.tools import raises
from nose import with_setup
__all__ = ['assert_in', 'assert_not_in', 'assert_equal', 'assert_not_equal',
'assert_almost_equal', 'assert_array_almost_equal', 'assert_true',
'assert_false', 'assert_raises', 'raises', 'with_setup']
def assert_array_almost_equal(arr1, arr2, decimal=6):
assert_equal(len(arr1), len(arr2))
for i in range(len(arr1)):
assert_almost_equal(arr1[i], arr2[i], decimal)

View file

@ -1,5 +1,5 @@
# install base requirements
-r requirements.txt
coverage==4.5.4
nose==1.3.7
pytest==4.6.6
pytest-cov==2.8.1