Skip to main content

Pseudotest: Regression testing framework for scientific software

Project description

pseudotest

pseudotest is a YAML-driven regression testing framework for scientific software.

It runs one executable against one or more input files in an isolated temporary directory, then validates outputs with flexible file/content/directory match rules.

Highlights

  • YAML test definitions for reproducible regression checks
  • Per-input execution settings (InputMethod, Processors, ExpectedFailure, ExtraFiles)
  • Built-in content extraction: grep, line, field, column, field_re/field_im (complex magnitude)
  • File and directory based checks: size, count_files, file_is_present
  • Numeric and string comparisons with optional tolerance (tol)
  • Vector-style broadcasted matches for concise match definition
  • CLI for running tests and for updating failing references or tolerances in-place
  • Optional YAML report output for CI artifacts
  • MPI support via MPIEXEC environment variable

Installation

From PyPI

pip install pseudotest

From source

git clone <repo-url>
cd pseudotest
pip install -e .

Optional dependencies

pip install -e .[devel,test]   # ruff, pre-commit, pytest, pytest-mock, pytest-cov
pip install -e .[docs]         # mkdocs

Command-line usage

Two entry points are installed:

  • pseudotest — run tests from a YAML file
  • pseudotest-update — run tests and update failing config entries in-place

Run regression tests

pseudotest test.yaml -D /path/to/executables
Option Default Description
-D, --directory DIR . Directory containing executables
-p, --preserve off Keep temporary working directory after run
-v / -vv off Logging verbosity (INFO / DEBUG)
-t, --timeout N 600 Per-input execution timeout in seconds
-r, --report FILE Append YAML report document to FILE

Update failing tests

# Increase tolerances to cover observed deltas
pseudotest-update test.yaml -D ./bin --tolerance

# Replace reference values with observed values
pseudotest-update test.yaml -D ./bin --reference

# Write the updated config to a separate file
pseudotest-update test.yaml -D ./bin --reference --output updated.yaml
Option Description
-t, --tolerance Compute and set tol for failing numeric matches
-r, --reference Replace reference values with observed values
-o, --output FILE Write changes to FILE instead of overwriting the original
--timeout N Per-input execution timeout in seconds

YAML test format

Minimal example

Name: My regression test
Executable: solver.x

Inputs:
  case_01.in:
    Matches:
      total_energy:
        file: output.txt
        grep: "Energy:"
        field: 2
        value: -42.5000
        tol: 1e-4

Full schema

Name: My regression test        # required
Enabled: true                   # set to false to skip the entire suite
Executable: solver.x            # filename looked up in -D/--directory
InputMethod: argument           # argument | stdin | rename  (default: argument)
RenameTo: input.dat             # required when InputMethod: rename

Inputs:
  case_01.in:
    ExtraFiles: [basis.dat, pseudo.UPF]  # copied into work dir before execution
    Processors: 4               # MPI process count (requires MPIEXEC env var)
    ExpectedFailure: false      # true = non-zero exit code is treated as pass
    InputMethod: argument       # overrides top-level InputMethod for this input
    Matches:
      <match_name>: ...

Input methods

Mode Execution shape
argument (default) solver.x case_01.in
stdin solver.x < case_01.in
rename Copy input as RenameTo, then run solver.x

Match types

Extract a field from a line found by keyword

Energy:
  file: results.txt
  grep: "Total energy:"   # find first line containing this substring
  field: 3                # extract the 3rd whitespace-separated token (1-based)
  value: -42.5000
  tol: 1e-4

Extract a field from a specific line number

Status:
  file: output.txt
  line: 5       # 1-based; negative values count from the end (line: -1 = last line)
  field: 2
  value: converged

Extract from the line after a keyword

When both grep and line are present, line is an offset from the matched line (0 = same, 1 = next):

Force:
  file: results.txt
  grep: "Forces (Ha/Bohr):"
  line: 1       # one line after the match
  field: 2
  value: -0.00123
  tol: 1e-5

Extract by character column (fixed-width output)

Band Gap:
  file: bands.txt
  grep: "Band gap"
  column: 21    # start at character 21 (1-based), take first token
  value: 1.0342
  tol: 1e-3

