diff --git a/.github/workflows/ci-cd.yml b/.github/workflows/ci-cd.yml
index f874d99e..8a66efee 100644
--- a/.github/workflows/ci-cd.yml
+++ b/.github/workflows/ci-cd.yml
@@ -43,6 +43,7 @@ on:
- '.github/workflows/publish-reports-github-pages.yml'
- '**.md'
- '*.sh'
+ workflow_call:
workflow_dispatch:
release:
types: [published]
@@ -69,7 +70,7 @@ jobs:
# To add more build types (Release, Debug, RelWithDebInfo, etc.) customize the build_type list.
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
- python-version: ['3.13']
+ python-version: ['3.12']
build_type: [RelWithDebInfo]
c_compiler: [clang, cl]
include:
@@ -98,7 +99,7 @@ jobs:
env:
ALLURE_RESULTS_PATH: build/allure-results
- COVERAGE_REPORT_PATH: build/python-coverage
+ COVERAGE_DATA_PATH: build/python-coverage.sqlite
steps:
- uses: actions/checkout@v4
@@ -237,51 +238,54 @@ jobs:
- name: "[🐍] Run Unit tests"
run: >
- poetry run coverage run -m pytest -m "not slow"
+ poetry run coverage run --data-file=${{ env.COVERAGE_DATA_PATH }} -m pytest -m "not slow"
--alluredir ${{ env.ALLURE_RESULTS_PATH }}
--color no
shell: bash # NOTE: required for windows poetry calls
- - name: "[🐍] Create coverage report"
- if: success() || failure()
- run: poetry run coverage html --directory ${{ env.COVERAGE_REPORT_PATH }}
- shell: bash # NOTE: required for windows poetry calls
+ - run: pwd
- # - name: "Print listing of build dir"
- # if: success() || failure()
- # run: find ${{github.workspace}}/build -path ${{github.workspace}}/build/_deps -prune -o -print
- # shell: bash # NOTE: required for windows
+ - run: ls -aR
- name: "[🐍] Store coverage results"
if: success() || failure()
uses: actions/upload-artifact@v4
with:
- name: upload-python-coverage-report-${{ matrix.os }}_py${{ matrix.python-version }}
- path: ${{ env.COVERAGE_REPORT_PATH }}
+ name: python-unit-tests-coverage-report-${{ matrix.os }}_py${{ matrix.python-version }}
+ path: ${{ env.COVERAGE_DATA_PATH }} # NOTE: pytest-cov coverage binary file, can be combined later!
retention-days: 1
if-no-files-found: error
-
+
- name: "[🐍] Store allure test results"
if: success() || failure()
uses: actions/upload-artifact@v4
with:
- name: upload-python-allure-report-${{ matrix.os }}_py${{ matrix.python-version }}
+ name: python-unit-tests-allure-report-${{ matrix.os }}_py${{ matrix.python-version }}
path: ${{ env.ALLURE_RESULTS_PATH }}
retention-days: 1
if-no-files-found: error
call-publish-pypi:
- name: "Trigger workflow to publish distribution 📦s to PyPI/TestPyPI"
+ name: "Publish distribution 📦s to PyPI/TestPyPI"
needs: build_and_test
concurrency:
group: ${{ github.repository }}-${{ github.event.number || github.head_ref || github.sha }}-${{ github.workflow }}-publish-pypi
cancel-in-progress: true
uses: ./.github/workflows/publish-pypi.yml
- call-publish-reports-github-pages:
- name: "Trigger workflow to publish reports to GitHub Pages"
+ call-integration-tests:
+ name: "Integration Tests"
needs: build_and_test
concurrency:
- group: ${{ github.repository }}-${{ github.event.number || github.head_ref || github.sha }}-${{ github.workflow }}-publish-reports-github-pages
+ group: ${{ github.repository }}-${{ github.event.number || github.head_ref || github.sha }}-${{ github.workflow }}-integration-tests
cancel-in-progress: true
- uses: ./.github/workflows/publish-reports-github-pages.yml
+ uses: ./.github/workflows/integration-tests.yml
+
+ # NOTE: this is called from integration tests
+ # call-publish-reports-github-pages:
+ # name: "Publish reports to GitHub Pages"
+ # needs: build_and_test
+ # concurrency:
+ # group: ${{ github.repository }}-${{ github.event.number || github.head_ref || github.sha }}-${{ github.workflow }}-publish-reports-github-pages
+ # cancel-in-progress: true
+ # uses: ./.github/workflows/publish-reports-github-pages.yml
diff --git a/.github/workflows/integration-tests.yml b/.github/workflows/integration-tests.yml
new file mode 100644
index 00000000..70216510
--- /dev/null
+++ b/.github/workflows/integration-tests.yml
@@ -0,0 +1,202 @@
+##
+## --------------------------------------------------------------------------------
+## SPDX-FileCopyrightText: 2024 Martin Jan Köhler and Harald Pretl
+## Johannes Kepler University, Institute for Integrated Circuits.
+##
+## This file is part of KPEX
+## (see https://github.com/martinjankoehler/klayout-pex).
+##
+## This program is free software: you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation, either version 3 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+## GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with this program. If not, see .
+## SPDX-License-Identifier: GPL-3.0-or-later
+## --------------------------------------------------------------------------------
+##
+
+name: "Integration tests"
+
+on:
+ workflow_call:
+ workflow_dispatch:
+ # pull_request:
+ # branches: [ "main" ]
+ # paths:
+ # - '.github/workflows/integration-tests.yml'
+
+env:
+ KLAYOUT_DEB: "klayout_0.29.10-1_amd64.deb"
+ KLAYOUT_URL: "https://www.klayout.org/downloads/Ubuntu-22/klayout_0.29.10-1_amd64.deb"
+
+
+jobs:
+ run-tests:
+ name: Integration Test ${{ matrix.os }}-${{ matrix.build_type }}-py${{ matrix.python-version }}
+ runs-on: ${{ matrix.os }}
+
+ strategy:
+ # Set fail-fast to false to ensure that feedback is delivered for all matrix combinations.
+ # Consider changing this to true when your workflow is stable.
+ fail-fast: false
+
+ matrix:
+ os: [ubuntu-22.04]
+ python-version: ['3.12']
+
+ continue-on-error: true
+
+ env:
+ ALLURE_RESULTS_PATH: build/allure-results
+ COVERAGE_DATA_PATH: build/python-coverage.sqlite
+
+ steps:
+ - uses: actions/checkout@v4
+
+ - name: "[🐍] Setup python ${{ matrix.python-version }}"
+ id: setup-python
+ uses: actions/setup-python@v5
+ with:
+ python-version: ${{ matrix.python-version }}
+
+ - name: "[🐍] Install Poetry"
+ uses: snok/install-poetry@v1
+ with:
+ virtualenvs-create: true
+ virtualenvs-in-project: true
+ virtualenvs-path: .venv
+ installer-parallel: true
+
+ - name: "[🐍] Load cached venv"
+ id: cached-poetry-dependencies
+ uses: actions/cache@v4
+ with:
+ path: .venv
+ key: venv-${{ runner.os }}-${{ steps.setup-python.outputs.python-version }}-${{ hashFiles('**/poetry.lock') }}
+
+ - name: "[🐍] Install dependencies"
+ run: poetry install --no-interaction --no-root
+ shell: bash # NOTE: required for windows poetry calls
+
+ - name: "Restore cached klayout deb 📦 download"
+ id: cache-klayout-deb
+ uses: actions/cache/restore@v4
+ with:
+ path: klayout-deb
+ key: ${{ runner.os }}-klayout-deb
+
+ - name: "Download klayout deb 📦"
+ if: steps.cache-klayout-deb.outputs.cache-hit != 'true'
+ run: |
+ mkdir -p klayout-deb
+ pushd klayout-deb
+ wget "$KLAYOUT_URL"
+ popd
+
+ - name: "Save cached klayout deb 📦 download"
+ if: success() || failure()
+ uses: actions/cache/save@v4
+ with:
+ path: klayout-deb
+ key: ${{ runner.os }}-klayout-deb
+
+ - name: "Install klayout deb 📦"
+ run: |
+ pushd klayout-deb
+ sudo apt-get update
+ sudo apt-get install ./$KLAYOUT_DEB
+ popd
+
+ - name: "Restore cached LVSDB results"
+ uses: actions/cache/restore@v4
+ with:
+ path: output_sky130A/.kpex_cache
+ key: ${{ matrix.os }}-${{ matrix.python-version }}-kpex_cache
+
+ - name: "Download and merge Python dist artifacts"
+ uses: actions/download-artifact@v4
+ with:
+ pattern: python-dist-ubuntu-latest*
+ merge-multiple: true
+ path: dist # destination
+
+ - name: "Unpack Source Distribution"
+ run: |
+ tar xvfz dist/klayout_pex*.tar.gz --strip-components=1
+
+ - name: "[🐍] Run Unit tests"
+ run: >
+ poetry run coverage run --data-file=${{ env.COVERAGE_DATA_PATH }} -m pytest -m "slow"
+ --alluredir ${{ env.ALLURE_RESULTS_PATH }}
+ --color no
+ shell: bash # NOTE: required for windows poetry calls
+
+ - name: "[🐍] Store coverage results"
+ if: success() || failure()
+ uses: actions/upload-artifact@v4
+ with:
+ name: python-integration-tests-coverage-report-${{ matrix.os }}_py${{ matrix.python-version }}
+ path: ${{ env.COVERAGE_DATA_PATH }} # NOTE: pytest-cov coverage binary file, can be combined later!
+ retention-days: 1
+ if-no-files-found: error
+
+ - name: "[🐍] Store allure test results"
+ if: success() || failure()
+ uses: actions/upload-artifact@v4
+ with:
+ name: python-integration-tests-allure-report-${{ matrix.os }}_py${{ matrix.python-version }}
+ path: ${{ env.ALLURE_RESULTS_PATH }}
+ retention-days: 1
+ if-no-files-found: error
+
+ - name: "Save cached LVSDB results"
+ if: success() || failure()
+ uses: actions/cache/save@v4
+ with:
+ path: output_sky130A/.kpex_cache
+ key: ${{ matrix.os }}-${{ matrix.python-version }}-kpex_cache
+
+ call-publish-reports-github-pages:
+ name: "Publish reports to GitHub Pages"
+ needs: run-tests
+ concurrency:
+ group: ${{ github.repository }}-${{ github.event.number || github.head_ref || github.sha }}-${{ github.workflow }}-publish-reports-github-pages
+ cancel-in-progress: true
+ uses: ./.github/workflows/publish-reports-github-pages.yml
+
+ # run-inside-docker:
+ # name: "Run tests"
+ # runs-on: ubuntu-latest
+ # continue-on-error: true
+ #
+ # steps:
+ # - uses: actions/checkout@v4
+ #
+ # - name: Generate Cache Key from Dockerfiles
+ # id: generate_cache_key
+ # run: |
+ # files="./tests/fixtures/docker-compose.yml"
+ # file_contents=$(cat $files)
+ # key=$(echo "${file_contents}" | sha1sum | awk '{print $1}')
+ # echo "key=${key}" >> "$GITHUB_OUTPUT"
+ #
+ # - name: "Cache Docker images"
+ # uses: ScribeMD/docker-cache@0.5.0
+ # with:
+ # key: docker-${{ runner.os }}-integration-tests-${{ steps.generate_cache_key.outputs.key }}
+ #
+ # - name: "Install Docker and run tests"
+ # uses: adambirds/docker-compose-action@v1.5.0
+ # with:
+ # compose-file: "./tests/fixtures/docker-compose.yml"
+ # test-container: iic-osic-tools
+ # test-command: --skip ./run_integration_tests.sh
+ # continue-on-error: true # ensure docker cache is written nevertheless
+
diff --git a/.github/workflows/publish-reports-github-pages.yml b/.github/workflows/publish-reports-github-pages.yml
index bbc158fc..0cde01ec 100644
--- a/.github/workflows/publish-reports-github-pages.yml
+++ b/.github/workflows/publish-reports-github-pages.yml
@@ -43,17 +43,17 @@ jobs:
# uses: actions/upload-artifact/merge@v4
# with:
# name: merged-allure-reports
- # pattern: upload-python-allure-report-*
+ # pattern: python-*-tests-allure-report-ubuntu*
# # delete-merged: true
# retention-days: 1
- name: "Download and merge Allure coverage artifacts"
uses: actions/download-artifact@v4
with:
- pattern: upload-python-allure-report-*
+ pattern: python-*-tests-allure-report-*
merge-multiple: true
path: build/allure-results # destination
-
+
- name: "Set up JDK (for Allure)"
uses: actions/setup-java@v4
with:
@@ -92,18 +92,49 @@ jobs:
python-coverage-report:
name: "Generate Coverage Report (Aggregated Suites)"
runs-on: ubuntu-latest
-
+
steps:
- uses: actions/checkout@v4
-
- - name: "Download and merge Python coverage artifacts"
+
+ - name: "[🐍] Download and merge 📦 artifacts"
uses: actions/download-artifact@v4
with:
- pattern: upload-python-coverage-report-*
+ pattern: python-dist-ubuntu-latest*
merge-multiple: true
- path: pycov # destination
+ path: dist # destination
+
+ - name: "[🐍] Unpack Source 📦"
+ run: |
+ tar xvfz dist/klayout_pex*.tar.gz --strip-components=1
+
+ - name: "[🐍] Download and merge coverage artifacts"
+ uses: actions/download-artifact@v4
+ with:
+ pattern: python-*-tests-coverage-report-ubuntu-*
+ merge-multiple: false
+ path: pycov-databases # destination
+
+ - name: "Display structure of downloaded files"
+ run: ls -R pycov-databases
+
+ - name: "[🐍] Setup python"
+ id: setup-python
+ uses: actions/setup-python@v5
+
+ - name: "[🐍] Install dependencies"
+ run: |
+ python -m pip install --upgrade pip
+ pip install coverage
- - name: Display structure of downloaded files
+ - name: "[🐍] Combine coverage reports"
+ run: |
+ coverage combine pycov-databases/*/python-coverage.sqlite
+
+ - name: "[🐍] Create coverage report"
+ run: |
+ coverage html --directory pycov --data-file=.coverage
+
+ - name: "Display structure of coverage report"
run: ls -R pycov
- name: "Publish test report"
diff --git a/_run_tests.sh b/_run_tests.sh
new file mode 100644
index 00000000..aff11aab
--- /dev/null
+++ b/_run_tests.sh
@@ -0,0 +1,62 @@
+#! /bin/bash
+##
+## --------------------------------------------------------------------------------
+## SPDX-FileCopyrightText: 2024 Martin Jan Köhler and Harald Pretl
+## Johannes Kepler University, Institute for Integrated Circuits.
+##
+## This file is part of KPEX
+## (see https://github.com/martinjankoehler/klayout-pex).
+##
+## This program is free software: you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation, either version 3 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+## GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with this program. If not, see .
+## SPDX-License-Identifier: GPL-3.0-or-later
+## --------------------------------------------------------------------------------
+##
+
+# Usage: run_tests
+# Example: run_tests "not slow"
+function run_tests() {
+ PATTERN="$1"
+
+ DIR=$(dirname -- $(realpath ${BASH_SOURCE}))
+
+ mkdir -p "$DIR"/build
+
+ ALLURE_RESULTS_PATH="$DIR/build/allure-results"
+ ALLURE_REPORT_PATH="$DIR/build/allure-report"
+ COVERAGE_PATH="$DIR/build/coverage-results"
+
+ rm -rf "$ALLURE_RESULTS_PATH"
+ rm -rf "$ALLURE_REPORT_PATH"
+ rm -rf "$COVERAGE_PATH"
+
+ set -x
+ set -e
+
+ poetry run coverage run -m pytest -m "$PATTERN" \
+ --alluredir "$ALLURE_RESULTS_PATH" \
+ --color no
+
+ poetry run coverage html --directory "$COVERAGE_PATH"
+
+ allure generate \
+ --single-file "$ALLURE_RESULTS_PATH" \
+ --output "$ALLURE_REPORT_PATH" \
+ --clean
+
+ if [[ -z "$RUNNER_OS" ]] && [[ -d "/Applications/Safari.app" ]]
+ then
+ open -a Safari "$ALLURE_REPORT_PATH"/index.html
+ open -a Safari "$COVERAGE_PATH"/index.html
+ fi
+}
diff --git a/klayout_pex/kpex_cli.py b/klayout_pex/kpex_cli.py
index b2f8bc81..f5b45dcd 100755
--- a/klayout_pex/kpex_cli.py
+++ b/klayout_pex/kpex_cli.py
@@ -169,6 +169,8 @@ def parse_args(arg_list: List[str] = None) -> argparse.Namespace:
group_pex_input.add_argument("--cache-lvs", dest="cache_lvs",
type=true_or_false, default=True,
help="Used cached LVSDB (for given input GDS) (default is %(default)s)")
+ group_pex_input.add_argument("--cache-dir", dest="cache_dir_path", default=None,
+ help="Path for cached LVSDB (default is .kpex_cache within --out_dir)")
group_pex_options = main_parser.add_argument_group("Parasitic Extraction Options")
group_pex_options.add_argument("--blackbox", dest="blackbox_devices",
@@ -402,6 +404,9 @@ def input_file_stem(path: str):
subproc(f"\nPlease activate one or more engines using the arguments:\n{engine_help}")
found_errors = True
+ if args.cache_dir_path is None:
+ args.cache_dir_path = os.path.join(args.output_dir_base_path, '.kpex_cache')
+
if found_errors:
raise ArgumentValidationError("Argument validation failed")
@@ -683,13 +688,22 @@ def create_lvsdb(self, args: argparse.Namespace) -> kdb.LayoutVsSchematic:
case InputMode.GDS:
lvs_log_path = os.path.join(args.output_dir_path, f"{args.effective_cell_name}_lvs.log")
lvsdb_path = os.path.join(args.output_dir_path, f"{args.effective_cell_name}.lvsdb.gz")
+ lvsdb_cache_path = os.path.join(args.cache_dir_path, args.pdk,
+ os.path.splitroot(os.path.abspath(args.gds_path))[-1],
+ f"{args.effective_cell_name}.lvsdb.gz")
lvs_needed = True
- if os.path.exists(lvsdb_path) and args.cache_lvs:
- if self.modification_date(lvsdb_path) > self.modification_date(args.gds_path):
- warning(f"Reusing cached LVSDB")
- subproc(lvsdb_path)
+ if args.cache_lvs:
+ if not os.path.exists(lvsdb_cache_path):
+ info(f"Cache miss: extracted LVSDB does not exist")
+ subproc(lvsdb_cache_path)
+ elif self.modification_date(lvsdb_cache_path) <= self.modification_date(args.gds_path):
+ info(f"Cache miss: extracted LVSDB is older than the input GDS")
+ subproc(lvsdb_cache_path)
+ else:
+ warning(f"Cache hit: Reusing cached LVSDB")
+ subproc(lvsdb_cache_path)
lvs_needed = False
if lvs_needed:
@@ -700,6 +714,12 @@ def create_lvsdb(self, args: argparse.Namespace) -> kdb.LayoutVsSchematic:
schematic_path=args.effective_schematic_path,
log_path=lvs_log_path,
lvsdb_path=lvsdb_path)
+ if args.cache_lvs:
+ cache_dir_path = os.path.dirname(lvsdb_cache_path)
+ if not os.path.exists(cache_dir_path):
+ os.makedirs(cache_dir_path, exist_ok=True)
+ shutil.copy(lvsdb_path, lvsdb_cache_path)
+
lvsdb.read(lvsdb_path)
return lvsdb
diff --git a/klayout_pex/magic/magic_runner.py b/klayout_pex/magic/magic_runner.py
index 3bc4cf7b..d2927eea 100644
--- a/klayout_pex/magic/magic_runner.py
+++ b/klayout_pex/magic/magic_runner.py
@@ -57,6 +57,7 @@ def prepare_magic_script(gds_path: str,
output_netlist_path = os.path.abspath(output_netlist_path)
halo_scale = 200.0
+ halo_decl = '' if halo is None else f"\nextract halo {round(halo * halo_scale)}"
script: str = ""
match pex_mode:
@@ -72,7 +73,7 @@ def prepare_magic_script(gds_path: str,
cellname delete {cell_name} -noprompt
cellname rename {cell_name}_flat {cell_name}
select top cell
-extract path {run_dir_path}{'' if halo is None else f"\nextract halo {round(halo * halo_scale)}"}
+extract path {run_dir_path}{halo_decl}
extract all
ext2spice cthresh {c_threshold}
ext2spice format ngspice
@@ -90,7 +91,7 @@ def prepare_magic_script(gds_path: str,
cellname delete {cell_name} -noprompt
cellname rename {cell_name}_flat {cell_name}
select top cell
-extract path {run_dir_path}{'' if halo is None else f"\nextract halo {round(halo * halo_scale)}"}
+extract path {run_dir_path}{halo_decl}
extract do resistance
extract all
ext2sim labels on
diff --git a/run_integration_tests.sh b/run_integration_tests.sh
new file mode 100755
index 00000000..4a7489f9
--- /dev/null
+++ b/run_integration_tests.sh
@@ -0,0 +1,28 @@
+#! /bin/bash
+##
+## --------------------------------------------------------------------------------
+## SPDX-FileCopyrightText: 2024 Martin Jan Köhler and Harald Pretl
+## Johannes Kepler University, Institute for Integrated Circuits.
+##
+## This file is part of KPEX
+## (see https://github.com/martinjankoehler/klayout-pex).
+##
+## This program is free software: you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation, either version 3 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+## GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with this program. If not, see .
+## SPDX-License-Identifier: GPL-3.0-or-later
+## --------------------------------------------------------------------------------
+##
+
+source _run_tests.sh
+
+run_tests "slow"
diff --git a/run_tests.sh b/run_tests.sh
old mode 100755
new mode 100644
index 63fdbc5c..82494a2e
--- a/run_tests.sh
+++ b/run_tests.sh
@@ -4,7 +4,7 @@
## SPDX-FileCopyrightText: 2024 Martin Jan Köhler and Harald Pretl
## Johannes Kepler University, Institute for Integrated Circuits.
##
-## This file is part of KPEX
+## This file is part of KPEX
## (see https://github.com/martinjankoehler/klayout-pex).
##
## This program is free software: you can redistribute it and/or modify
@@ -23,35 +23,6 @@
## --------------------------------------------------------------------------------
##
-DIR=$(dirname -- $(realpath ${BASH_SOURCE}))
-
-mkdir -p "$DIR"/build
-
-ALLURE_RESULTS_PATH="$DIR/build/allure-results"
-ALLURE_REPORT_PATH="$DIR/build/allure-report"
-COVERAGE_PATH="$DIR/build/coverage-results"
-
-set -x
-set -e
-
-rm -rf "$ALLURE_RESULTS_PATH"
-rm -rf "$ALLURE_REPORT_PATH"
-rm -rf "$COVERAGE_PATH"
-
-poetry run coverage run -m pytest -m "not slow" \
- --alluredir "$ALLURE_RESULTS_PATH" \
- --color no
-
-poetry run coverage html --directory "$COVERAGE_PATH"
-
-allure generate \
- --single-file "$ALLURE_RESULTS_PATH" \
- --output "$ALLURE_REPORT_PATH" \
- --clean
-
-if [[ -z "$RUNNER_OS" ]] && [[ -d "/Applications/Safari.app" ]]
-then
- open -a Safari "$ALLURE_REPORT_PATH"/index.html
- open -a Safari "$COVERAGE_PATH"/index.html
-fi
+source _run_tests.sh
+run_tests "slow or not slow"
diff --git a/run_unit_tests.sh b/run_unit_tests.sh
new file mode 100755
index 00000000..032aea38
--- /dev/null
+++ b/run_unit_tests.sh
@@ -0,0 +1,28 @@
+#! /bin/bash
+##
+## --------------------------------------------------------------------------------
+## SPDX-FileCopyrightText: 2024 Martin Jan Köhler and Harald Pretl
+## Johannes Kepler University, Institute for Integrated Circuits.
+##
+## This file is part of KPEX
+## (see https://github.com/martinjankoehler/klayout-pex).
+##
+## This program is free software: you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation, either version 3 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+## GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with this program. If not, see .
+## SPDX-License-Identifier: GPL-3.0-or-later
+## --------------------------------------------------------------------------------
+##
+
+source _run_tests.sh
+
+run_tests "not slow"
diff --git a/tests/rcx25/rcx25_test.py b/tests/rcx25/rcx25_test.py
index b27c8ec8..d301ceca 100644
--- a/tests/rcx25/rcx25_test.py
+++ b/tests/rcx25/rcx25_test.py
@@ -87,12 +87,10 @@ def _run_rcx25d_single_cell(*path_components) -> Tuple[CellExtractionResults, CS
preview_png_path = tempfile.mktemp(prefix=f"layout_preview_", suffix=".png")
_save_layout_preview(gds_path, preview_png_path)
- tech_json_path = os.path.realpath(os.path.join(__file__, '..', '..', '..',
- 'build', 'sky130A_tech.pb.json'))
output_dir_path = os.path.realpath(os.path.join(__file__, '..', '..', '..', 'output_sky130A'))
cli = KpexCLI()
cli.main(['main',
- '--tech', tech_json_path,
+ '--pdk', 'sky130A',
'--gds', gds_path,
'--out_dir', output_dir_path,
'--2.5D', 'y'])