layers testing WIP

This commit is contained in:
jaseg 2022-01-27 00:49:47 +01:00
parent 3b7330f9a8
commit 39756077b4
5 changed files with 328 additions and 159 deletions

View file

@ -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',
},
}

View file

@ -1,14 +1,15 @@
#! /usr/bin/env python
# -*- coding: utf-8 -*-
# copyright 2014 Hamilton Kibbe <ham@hamiltonkib.be>
#
# Copyright 2014 Hamilton Kibbe <ham@hamiltonkib.be>
# Copyright 2021 Jan Götte <code@jaseg.de>
#
# 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<num>[0-9]+)', fn)):
elif (m := re.match(r'(la?y?e?r?|in(ner)?)\W*(?P<num>[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]

View file

@ -1,17 +1,17 @@
#! /usr/bin/env python
# -*- coding: utf-8 -*-
#
# Modified from parser.py by Paulo Henrique Silva <ph.silva@gmail.com>
# Copyright 2014 Hamilton Kibbe <ham@hamiltonkib.be>
# Copyright 2019 Hiroshi Murayama <opiopan@gmail.com>
# Copyright 2021 Jan Götte <code@jaseg.de>
# Modified from parser.py by Paulo Henrique Silva <ph.silva@gmail.com>
#
# 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()

View file

@ -1,158 +1,321 @@
#! /usr/bin/env python
# -*- coding: utf-8 -*-
# copyright 2016 Hamilton Kibbe <ham@hamiltonkib.be>
#
# Copyright 2021 Jan Götte <code@jaseg.de>
#
# 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 os
import pytest
from ..layers import *
from ..common import read
from .utils import *
from ..layers import LayerStack
from ..rs274x import GerberFile
from ..excellon import ExcellonFile
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")
# 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',
},
def test_guess_layer_class():
""" Test layer type inferred correctly from filename
"""
'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',
},
# 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"),
]
'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,
},
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))
'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',
},
for filename, layer_class in test_vectors:
assert layer_class == guess_layer_class(filename)
'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',
},
def test_guess_layer_class_regex():
""" Test regular expressions for layer matching
"""
'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,
},
# Add any specific test case (filename, layer_class)
test_vectors = [("test - top copper.gbr", "top"), ("test - copper top.gbr", "top")]
'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,
},
# Add custom regular expressions
layer_hints = [
Hint(
layer="top",
ext=[],
name=[],
regex=r"(.*)(\scopper top|\stop copper).gbr",
content=[],
)
]
hints.extend(layer_hints)
'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',
},
for filename, layer_class in test_vectors:
assert layer_class == guess_layer_class(filename)
'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',
},
def test_guess_layer_class_by_content():
""" Test layer class by checking content
"""
'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',
},
expected_layer_class = "bottom"
filename = os.path.join(
os.path.dirname(__file__), "resources/example_guess_by_content.g0"
)
'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,
},
}
layer_hints = [
Hint(
layer="bottom",
ext=[],
name=[],
regex="",
content=["G04 Layer name: Bottom"],
)
]
hints.extend(layer_hints)
@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') }
assert expected_layer_class == guess_layer_class_by_content(filename)
stack = LayerStack.from_directory(path)
for side in 'top', 'bottom':
for layer in 'copper', 'silk', 'mask', 'paste':
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"),
]
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
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
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)
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"
for layer in stack.drill_files:
assert isinstance(layer, ExcellonFile)
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

View file

@ -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))