Count matching lines

Warnings:
  file: run.log
  grep: "WARNING"
  count: 0      # assert no lines contain "WARNING"

Complex number magnitude

Extracts two fields and compares sqrt(re² + im²) to value:

eigenvalue:
  file: evals.txt
  grep: "Eigenvalue:"
  field_re: 2   # field holding the real part
  field_im: 3   # field holding the imaginary part
  value: 3.1416
  tol: 1e-4

File size

restart:
  file: restart.bin
  size: 65536

Directory checks

dir_count:
  directory: output
  count_files: 5

dir_has_file:
  directory: output
  file_is_present: summary.txt

Broadcast (vector checks)

List values expand a single match into one sub-check per element. All list parameters must have equal length; scalars are reused:

multi_energy:
  matches: ["Run1", "Run2"]
  file: [run1/out.txt, run2/out.txt]
  grep: "Energy:"
  field: 2
  value: [-10.0, -20.0]
  tol: 1e-6     # scalar: applies to both elements

matches is optional and names each match in the list.

Protecting a match from automatic updates

critical:
  file: results.txt
  grep: "Final value"
  field: 3
  value: 123.45
  protected: true   # pseudotest-update will never modify this match

MPI support

Set MPIEXEC to your MPI launcher to enable parallel execution:

MPIEXEC=mpiexec pseudotest test.yaml -D ./bin

Processors in each input controls the process count. Supported launchers:

Launcher Process-count flag
mpiexec, mpirun, mpiexec.hydra, orterun -np
srun (SLURM) -n
aprun (Cray) -n

Python API

from pseudotest import PseudoTestRunner

runner = PseudoTestRunner()
exit_code = runner.run(
    test_file_path="test.yaml",
    executable_directory="./bin",
    preserve_workdir=False,
    timeout=600,
    report_file="report.yaml",     # optional
    update_mode=None,               # "tolerance" | "reference" | None
    update_output=None,             # optional path for updated config
)

Exit codes

Code Meaning
0 All tests passed
1 One or more executions or matches failed
2 Configuration or usage error
3 Runtime error
99 Internal/unexpected error

Documentation

Detailed MkDocs-ready guides are in docs/:

  • docs/user-guide.md — full feature reference with examples
  • docs/developer-guide.md — architecture, adding match types, internals

Run locally:

pip install -e .[docs]
zensical serve

License

Mozilla Public License 2.0 (MPL-2.0)

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

pseudotest-0.2.0.tar.gz (59.8 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

pseudotest-0.2.0-py3-none-any.whl (33.8 kB view details)

Uploaded Python 3

File details

Details for the file pseudotest-0.2.0.tar.gz.

File metadata

  • Download URL: pseudotest-0.2.0.tar.gz
  • Upload date:
  • Size: 59.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.12.9

File hashes

Hashes for pseudotest-0.2.0.tar.gz
Algorithm Hash digest
SHA256 89572c05fbe16dd12638049e93583a4b7d6b05836ffb78ec3fce7fa526c8c0d7
MD5 810319e79c1b2135f8902bd70d2e2863
BLAKE2b-256 81bccb8c58c594dc0ad851b20ca7400a16be57f7b1ba481bab79be7d50be126c

See more details on using hashes here.

Provenance

The following attestation bundles were made for pseudotest-0.2.0.tar.gz:

Publisher: cd.yml on micaeljtoliveira/pseudotest

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file pseudotest-0.2.0-py3-none-any.whl.

File metadata

  • Download URL: pseudotest-0.2.0-py3-none-any.whl
  • Upload date:
  • Size: 33.8 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.12.9

File hashes

Hashes for pseudotest-0.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 99c790e042073069eff0ebbecee8ac653f74d4b30c96ca42b15ac4308c3d05d5
MD5 f5327b9539cc76a6775a1a2868a19dc7
BLAKE2b-256 1aac8077ef4fc85b641380054626c058faa7e427274d25a93c2313a03bd7a37d

See more details on using hashes here.

Provenance

The following attestation bundles were made for pseudotest-0.2.0-py3-none-any.whl:

Publisher: cd.yml on micaeljtoliveira/pseudotest

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page