Manual merge resolution
This commit is contained in:
commit
5288a8314b
23 changed files with 2431 additions and 2110 deletions
|
|
@ -1,4 +1,8 @@
|
|||
[run]
|
||||
branch = True
|
||||
source = gerber
|
||||
|
||||
[report]
|
||||
ignore_errors = True
|
||||
omit =
|
||||
*/python?.?/*
|
||||
*/site-packages/nose/*
|
||||
gerber/tests/*
|
||||
|
|
|
|||
46
.github/workflows/pcb-tools.yml
vendored
46
.github/workflows/pcb-tools.yml
vendored
|
|
@ -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
1
.gitignore
vendored
|
|
@ -48,3 +48,4 @@ Thumbs.db
|
|||
|
||||
# Virtual environment
|
||||
venv
|
||||
coverage.xml
|
||||
|
|
|
|||
12
Makefile
12
Makefile
|
|
@ -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:
|
||||
|
|
|
|||
10
README.md
10
README.md
|
|
@ -1,8 +1,8 @@
|
|||
pcb-tools
|
||||
============
|
||||
[](https://travis-ci.org/curtacircuitos/pcb-tools)
|
||||
[](https://coveralls.io/r/curtacircuitos/pcb-tools?branch=master)
|
||||
[](https://readthedocs.org/projects/pcb-tools/?badge=latest)
|
||||
[](https://github.com/curtacircuitos/pcb-tools/actions)
|
||||
[](https://codecov.io/gh/curtacircuitos/pcb-tools)
|
||||
[](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
|
||||
|
|
|
|||
|
|
@ -119,3 +119,4 @@ def main():
|
|||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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]]),
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
|
|
|
|||
|
|
@ -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__)
|
||||
|
|
|
|||
|
|
@ -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
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue