Skip to content

Commit c4778e3

Browse files
committed
Add a basic plot test
1 parent cc06a8e commit c4778e3

File tree

10 files changed

+458
-2
lines changed

10 files changed

+458
-2
lines changed

.gitignore

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,4 +26,6 @@ wheels/
2626
MANIFEST
2727

2828

29-
.pytest_cache
29+
.pytest_cache
30+
31+
*.kicad_pcb-bak

README.md

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,10 +50,20 @@ export PYTHONPATH=~/local/kicad/lib/python2.7/site-packages
5050
export LD_LIBRARY_PATH=~/local/kicad/lib64
5151
```
5252

53+
If you've installed "normally", you should not need to do this.
54+
55+
## Testing
56+
57+
There are some tests. Run them with pytest:
58+
59+
```
60+
pytest
61+
```
62+
5363
# TODO list
5464

5565
There are some things that still need work:
5666

5767
* DRC checking - that can't be done over the Python interface yet. If/when
5868
this is added to KiCad, KiPlot will be able to also be used for DRC
59-
functional tests instead of a complex additonal test harness in C++.
69+
functional tests instead of a complex additonal test harness in C++.

src/kiplot/plot_config.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11

2+
import os
3+
24
import pcbnew
35

46
from . import error
@@ -446,6 +448,27 @@ def __init__(self):
446448
def add_output(self, new_op):
447449
self._outputs.append(new_op)
448450

451+
def get_output_by_name(self, output_name):
452+
"""
453+
Gets an output with a given name.
454+
455+
@param output_name the name of the output to find
456+
"""
457+
for o in self.outputs:
458+
459+
if o.name == output_name:
460+
return o
461+
462+
return None
463+
464+
def resolve_output_dir_for_name(self, output_name):
465+
"""
466+
Get the output dir for a given output name
467+
"""
468+
469+
o = self.get_output_by_name(output_name)
470+
return os.path.join(self.outdir, o.outdir) if o else None
471+
449472
def validate(self):
450473

451474
errs = []
Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
(kicad_pcb (version 20171130) (host pcbnew "(5.0.0-rc2-76-gb5f63567d)")
2+
3+
(general
4+
(thickness 1.6)
5+
(drawings 5)
6+
(tracks 4)
7+
(zones 0)
8+
(modules 1)
9+
(nets 1)
10+
)
11+
12+
(page A4)
13+
(title_block
14+
(title "Simple Plotting Test")
15+
(date 2018-06-04)
16+
(rev A)
17+
(company "KiPlot - KiCad Plotting Driver")
18+
)
19+
20+
(layers
21+
(0 F.Cu signal)
22+
(31 B.Cu signal)
23+
(32 B.Adhes user)
24+
(33 F.Adhes user)
25+
(34 B.Paste user)
26+
(35 F.Paste user)
27+
(36 B.SilkS user)
28+
(37 F.SilkS user)
29+
(38 B.Mask user)
30+
(39 F.Mask user)
31+
(40 Dwgs.User user)
32+
(41 Cmts.User user)
33+
(42 Eco1.User user)
34+
(43 Eco2.User user)
35+
(44 Edge.Cuts user)
36+
(45 Margin user)
37+
(46 B.CrtYd user)
38+
(47 F.CrtYd user)
39+
(48 B.Fab user)
40+
(49 F.Fab user)
41+
)
42+
43+
(setup
44+
(last_trace_width 0.25)
45+
(trace_clearance 0.2)
46+
(zone_clearance 0.508)
47+
(zone_45_only no)
48+
(trace_min 0.2)
49+
(segment_width 0.2)
50+
(edge_width 0.15)
51+
(via_size 0.8)
52+
(via_drill 0.4)
53+
(via_min_size 0.4)
54+
(via_min_drill 0.3)
55+
(uvia_size 0.3)
56+
(uvia_drill 0.1)
57+
(uvias_allowed no)
58+
(uvia_min_size 0.2)
59+
(uvia_min_drill 0.1)
60+
(pcb_text_width 0.3)
61+
(pcb_text_size 1.5 1.5)
62+
(mod_edge_width 0.15)
63+
(mod_text_size 1 1)
64+
(mod_text_width 0.15)
65+
(pad_size 1.524 1.524)
66+
(pad_drill 0.762)
67+
(pad_to_mask_clearance 0.2)
68+
(aux_axis_origin 0 0)
69+
(visible_elements FFFFFF7F)
70+
(pcbplotparams
71+
(layerselection 0x010fc_ffffffff)
72+
(usegerberextensions false)
73+
(usegerberattributes false)
74+
(usegerberadvancedattributes false)
75+
(creategerberjobfile false)
76+
(excludeedgelayer true)
77+
(linewidth 0.150000)
78+
(plotframeref false)
79+
(viasonmask false)
80+
(mode 1)
81+
(useauxorigin false)
82+
(hpglpennumber 1)
83+
(hpglpenspeed 20)
84+
(hpglpendiameter 15.000000)
85+
(psnegative false)
86+
(psa4output false)
87+
(plotreference true)
88+
(plotvalue true)
89+
(plotinvisibletext false)
90+
(padsonsilk false)
91+
(subtractmaskfromsilk false)
92+
(outputformat 1)
93+
(mirror false)
94+
(drillshape 1)
95+
(scaleselection 1)
96+
(outputdirectory ""))
97+
)
98+
99+
(net 0 "")
100+
101+
(net_class Default "This is the default net class."
102+
(clearance 0.2)
103+
(trace_width 0.25)
104+
(via_dia 0.8)
105+
(via_drill 0.4)
106+
(uvia_dia 0.3)
107+
(uvia_drill 0.1)
108+
)
109+
110+
(module TestPoint:TestPoint_THTPad_2.0x2.0mm_Drill1.0mm (layer F.Cu) (tedit 5B1533F4) (tstamp 5B15541F)
111+
(at 140 100)
112+
(descr "THT rectangular pad as test Point, square 2.0mm_Drill1.0mm side length, hole diameter 1.0mm")
113+
(tags "test point THT pad rectangle square")
114+
(attr virtual)
115+
(fp_text reference TP1 (at 0 -2) (layer F.SilkS)
116+
(effects (font (size 1 1) (thickness 0.15)))
117+
)
118+
(fp_text value TestPoint2mm (at 0 2.05) (layer F.Fab)
119+
(effects (font (size 1 1) (thickness 0.15)))
120+
)
121+
(fp_line (start 1.5 1.5) (end -1.5 1.5) (layer F.CrtYd) (width 0.05))
122+
(fp_line (start 1.5 1.5) (end 1.5 -1.5) (layer F.CrtYd) (width 0.05))
123+
(fp_line (start -1.5 -1.5) (end -1.5 1.5) (layer F.CrtYd) (width 0.05))
124+
(fp_line (start -1.5 -1.5) (end 1.5 -1.5) (layer F.CrtYd) (width 0.05))
125+
(fp_line (start -1.2 1.2) (end -1.2 -1.2) (layer F.SilkS) (width 0.12))
126+
(fp_line (start 1.2 1.2) (end -1.2 1.2) (layer F.SilkS) (width 0.12))
127+
(fp_line (start 1.2 -1.2) (end 1.2 1.2) (layer F.SilkS) (width 0.12))
128+
(fp_line (start -1.2 -1.2) (end 1.2 -1.2) (layer F.SilkS) (width 0.12))
129+
(fp_text user %R (at 0 -2) (layer F.Fab)
130+
(effects (font (size 1 1) (thickness 0.15)))
131+
)
132+
(pad 1 thru_hole rect (at 0 0) (size 2 2) (drill 1) (layers *.Cu *.Mask))
133+
)
134+
135+
(gr_arc (start 145 98) (end 148 98) (angle -90) (layer Edge.Cuts) (width 0.2))
136+
(gr_line (start 145 95) (end 132 95) (layer Edge.Cuts) (width 0.2))
137+
(gr_line (start 148 106) (end 148 98) (layer Edge.Cuts) (width 0.2))
138+
(gr_line (start 132 106) (end 148 106) (layer Edge.Cuts) (width 0.2))
139+
(gr_line (start 132 95) (end 132 106) (layer Edge.Cuts) (width 0.2))
140+
141+
(segment (start 140 100) (end 143 100) (width 1) (layer F.Cu) (net 0))
142+
(segment (start 140 100) (end 142 102) (width 1) (layer B.Cu) (net 0))
143+
(segment (start 142 102) (end 143 102) (width 1) (layer B.Cu) (net 0))
144+
(segment (start 134 99) (end 134 101) (width 1) (layer F.Cu) (net 0))
145+
146+
)

tests/conftest.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
"""
2+
Test configuration
3+
"""
4+
5+
6+
def pytest_addoption(parser):
7+
parser.addoption("--plot_dir", action="store", default=None,
8+
help="the plot dir to use (omit to use a temp dir). "
9+
"If given, plots will _not_ be cleared after testing.")

tests/test_plot/README.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
This directory contains tests for full testing of KiCad plots.
2+
This tests:
3+
4+
* KiPlot's config parsing and driving of KiCad
5+
* KiCad's own plotting code
6+
7+
Generally, boards are drawn from `../board_samples` and configs from
8+
`yaml_samples`. Sometimes, the YAML samples are modified by the test
9+
runners to avoid having hundreds of them!
10+
11+
Bug that should be tested for:
12+
13+
* https://bugs.launchpad.net/kicad/+bug/1775037

tests/test_plot/__init__.py

Whitespace-only changes.
Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
2+
import os
3+
import shutil
4+
import tempfile
5+
import logging
6+
import pytest
7+
8+
9+
from kiplot import kiplot
10+
from kiplot import config_reader
11+
12+
13+
KICAD_PCB_EXT = '.kicad_pcb'
14+
15+
16+
class KiPlotTestContext(object):
17+
18+
def __init__(self, test_name):
19+
self.cfg = None
20+
21+
# The name used for the test output dirs and other logging
22+
self.test_name = test_name
23+
24+
# The name of the PCB board file (will be interpolated into the plot
25+
# files by pcbnewm so we need to know
26+
self.board_name = None
27+
28+
# The actual board file that will be loaded
29+
self.board_file = None
30+
31+
# The directory under which to place plots (None: use a temp dir)
32+
self.plot_dir = pytest.config.getoption('plot_dir')
33+
34+
# The actual output dir for this plot run
35+
self._output_dir = None
36+
# Clean the output dir afterwards (true for temp dirs)
37+
self._del_dir_after = self.plot_dir is None
38+
39+
def _get_text_cfg_dir(self):
40+
41+
this_dir = os.path.dirname(os.path.realpath(__file__))
42+
43+
return os.path.join(this_dir, '../yaml_samples')
44+
45+
def _get_board_cfg_dir(self):
46+
47+
this_dir = os.path.dirname(os.path.realpath(__file__))
48+
49+
return os.path.join(this_dir, '../board_samples')
50+
51+
def load_yaml_config_file(self, filename):
52+
"""
53+
Reads a config from a YAML file
54+
"""
55+
56+
cfg_file = os.path.join(self._get_text_cfg_dir(), filename)
57+
58+
cr = config_reader.CfgYamlReader()
59+
60+
with open(cfg_file) as cf_file:
61+
cfg = cr.read(cf_file)
62+
63+
self.cfg = cfg
64+
65+
def _load_board_file(self, filename=None):
66+
"""
67+
Load the named board.
68+
69+
@param filename: a filename to load, or None to load the relevant
70+
board name from the board sample dir
71+
"""
72+
73+
if filename is None:
74+
self.board_file = os.path.join(self._get_board_cfg_dir(),
75+
self.board_name + KICAD_PCB_EXT)
76+
else:
77+
self.board_file = filename
78+
79+
assert os.path.isfile(self.board_file)
80+
81+
def _set_up_output_dir(self):
82+
83+
if not self.plot_dir:
84+
# create a tmp dir
85+
self.output_dir = tempfile.mkdtemp(
86+
prefix='tmp_kiplot_{}'.format(self.test_name))
87+
88+
else:
89+
self.output_dir = os.path.join(self.plot_dir, self.test_name)
90+
# just create the dir
91+
if os.path.isdir(self.output_dir):
92+
# exists, that's OK
93+
pass
94+
else:
95+
os.makedirs(self.output_dir)
96+
97+
self.cfg.outdir = self.output_dir
98+
logging.info(self.output_dir)
99+
100+
def clean_up(self):
101+
102+
if self._del_dir_after:
103+
shutil.rmtree(self.output_dir)
104+
105+
def do_plot(self):
106+
107+
self.cfg.validate()
108+
109+
self._load_board_file(self.board_file)
110+
111+
self._set_up_output_dir()
112+
113+
plotter = kiplot.Plotter(self.cfg)
114+
plotter.plot(self.board_file)

0 commit comments

Comments
 (0)