From 0362993bc2f177b0bd820662144e31181577c9ed Mon Sep 17 00:00:00 2001 From: vipul-09 Date: Sat, 23 May 2026 10:47:17 +0530 Subject: [PATCH 1/2] Added scalable integration testing framework and CI pipeline --- .github/workflows/tests.yml | 37 ++ integration_tests/__init__.py | 0 integration_tests/test_rc.py | 22 + reports/assets/style.css | 319 ++++++++++++++ reports/report.html | 773 ++++++++++++++++++++++++++++++++++ testcases/rc.cir | 11 + testcases/rl.cir | 10 + testcases/rlc.cir | 12 + utils/__init__.py | 0 utils/simulator.py | 12 + 10 files changed, 1196 insertions(+) create mode 100644 .github/workflows/tests.yml create mode 100644 integration_tests/__init__.py create mode 100644 integration_tests/test_rc.py create mode 100644 reports/assets/style.css create mode 100644 reports/report.html create mode 100644 testcases/rc.cir create mode 100644 testcases/rl.cir create mode 100644 testcases/rlc.cir create mode 100644 utils/__init__.py create mode 100644 utils/simulator.py diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml new file mode 100644 index 000000000..ce9234705 --- /dev/null +++ b/.github/workflows/tests.yml @@ -0,0 +1,37 @@ +name: eSim Automated Tests + +on: + push: + pull_request: + +jobs: + test: + + runs-on: ubuntu-latest + + steps: + + - name: Checkout Repository + uses: actions/checkout@v4 + + - name: Setup Python + uses: actions/setup-python@v5 + with: + python-version: '3.13' + + - name: Install Dependencies + run: | + python -m pip install --upgrade pip + pip install pytest pytest-html pytest-cov + sudo apt update + sudo apt install -y ngspice + + - name: Run Integration Tests + run: | + pytest --html=reports/report.html + + - name: Upload HTML Report + uses: actions/upload-artifact@v4 + with: + name: pytest-report + path: reports/report.html \ No newline at end of file diff --git a/integration_tests/__init__.py b/integration_tests/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/integration_tests/test_rc.py b/integration_tests/test_rc.py new file mode 100644 index 000000000..e8896e56c --- /dev/null +++ b/integration_tests/test_rc.py @@ -0,0 +1,22 @@ +import pytest + +from utils.simulator import run_ngspice + + +circuits = [ + "rc.cir", + "rl.cir", + "rlc.cir" +] + + +@pytest.mark.parametrize("circuit", circuits) +def test_circuit_simulation(circuit): + + result = run_ngspice(f"testcases/{circuit}") + + assert result.returncode == 0 + + assert "No. of Data Rows" in result.stdout + + assert "Simulation executed from .control section" in result.stdout \ No newline at end of file diff --git a/reports/assets/style.css b/reports/assets/style.css new file mode 100644 index 000000000..561524c69 --- /dev/null +++ b/reports/assets/style.css @@ -0,0 +1,319 @@ +body { + font-family: Helvetica, Arial, sans-serif; + font-size: 12px; + /* do not increase min-width as some may use split screens */ + min-width: 800px; + color: #999; +} + +h1 { + font-size: 24px; + color: black; +} + +h2 { + font-size: 16px; + color: black; +} + +p { + color: black; +} + +a { + color: #999; +} + +table { + border-collapse: collapse; +} + +/****************************** + * SUMMARY INFORMATION + ******************************/ +#environment td { + padding: 5px; + border: 1px solid #e6e6e6; + vertical-align: top; +} +#environment tr:nth-child(odd) { + background-color: #f6f6f6; +} +#environment ul { + margin: 0; + padding: 0 20px; +} + +/****************************** + * TEST RESULT COLORS + ******************************/ +span.passed, +.passed .col-result { + color: green; +} + +span.skipped, +span.xfailed, +span.rerun, +.skipped .col-result, +.xfailed .col-result, +.rerun .col-result { + color: orange; +} + +span.error, +span.failed, +span.xpassed, +.error .col-result, +.failed .col-result, +.xpassed .col-result { + color: red; +} + +.col-links__extra { + margin-right: 3px; +} + +/****************************** + * RESULTS TABLE + * + * 1. Table Layout + * 2. Extra + * 3. Sorting items + * + ******************************/ +/*------------------ + * 1. Table Layout + *------------------*/ +#results-table { + border: 1px solid #e6e6e6; + color: #999; + font-size: 12px; + width: 100%; +} +#results-table th, +#results-table td { + padding: 5px; + border: 1px solid #e6e6e6; + text-align: left; +} +#results-table th { + font-weight: bold; +} + +/*------------------ + * 2. Extra + *------------------*/ +.logwrapper { + max-height: 230px; + overflow-y: scroll; + background-color: #e6e6e6; +} +.logwrapper.expanded { + max-height: none; +} +.logwrapper.expanded .logexpander:after { + content: "collapse [-]"; +} +.logwrapper .logexpander { + z-index: 1; + position: sticky; + top: 10px; + width: max-content; + border: 1px solid; + border-radius: 3px; + padding: 5px 7px; + margin: 10px 0 10px calc(100% - 80px); + cursor: pointer; + background-color: #e6e6e6; +} +.logwrapper .logexpander:after { + content: "expand [+]"; +} +.logwrapper .logexpander:hover { + color: #000; + border-color: #000; +} +.logwrapper .log { + min-height: 40px; + position: relative; + top: -50px; + height: calc(100% + 50px); + border: 1px solid #e6e6e6; + color: black; + display: block; + font-family: "Courier New", Courier, monospace; + padding: 5px; + padding-right: 80px; + white-space: pre-wrap; +} + +div.media { + border: 1px solid #e6e6e6; + float: right; + height: 240px; + margin: 0 5px; + overflow: hidden; + width: 320px; +} + +.media-container { + display: grid; + grid-template-columns: 25px auto 25px; + align-items: center; + flex: 1 1; + overflow: hidden; + height: 200px; +} + +.media-container--fullscreen { + grid-template-columns: 0px auto 0px; +} + +.media-container__nav--right, +.media-container__nav--left { + text-align: center; + cursor: pointer; +} + +.media-container__viewport { + cursor: pointer; + text-align: center; + height: inherit; +} +.media-container__viewport img, +.media-container__viewport video { + object-fit: cover; + width: 100%; + max-height: 100%; +} + +.media__name, +.media__counter { + display: flex; + flex-direction: row; + justify-content: space-around; + flex: 0 0 25px; + align-items: center; +} + +.collapsible td:not(.col-links) { + cursor: pointer; +} +.collapsible td:not(.col-links):hover::after { + color: #bbb; + font-style: italic; + cursor: pointer; +} + +.col-result { + width: 130px; +} +.col-result:hover::after { + content: " (hide details)"; +} + +.col-result.collapsed:hover::after { + content: " (show details)"; +} + +#environment-header h2:hover::after { + content: " (hide details)"; + color: #bbb; + font-style: italic; + cursor: pointer; + font-size: 12px; +} + +#environment-header.collapsed h2:hover::after { + content: " (show details)"; + color: #bbb; + font-style: italic; + cursor: pointer; + font-size: 12px; +} + +/*------------------ + * 3. Sorting items + *------------------*/ +.sortable { + cursor: pointer; +} +.sortable.desc:after { + content: " "; + position: relative; + left: 5px; + bottom: -12.5px; + border: 10px solid #4caf50; + border-bottom: 0; + border-left-color: transparent; + border-right-color: transparent; +} +.sortable.asc:after { + content: " "; + position: relative; + left: 5px; + bottom: 12.5px; + border: 10px solid #4caf50; + border-top: 0; + border-left-color: transparent; + border-right-color: transparent; +} + +.hidden, .summary__reload__button.hidden { + display: none; +} + +.summary__data { + flex: 0 0 550px; +} +.summary__reload { + flex: 1 1; + display: flex; + justify-content: center; +} +.summary__reload__button { + flex: 0 0 300px; + display: flex; + color: white; + font-weight: bold; + background-color: #4caf50; + text-align: center; + justify-content: center; + align-items: center; + border-radius: 3px; + cursor: pointer; +} +.summary__reload__button:hover { + background-color: #46a049; +} +.summary__spacer { + flex: 0 0 550px; +} + +.controls { + display: flex; + justify-content: space-between; +} + +.filters, +.collapse { + display: flex; + align-items: center; +} +.filters button, +.collapse button { + color: #999; + border: none; + background: none; + cursor: pointer; + text-decoration: underline; +} +.filters button:hover, +.collapse button:hover { + color: #ccc; +} + +.filter__label { + margin-right: 10px; +} diff --git a/reports/report.html b/reports/report.html new file mode 100644 index 000000000..af2c15b6a --- /dev/null +++ b/reports/report.html @@ -0,0 +1,773 @@ + + + + + report.html + + + +

report.html

+

Report generated on 23-May-2026 at 10:17:07 by pytest-html + v4.2.0

+
+

Environment

+
+
+ + + + + +
+
+

Summary

+
+
+

3 tests took 774 ms.

+

(Un)check the boxes to filter the results.

+
+ +
+
+
+
+ + 0 Failed, + + 3 Passed, + + 0 Skipped, + + 0 Expected failures, + + 0 Unexpected passes, + + 0 Errors, + + 0 Reruns + + 0 Retried, +
+
+  /  +
+
+
+
+
+
+
+
+ + + + + + + + + +
ResultTestDurationLinks
+ + + \ No newline at end of file diff --git a/testcases/rc.cir b/testcases/rc.cir new file mode 100644 index 000000000..7c4e5e7a6 --- /dev/null +++ b/testcases/rc.cir @@ -0,0 +1,11 @@ +V1 in 0 DC 5 +R1 in out 1k +C1 out 0 1u +R2 out 0 1Meg + +.control +run +.endc + +.tran 1ms 10ms +.end \ No newline at end of file diff --git a/testcases/rl.cir b/testcases/rl.cir new file mode 100644 index 000000000..30e5b5217 --- /dev/null +++ b/testcases/rl.cir @@ -0,0 +1,10 @@ +V1 in 0 DC 5 +R1 in out 1k +L1 out 0 1m + +.control +run +.endc + +.tran 1ms 10ms +.end \ No newline at end of file diff --git a/testcases/rlc.cir b/testcases/rlc.cir new file mode 100644 index 000000000..cb3e39fe6 --- /dev/null +++ b/testcases/rlc.cir @@ -0,0 +1,12 @@ +V1 in 0 DC 5 +R1 in out 1k +L1 out mid 1m +C1 mid 0 1u +R2 mid 0 1Meg + +.control +run +.endc + +.tran 1ms 10ms +.end \ No newline at end of file diff --git a/utils/__init__.py b/utils/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/utils/simulator.py b/utils/simulator.py new file mode 100644 index 000000000..44d2424be --- /dev/null +++ b/utils/simulator.py @@ -0,0 +1,12 @@ +import subprocess + + +def run_ngspice(netlist): + + result = subprocess.run( + ["ngspice", "-b", netlist], + capture_output=True, + text=True + ) + + return result \ No newline at end of file From 8063982a3f5e9557359dc0c892032cac734d7556 Mon Sep 17 00:00:00 2001 From: vipul-09 Date: Sat, 23 May 2026 11:38:46 +0530 Subject: [PATCH 2/2] Added documentation and cleaned testing framework structure --- .gitignore | 4 + integration_tests/README.md | 80 +++++++++++++++++++ .../{test_rc.py => test_simulations.py} | 0 3 files changed, 84 insertions(+) create mode 100644 integration_tests/README.md rename integration_tests/{test_rc.py => test_simulations.py} (100%) diff --git a/.gitignore b/.gitignore index f321b0e7a..9f5a43e30 100644 --- a/.gitignore +++ b/.gitignore @@ -18,3 +18,7 @@ nghdl* tags build/ dist/ +venv/ +__pycache__/ +.pytest_cache/ +reports/*.html diff --git a/integration_tests/README.md b/integration_tests/README.md new file mode 100644 index 000000000..f70183791 --- /dev/null +++ b/integration_tests/README.md @@ -0,0 +1,80 @@ +# Automated Integration Testing Framework + +This module provides automated integration testing for eSim simulations using pytest and ngspice. + +--- + +## Features + +- Automated ngspice simulation execution +- Multi-circuit regression testing +- GitHub Actions CI/CD integration +- HTML test report generation +- Parameterized pytest framework + +--- + +## Folder Structure + +```text +integration_tests/ +testcases/ +utils/ +reports/ +``` + +--- + +## Supported Testcases + +- RC Circuit +- RL Circuit +- RLC Circuit + +--- + +## Run Tests + +Activate virtual environment: + +```bash +source venv/bin/activate +``` + +Run all tests: + +```bash +pytest +``` + +--- + +## Generate HTML Report + +```bash +pytest --html=reports/report.html +``` + +--- + +## CI/CD Pipeline + +GitHub Actions automatically runs tests on: +- push +- pull requests + +Workflow file: + +```text +.github/workflows/tests.yml +``` + +--- + +## Technologies Used + +- Python +- pytest +- ngspice +- GitHub Actions +- pytest-html \ No newline at end of file diff --git a/integration_tests/test_rc.py b/integration_tests/test_simulations.py similarity index 100% rename from integration_tests/test_rc.py rename to integration_tests/test_simulations.py