Metadata-Version: 2.4
Name: rtm-needs
Version: 0.2.0
Summary: Link sphinx-needs requirements (needs.json) to GoogleTest JUnit XML and emit an RTM report.
Author: ClosedLoop
License: MIT License
        
        Copyright (c) 2026 closedlooptools-spec
        
        Permission is hereby granted, free of charge, to any person obtaining a copy
        of this software and associated documentation files (the "Software"), to deal
        in the Software without restriction, including without limitation the rights
        to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
        copies of the Software, and to permit persons to whom the Software is
        furnished to do so, subject to the following conditions:
        
        The above copyright notice and this permission notice shall be included in all
        copies or substantial portions of the Software.
        
        THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
        IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
        FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
        AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
        LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
        OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
        SOFTWARE.
License-File: LICENSE
Keywords: gtest,junit,requirements,rtm,sphinx-needs,traceability
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Software Development :: Quality Assurance
Classifier: Topic :: Software Development :: Testing
Requires-Python: >=3.9
Description-Content-Type: text/markdown

# RTM – Requirements Traceability for Sphinx-Needs + GTest

RTM is a small, opinionated tool that links **software requirements** (from `sphinx-needs`) to **unit tests** (from GoogleTest JUnit XML), producing a clear, machine-readable **requirements traceability matrix (RTM)** and a human-readable summary.

It is designed to be:
- **Simple** – no Sphinx plugins, no test framework hooks
- **Deterministic** – all logic is explicit and inspectable
- **Extensible** – users control how requirements are extracted from tests
- **CI-friendly** – JSON output for downstream tooling

The CLI is the primary interface, but all components are importable as a Python module.

---

## What this tool does

RTM answers questions like:
- Which requirements have tests?
- Which requirements are missing tests?
- Which tests claim to cover requirements that don’t exist?
- Which tests cover multiple requirements?
- Are all tests for a given requirement passing?

It does **not**:
- Enforce any particular testing style
- Modify your test framework
- Require Sphinx to know about tests
- Require tests to be modeled as Sphinx needs

---

## Inputs

### 1. Requirements (`needs.json`)
Generated by `sphinx-needs` when you build your documentation.

Example location:
```
docs/_build/needs/needs.json
```

Only needs of type `req` are considered.

---

### 2. Test results (JUnit XML)
Generated by GoogleTest, for example:
```
--gtest_output=xml:build/test-results/junit.xml
```

RTM reads:
- suite name (`classname`)
- test name (`name`)
- status (`pass`, `fail`, `error`, `skip`)
- optional `<properties>` blocks

---

## Default behavior (very dumb, by design)

By default, RTM assumes:

> **The test name itself is the requirement ID.**

Example:
```
TEST(MathUtilsTest, REQ_MATH_ADD_001)
```

If your test names include extra text:
```
REQ_MATH_ADD_001_AddPositiveNumbers
```

…the default extractor will **not** match this. This is intentional.

The philosophy is:
- Start with a trivial, predictable default
- Let users explicitly define smarter extraction logic

---

## Custom requirement extraction (the key feature)

Instead of hard-coding regex rules, RTM lets you define **how requirements are extracted** from each test.

You provide a small Python function that receives:
```py
(suite: str, name: str, properties: dict[str, list[str]])
```

…and returns:
```py
list[str]  # requirement IDs
```

RTM then:
- Validates extracted IDs against `needs.json`
- Links known IDs
- Reports unknown / missing / unmapped cases separately

---

## Example: properties-first extractor (recommended)

If your JUnit XML includes properties like:
```xml
<property name="requirement" value="REQ_MATH_ADD_001"/>
```

Create a file `extractor_props_first.py`:

```py
def extract_requirements(suite, name, properties):
    # Prefer explicit metadata
    vals = properties.get("requirement")
    if vals:
        return vals

    # Fallback (optional)
    return []
```

Run RTM with:
```bash
rtm-link \
  --needs docs/_build/needs/needs.json \
  --junit build/test-results/junit.xml \
  --out rtm_report.json \
  --extractor extractor_props_first.py \
  --print
```

This is the cleanest long-term approach:
- No regex
- No naming conventions
- Stable under renames

---

## CLI usage

```bash
rtm-link \
  --needs PATH/TO/needs.json \
  --junit PATH/TO/junit.xml \
  --out PATH/TO/rtm_report.json \
  [--extractor extractor.py] \
  [--print]
```

Options:
- `--needs` – sphinx-needs `needs.json`
- `--junit` – JUnit XML from gtest
- `--out` – output JSON report
- `--extractor` – custom Python extractor module
- `--print` – print human-readable report to stdout

---

## Output

### Human-readable summary
Printed with `--print`, e.g.:

```
=== RTM Link Summary ===
Requirements: 3
  With >=1 test: 3
  Missing tests: 0
  All tests passing: 3
  Any test failing: 0
Tests: 3
  Unmapped tests (no IDs extracted): 0
  Multi-mapped tests: 0
  Tests w/ unknown req IDs: 0
  Tests w/ only unknown IDs: 0
  Unknown req IDs (unique): 0
```

### JSON report
Written to `--out`, suitable for CI, dashboards, or custom checks.

Includes:
- Summary counts
- Per-requirement test linkage
- Unmapped tests
- Multi-mapped tests
- Unknown requirement references
- Missing / failing requirements

---

## Semantics (important)

RTM distinguishes these cases explicitly:

- **Unmapped test**  
  Extractor returned *no IDs at all*.

- **Unknown requirement reference**  
  Extractor returned IDs that do not exist in `needs.json`.

- **Only unknown requirements**  
  Extractor returned IDs, but *none* were valid.

These are intentionally separate failure modes.

---

## Using RTM as a Python module

RTM is fully importable; the CLI is just a thin wrapper.

Example:

```py
from pathlib import Path
from rtm_needs import NeedsJsonReader, JUnitReader, Linker

reqs = NeedsJsonReader().read(Path("docs/_build/needs/needs.json"))
tests = JUnitReader().read(Path("build/test-results/junit.xml"))

report = Linker().link(reqs=reqs, tests=tests)
print(report)
```

With a custom extractor:

```py
def extract_requirements(suite, name, properties):
    return properties.get("requirement", [])

report = Linker().link(reqs=reqs, tests=tests, extractor=extract_requirements)
```

---

## What RTM is *not* (by design)

- Not a requirements management system
- Not a test framework
- Not a Sphinx extension
- Not a policy engine

RTM is a **bridge** between two existing artifacts:
- Requirements written for humans
- Tests written for machines

---

## Roadmap (non-binding)

Possible future additions:
- Optional CI failure policies
- Built-in extractors (name prefix, delimiters, etc.)
- Coverage percentages / thresholds
- Alternate output formats (Markdown, CSV)
- Small test suite

All intentionally deferred until real usage demands them.

---

## License

MIT (or your preferred OSS license).

---

## Philosophy

> Traceability should be explicit, inspectable, and boring.

If it’s surprising, it’s wrong.
