From 39756077b4930b1eee4c029dbce1dacb23a21559 Mon Sep 17 00:00:00 2001 From: jaseg Date: Thu, 27 Jan 2022 00:49:47 +0100 Subject: layers testing WIP --- gerbonara/gerber/layer_rules.py | 6 +- gerbonara/gerber/layers.py | 46 +++- gerbonara/gerber/rs274x.py | 28 +-- gerbonara/gerber/tests/test_layers.py | 451 +++++++++++++++++++++++----------- gerbonara/gerber/tests/utils.py | 2 +- 5 files changed, 351 insertions(+), 182 deletions(-) diff --git a/gerbonara/gerber/layer_rules.py b/gerbonara/gerber/layer_rules.py index 4969d36..1733964 100644 --- a/gerbonara/gerber/layer_rules.py +++ b/gerbonara/gerber/layer_rules.py @@ -124,9 +124,9 @@ MATCH_RULES = { 'drill mech': r'.*\.rou', 'drill mech': r'.*\.drl', 'generic gerber': r'.*\.art', - 'excellon params': 'nc_param\.txt', + 'excellon params': r'nc_param\.txt', # put .log file last to prefer .txt - 'excellon params': 'ncdrill\.log', - 'excellon params': 'ncroute\.log', + 'excellon params': r'ncdrill\.log', + 'excellon params': r'ncroute\.log', }, } diff --git a/gerbonara/gerber/layers.py b/gerbonara/gerber/layers.py index 7c9ae8c..a439de3 100644 --- a/gerbonara/gerber/layers.py +++ b/gerbonara/gerber/layers.py @@ -1,14 +1,15 @@ #! /usr/bin/env python # -*- coding: utf-8 -*- - -# copyright 2014 Hamilton Kibbe +# +# Copyright 2014 Hamilton Kibbe +# Copyright 2021 Jan Götte # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at - +# # http://www.apache.org/licenses/LICENSE-2.0 - +# # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -19,9 +20,13 @@ import os import re import warnings from collections import namedtuple +from pathlib import Path from .excellon import ExcellonFile +from .rs274x import GerberFile from .ipc356 import IPCNetlist +from .cam import FileSettings +from .layer_rules import MATCH_RULES STANDARD_LAYERS = [ @@ -114,18 +119,18 @@ def layername_autoguesser(fn): elif re.match('(solder)?mask', fn): use = 'mask' - elif (m := re.match(f'(la?y?e?r?|in(ner)?)\W*(?P[0-9]+)', fn)): + elif (m := re.match(r'(la?y?e?r?|in(ner)?)\W*(?P[0-9]+)', fn)): use = 'copper' side = f'inner_{m["num"]:02d}' elif re.match('film', fn): use = 'copper' - elif re.match('out(line)?'): + elif re.match('out(line)?', fn): use = 'drill' side = 'outline' - elif re.match('drill|rout?e?'): + elif re.match('drill|rout?e?', fn): use = 'drill' side = 'unknown' @@ -149,10 +154,11 @@ class LayerStack: generator, filemap = best_match(files) if len(filemap) < 6: + warnings.warn('Ambiguous gerber filenames. Trying last-resort autoguesser.') generator = None filemap = autoguess(files) - if len(filemap < 6): - raise ValueError('Cannot figure out gerber file mapping') + if len(filemap) < 6: + raise ValueError('Cannot figure out gerber file mapping. Partial map is: ', filemap) elif generator == 'geda': # geda is written by geniuses who waste no bytes of unnecessary output so it doesn't actually include the @@ -210,7 +216,7 @@ class LayerStack: side, _, use = key.partition(' ') layers[(side, use)] = layer - hints = { layer.generator_hints } + { generator } + hints = set(layer.generator_hints) | { generator } if len(hints) > 1: warnings.warn('File identification returned ambiguous results. Please raise an issue on the gerbonara ' 'tracker and if possible please provide these input files for reference.') @@ -287,13 +293,29 @@ class LayerStack: def __len__(self): return len(self.layers) + def get(self, index, default=None): + if self.contains(key): + return self[key] + else: + return default + + def __contains__(self, index): + if isinstance(index, str): + side, _, use = index.partition(' ') + return (side, use) in self.layers + + elif isinstance(index, tuple): + return index in self.layers + + return index < len(self.copper_layers) + def __getitem__(self, index): if isinstance(index, str): side, _, use = index.partition(' ') - return self.layers.get((side, use)) + return self.layers[(side, use)] elif isinstance(index, tuple): - return self.layers.get(index) + return self.layers[index] return self.copper_layers[index] diff --git a/gerbonara/gerber/rs274x.py b/gerbonara/gerber/rs274x.py index fe85c09..5973ead 100644 --- a/gerbonara/gerber/rs274x.py +++ b/gerbonara/gerber/rs274x.py @@ -1,17 +1,17 @@ #! /usr/bin/env python # -*- coding: utf-8 -*- - +# +# Modified from parser.py by Paulo Henrique Silva # Copyright 2014 Hamilton Kibbe # Copyright 2019 Hiroshi Murayama # Copyright 2021 Jan Götte -# Modified from parser.py by Paulo Henrique Silva # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at - +# # http://www.apache.org/licenses/LICENSE-2.0 - +# # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -566,7 +566,8 @@ class GerberParser: self.line = None def warn(self, msg, kls=SyntaxWarning): - warnings.warn('{self.filename}:{self.lineno} "{self.line.replace("\n", "\\n")}": {msg}', kls) + line_joined = self.line.replace('\n', '\\n') + warnings.warn(f'{self.filename}:{self.lineno} "{line_joined}": {msg}', kls) @classmethod def _split_commands(kls, data): @@ -951,23 +952,6 @@ class GerberParser: def _parse_ignored(self, match): pass - -def _match_one(expr, data): - match = expr.match(data) - if match is None: - return ({}, None) - else: - return (match.groupdict(), data[match.end(0):]) - - -def _match_one_from_many(exprs, data): - for expr in exprs: - match = expr.match(data) - if match: - return (match.groupdict(), data[match.end(0):]) - - return ({}, None) - if __name__ == '__main__': import argparse parser = argparse.ArgumentParser() diff --git a/gerbonara/gerber/tests/test_layers.py b/gerbonara/gerber/tests/test_layers.py index 2178787..62ef522 100644 --- a/gerbonara/gerber/tests/test_layers.py +++ b/gerbonara/gerber/tests/test_layers.py @@ -1,158 +1,321 @@ #! /usr/bin/env python # -*- coding: utf-8 -*- - -# copyright 2016 Hamilton Kibbe +# +# Copyright 2021 Jan Götte # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at - +# # http://www.apache.org/licenses/LICENSE-2.0 - +# # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. +from pathlib import Path + +import pytest + +from .utils import * +from ..layers import LayerStack +from ..rs274x import GerberFile +from ..excellon import ExcellonFile + +# hand-classified +REFERENCE_DIRS = { + 'Target3001': { + 'IRNASIoTbank1.2.Apr': None, + 'IRNASIoTbank1.2.Bot': 'bottom copper', + 'IRNASIoTbank1.2.Drill': 'drill plated', + 'IRNASIoTbank1.2.Info': None, + 'IRNASIoTbank1.2.Outline': 'drill outline', + 'IRNASIoTbank1.2.PasteBot': 'bottom paste', + 'IRNASIoTbank1.2.PasteTop': 'top paste', + 'IRNASIoTbank1.2.PosiBot': 'bottom silkscreen', + 'IRNASIoTbank1.2.PosiTop': 'top silkscreen', + 'IRNASIoTbank1.2.StopBot': 'bottom mask', + 'IRNASIoTbank1.2.StopTop': 'top mask', + 'IRNASIoTbank1.2.Tool': None, + 'IRNASIoTbank1.2.Top': 'top copper', + 'IRNASIoTbank1.2.Whl': None, + }, + + 'allegro': { + '08_057494d-ipc356.ipc': None, + '08_057494d.rou': 'drill outline', + 'Read_Me.1': None, + 'art_param.txt': None, + 'assy1.art': None, + 'assy2.art': None, + 'fab1.art': None, + 'l1_primary.art': 'top copper', + 'l2_gnd.art': 'inner_2 copper', + 'l3_vcc.art': 'inner_3 copper', + 'l4_secondary.art': 'bottom copper', + 'mask_prm.art': 'top mask', + 'mask_sec.art': 'bottom mask', + 'nc_param.txt': None, + 'ncdrill-1-4.drl': 'drill unknown', + 'ncdrill.log': None, + 'netlist.err': None, + 'paste_prm.art': 'top paste', + 'paste_sec.art': 'bottom paste', + 'photo.log': None, + 'silk_prm.art': 'top silk', + 'silk_sec.art': 'bottom silk', + }, + + 'allegro-2': { + 'MINNOWMAX_REVA2_PUBLIC_BOTTOMSIDE.pdf': None, + 'MINNOWMAX_REVA2_PUBLIC_TOPSIDE.pdf': None, + 'MinnowMax_RevA1_IPC356A.ipc': None, + 'MinnowMax_RevA1_DRILL/MinnowMax_RevA1_NCDRILL.drl': 'drill unknown', + 'MinnowMax_RevA1_DRILL/MinnowMax_RevA1_NCROUTE.rou': 'drill outline', + 'MinnowMax_RevA1_DRILL/nc_param.txt': None, + 'MinnowMax_RevA1_DRILL/ncdrill.log': None, + 'MinnowMax_RevA1_DRILL/ncroute.log': None, + 'MinnowMax_assy.art': None, + 'MinnowMax_bslk.art': 'bottom silk', + 'MinnowMax_fab.art': None, + 'MinnowMax_lyr10_GAF.art': 'bottom copper', + 'MinnowMax_lyr1_GAF.art': 'top copper', + 'MinnowMax_lyr2.art': 'inner_2 copper', + 'MinnowMax_lyr3.art': 'inner_3 copper', + 'MinnowMax_lyr4.art': 'inner_4 copper', + 'MinnowMax_lyr5.art': 'inner_5 copper', + 'MinnowMax_lyr6.art': 'inner_6 copper', + 'MinnowMax_lyr7.art': 'inner_7 copper', + 'MinnowMax_lyr8.art': 'inner_8 copper', + 'MinnowMax_lyr9.art': 'inner_9 copper', + 'MinnowMax_smc_GAF.art': 'top mask', + 'MinnowMax_sms_GAF.art': 'bottom mask', + 'MinnowMax_spc.art': 'top paste', + 'MinnowMax_sps.art': 'bottom paste', + 'MinnowMax_tslk_GAF.art': 'top silk', + }, + + 'altium-composite-drill': { + 'Gerber/LimeSDR-QPCIe_1v2-macro.APR_LIB': None, + 'Gerber/LimeSDR-QPCIe_1v2.EXTREP': None, + 'Gerber/LimeSDR-QPCIe_1v2.G1': 'inner_1 copper', + 'Gerber/LimeSDR-QPCIe_1v2.G10': 'inner_10 copper', + 'Gerber/LimeSDR-QPCIe_1v2.G11': 'inner_11 copper', + 'Gerber/LimeSDR-QPCIe_1v2.G12': 'inner_12 copper', + 'Gerber/LimeSDR-QPCIe_1v2.G2': 'inner_2 copper', + 'Gerber/LimeSDR-QPCIe_1v2.G3': 'inner_3 copper', + 'Gerber/LimeSDR-QPCIe_1v2.G4': 'inner_4 copper', + 'Gerber/LimeSDR-QPCIe_1v2.G5': 'inner_5 copper', + 'Gerber/LimeSDR-QPCIe_1v2.G6': 'inner_6 copper', + 'Gerber/LimeSDR-QPCIe_1v2.G7': 'inner_7 copper', + 'Gerber/LimeSDR-QPCIe_1v2.G8': 'inner_8 copper', + 'Gerber/LimeSDR-QPCIe_1v2.G9': 'inner_9 copper', + 'Gerber/LimeSDR-QPCIe_1v2.GBL': 'bottom copper', + 'Gerber/LimeSDR-QPCIe_1v2.GBO': 'bottom silk', + 'Gerber/LimeSDR-QPCIe_1v2.GBP': 'bottom paste', + 'Gerber/LimeSDR-QPCIe_1v2.GBS': 'bottom mask', + 'Gerber/LimeSDR-QPCIe_1v2.GM1': 'dirll outlinej', + 'Gerber/LimeSDR-QPCIe_1v2.GM14': None, + 'Gerber/LimeSDR-QPCIe_1v2.GM15': None, + 'Gerber/LimeSDR-QPCIe_1v2.GPB': None, + 'Gerber/LimeSDR-QPCIe_1v2.GPT': None, + 'Gerber/LimeSDR-QPCIe_1v2.GTL': 'bottom copper', + 'Gerber/LimeSDR-QPCIe_1v2.GTO': 'bottom silk', + 'Gerber/LimeSDR-QPCIe_1v2.GTP': 'bottom paste', + 'Gerber/LimeSDR-QPCIe_1v2.GTS': 'bottom mask', + 'Gerber/LimeSDR-QPCIe_1v2.REP': None, + 'Gerber/LimeSDR-QPCIe_1v2.RUL': None, + 'Gerber/LimeSDR-QPCIe_1v2.apr': None, + 'NC Drill/LimeSDR-QPCIe_1v2-RoundHoles.TXT': 'drill unknown', + 'NC Drill/LimeSDR-QPCIe_1v2-SlotHoles.TXT': 'drill unknown', + 'NC Drill/LimeSDR-QPCIe_1v2.DRR': None, + 'NC Drill/LimeSDR-QPCIe_1v2.LDP': None, + }, + + 'diptrace': { + 'mainboard.drl': 'drill plated', + 'mainboard_BoardOutline.gbr': 'drill outline', + 'mainboard_Bottom.gbr': 'bottom copper', + 'mainboard_BottomMask.gbr': 'bottom mask', + 'mainboard_Top.gbr': 'top copper', + 'mainboard_TopMask.gbr': 'top mask', + 'mainboard_TopSilk.gbr': 'top silk', + }, + + 'eagle-newer': { + 'copper_bottom.gbr': 'bottom copper', + 'copper_top.gbr': 'top copper', + 'drills.xln': 'drill unknown', + 'gerber_job.gbrjob': None, + 'profile.gbr': 'drill outline', + 'silkscreen_bottom.gbr': 'bottom silk', + 'silkscreen_top.gbr': 'top silk', + 'soldermask_bottom.gbr': 'bottom mask', + 'soldermask_top.gbr': 'top mask', + 'solderpaste_bottom.gbr': 'bottom paste', + 'solderpaste_top.gbr': 'top paste', + }, + + 'eagle_files': { + 'copper_bottom_l4.gbr': 'bottom copper', + 'copper_inner_l2.gbr': 'inner_2 copper', + 'copper_inner_l3.gbr': 'inner_3 copper', + 'copper_top_l1.gbr': 'top copper', + 'profile.gbr': 'drill outline', + 'silkscreen_bottom.gbr': 'bottom silk', + 'silkscreen_top.gbr': 'top silk', + 'soldermask_bottom.gbr': 'bottom mask', + 'soldermask_top.gbr': 'top mask', + 'solderpaste_bottom.gbr': 'bottom paste', + 'solderpaste_top.gbr': 'top paste', + }, + + 'easyeda': { + 'Gerber_BoardOutline.GKO': 'drill outline', + 'Gerber_BottomLayer.GBL': 'bottom copper', + 'Gerber_BottomSolderMaskLayer.GBS': 'bottom mask', + 'Gerber_Drill_NPTH.DRL': 'drill nonplated', + 'Gerber_Drill_PTH.DRL': 'drill plated', + 'Gerber_TopLayer.GTL': 'top copper', + 'Gerber_TopPasteMaskLayer.GTP': 'top mask', + 'Gerber_TopPasteMaskLayer.bottom.svg': None, + 'Gerber_TopPasteMaskLayer.gtp.top.solderpaste.svg': None, + 'Gerber_TopPasteMaskLayer.gtp.top.solderpaste_2.svg': None, + 'Gerber_TopPasteMaskLayer.top.svg': None, + 'Gerber_TopSilkLayer.GTO': 'top silk', + 'Gerber_TopSolderMaskLayer.GTS': 'top mask', + 'How-to-order-PCB.txt': None, + }, + + 'fritzing': { + 'combined.GKO': 'drill outline', + 'combined.gbl': 'bottom copper', + 'combined.gbo': 'bottom silk', + 'combined.gbs': 'bottom mask', + 'combined.gm1': None, + 'combined.gtl': 'top copper', + 'combined.gto': 'top silk', + 'combined.gts': 'top mask', + 'combined.txt': 'drill unknown', + 'gyro_328p_6050_2021_panelize.gerberset': None, + }, + + 'geda': { + 'controller.bottom.gbr': 'bottom copper', + 'controller.bottommask.gbr': 'bottom mask', + 'controller.fab.gbr': None, + 'controller.group3.gbr': None, + 'controller.plated-drill.cnc': 'drill plated', + 'controller.top.gbr': 'top copper', + 'controller.topmask.gbr': 'top mask', + 'controller.topsilk.gbr': 'top silk', + 'controller.unplated-drill.cnc': 'drill nonplated', + }, + + 'pcb-rnd': { + 'power-art.asb': None, + 'power-art.ast': None, + 'power-art.fab': None, + 'power-art.gbl': 'bottom ccopper', + 'power-art.gbo': 'bottom silk', + 'power-art.gbp': 'bottom paste', + 'power-art.gbs': 'bottom mask', + 'power-art.gko': 'drill outline', + 'power-art.gtl': 'top copper', + 'power-art.gto': 'top silk', + 'power-art.gtp': 'top paste', + 'power-art.gts': 'top mask', + 'power-art.lht': None, + 'power-art.xln': 'drill unknown', + }, + + 'siemens': { + '80101_0125_F200_ContourPlated.ncd': 'drill outline', + '80101_0125_F200_DrillDrawingThrough.gdo': None, + '80101_0125_F200_L01_Top.gdo': 'top copper', + '80101_0125_F200_L02.gdo': 'inner_2 copper', + '80101_0125_F200_L03.gdo': 'inner_3 copper', + '80101_0125_F200_L04.gdo': 'inner_4 copper', + '80101_0125_F200_L05.gdo': 'inner_5 copper', + '80101_0125_F200_L06.gdo': 'inner_6 copper', + '80101_0125_F200_L07.gdo': 'inner_7 copper', + '80101_0125_F200_L08.gdo': 'inner_8 copper', + '80101_0125_F200_L09.gdo': 'inner_9 copper', + '80101_0125_F200_L10.gdo': 'inner_10 copper', + '80101_0125_F200_L11.gdo': 'inner_11 copper', + '80101_0125_F200_L12_Bottom.gdo': 'bottom copper', + '80101_0125_F200_SilkscreenBottom.gdo': 'bottom silk', + '80101_0125_F200_SilkscreenTop.gdo': 'top silk', + '80101_0125_F200_SolderPasteBottom.gdo': 'bottom paste', + '80101_0125_F200_SolderPasteTop.gdo': 'top paste', + '80101_0125_F200_SoldermaskBottom.gdo': 'bottom mask', + '80101_0125_F200_SoldermaskTop.gdo': 'top mask', + '80101_0125_F200_ThruHoleNonPlated.ncd': 'drill nonplated', + '80101_0125_F200_ThruHolePlated.ncd': 'drill plated', + }, + + 'siemens-2': { + 'Gerber/BoardOutlline.gdo': 'drill outline', + 'Gerber/DrillDrawingThrough.gdo': None, + 'Gerber/EtchLayerBottom.gdo': 'bottom copper', + 'Gerber/EtchLayerTop.gdo': 'top copper', + 'Gerber/GerberPlot.gpf': None, + 'Gerber/PCB.dsn': None, + 'Gerber/SolderPasteBottom.gdo': 'bottom paste', + 'Gerber/SolderPasteTop.gdo': 'top paste', + 'Gerber/SoldermaskBottom.gdo': 'bottom mask', + 'Gerber/SoldermaskTop.gdo': 'top mask', + 'NCDrill/ContourPlated.ncd': 'drill outline', + 'NCDrill/ThruHoleNonPlated.ncd': 'drill nonplated', + 'NCDrill/ThruHolePlated.ncd': 'drill plated', + }, + + 'upverter': { + 'design_export.drl': 'drill unknown', + 'design_export.gbl': 'bottom copper', + 'design_export.gbo': 'bottom silk', + 'design_export.gbp': 'bottom paste', + 'design_export.gbs': 'bottom mask', + 'design_export.gko': 'drill outline', + 'design_export.gtl': 'top copper', + 'design_export.gto': 'top silk', + 'design_export.gtp': 'top paste', + 'design_export.gts': 'top mask', + 'design_export.xln': 'drill unknown', + 'layers.cfg': None, + }, + } + +@filter_syntax_warnings +@pytest.mark.parametrize('ref_dir', list(REFERENCE_DIRS.items())) +def test_layer_classifier(ref_dir): + ref_dir, file_map = ref_dir + path = reference_path(ref_dir) + print('Reference path is', path) + file_map = { filename: role for filename, role in file_map.items() if role is not None } + rev_file_map = { value: key for key, value in file_map.items() } + drill_files = { filename: role for filename, role in file_map.items() if role.startswith('drill') } + + stack = LayerStack.from_directory(path) + + for side in 'top', 'bottom': + for layer in 'copper', 'silk', 'mask', 'paste': + + if (side, layer) in rev_file_map: + assert (side, layer) in stack + found = stack[side, layer] + assert isinstance(found, GerberFile) + assert found.filename == Path(rev_file_map[side, layer]).name + + else: # not in file_map + assert (side, layer) not in stack + + for filename, role in drill_files: + assert any(layer.filename == Path(filename).name for layer in stack.drill_layers) + assert len(stack.drill_layers) == len(drill_files) + + for layer in stack.drill_files: + assert isinstance(layer, ExcellonFile) -import os - -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") - - -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"), - ] - - for hint in hints: - for ext in hint.ext: - assert hint.layer == guess_layer_class("board.{}".format(ext)) - for name in hint.name: - assert hint.layer == guess_layer_class("{}.pho".format(name)) - - for filename, layer_class in test_vectors: - 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")] - - # Add custom regular expressions - layer_hints = [ - 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 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" - ) - - layer_hints = [ - Hint( - layer="bottom", - ext=[], - name=[], - regex="", - content=["G04 Layer name: Bottom"], - ) - ] - hints.extend(layer_hints) - - 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"), - ] - - 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 [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 isinstance(layer, PCBLayer) - layer = PCBLayer.from_cam(read(NCDRILL_FILE)) - assert isinstance(layer, DrillLayer) - layer = PCBLayer.from_cam(read(NETLIST_FILE)) - assert isinstance(layer, PCBLayer) - assert layer.layer_class == "ipc_netlist" - - -def test_PCBLayer_bounds(): - source = read(COPPER_FILE) - layer = PCBLayer.from_cam(source) - assert source.bounds == layer.bounds - - -def test_DrillLayer_from_cam(): - no_exceptions = True - try: - layer = DrillLayer.from_cam(read(NCDRILL_FILE)) - assert isinstance(layer, DrillLayer) - except: - no_exceptions = False - assert no_exceptions diff --git a/gerbonara/gerber/tests/utils.py b/gerbonara/gerber/tests/utils.py index bf30a1f..a3da40b 100644 --- a/gerbonara/gerber/tests/utils.py +++ b/gerbonara/gerber/tests/utils.py @@ -74,7 +74,7 @@ def reference(request, print_on_error): print_on_error(f'Reference file: {ref}') def filter_syntax_warnings(fun): - a = pytest.mark.filterwarnings('ignore:Deprecated.*statement found.*:DeprecationWarning') + a = pytest.mark.filterwarnings('ignore:.*Deprecated.*statement found.*:DeprecationWarning') b = pytest.mark.filterwarnings('ignore::SyntaxWarning') return a(b(fun)) -- cgit