Coverage for tests/tech_info_test.py: 100%
50 statements
« prev ^ index » next coverage.py v7.16.1, created at 2026-09-17 19:08 +0000
« prev ^ index » next coverage.py v7.16.1, created at 2026-09-17 19:08 +0000
1#
2# --------------------------------------------------------------------------------
3# SPDX-FileCopyrightText: 2024-2026 Martin Jan Köhler and Harald Pretl
4# Johannes Kepler University, Institute for Integrated Circuits.
5#
6# This file is part of KPEX
7# (see https://github.com/iic-jku/klayout-pex).
8#
9# This program is free software: you can redistribute it and/or modify
10# it under the terms of the GNU General Public License as published by
11# the Free Software Foundation, either version 3 of the License, or
12# (at your option) any later version.
13#
14# This program is distributed in the hope that it will be useful,
15# but WITHOUT ANY WARRANTY; without even the implied warranty of
16# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17# GNU General Public License for more details.
18#
19# You should have received a copy of the GNU General Public License
20# along with this program. If not, see <http://www.gnu.org/licenses/>.
21# SPDX-License-Identifier: GPL-3.0-or-later
22# --------------------------------------------------------------------------------
23#
25from __future__ import annotations
27import glob
28import os
29import tempfile
30import unittest
32import allure
33import google.protobuf.json_format
35from klayout_pex.tech_info import TechDefError, TechInfo
36import klayout_pex_protobuf.kpex.tech.tech_pb2 as tech_pb2
37import klayout_pex_protobuf.kpex.tech.process_stack_pb2 as stack_pb2
40def tech_pbjson_paths() -> list[str]:
41 protobuf_dir = os.path.realpath(os.path.join(__file__, '..', '..',
42 'klayout_pex_protobuf'))
43 return sorted(glob.glob(os.path.join(protobuf_dir, '*_tech.pb.json')))
46def tech_with_duplicates() -> tech_pb2.Technology:
47 tech = tech_pb2.Technology(name='test')
49 stack = tech.process_stack
50 for name in ('met1', 'met2', 'met1'):
51 layer = stack.layers.add(name=name,
52 layer_type=stack_pb2.ProcessStackInfo.LAYER_TYPE_METAL)
53 layer.metal_layer.z = 1.0
54 layer.metal_layer.thickness = 0.1
55 # A contact shares the namespace with the layers, so this collides too.
56 stack.layers[1].metal_layer.contact_above.name = 'met2'
58 tech.layers.add(name='met1')
59 tech.layers.add(name='met1')
61 for name in ('met1_con', 'met1_con'):
62 tech.lvs_computed_layers.add().layer_info.name = name
64 return tech
67@allure.parent_suite('Unit Tests')
68@allure.tag('TechInfo', 'Technology')
69class Test(unittest.TestCase):
70 def test_shipped_tech_definitions_have_unique_names(self):
71 paths = tech_pbjson_paths()
72 self.assertNotEqual([], paths, "No generated tech definition to check, "
73 "run the build first")
74 for path in paths:
75 with self.subTest(tech=os.path.basename(path)):
76 self.assertEqual([], TechInfo.duplicate_names(
77 TechInfo.parse_tech_def(path)))
79 def test_duplicate_names_are_reported_per_namespace(self):
80 problems = TechInfo.duplicate_names(tech_with_duplicates())
81 self.assertEqual(4, len(problems), problems)
82 self.assertIn("the process stack namespace declares 'met1' 2 times", problems[0])
83 self.assertIn("the process stack namespace declares 'met2' 2 times", problems[1])
84 self.assertIn('as contact, layer', problems[1])
85 self.assertIn("the layer namespace declares 'met1' 2 times", problems[2])
86 self.assertIn("the LVS computed layer namespace declares 'met1_con' 2 times",
87 problems[3])
89 def test_the_reader_refuses_a_duplicate(self):
90 with tempfile.TemporaryDirectory() as tmp_dir:
91 path = os.path.join(tmp_dir, 'duplicates_tech.pb.json')
92 with open(path, 'w') as f:
93 f.write(google.protobuf.json_format.MessageToJson(tech_with_duplicates()))
94 with self.assertRaises(TechDefError):
95 TechInfo.parse_tech_def(path)