Source code for jamb.matrix.formats.json

"""JSON traceability matrix output."""

import json
from typing import Any

from jamb.core.models import FullChainMatrix, MatrixMetadata, TestRecord


[docs] def render_test_records_json( records: list[TestRecord], metadata: MatrixMetadata | None = None, ) -> str: """Render test records as JSON for machine processing. Args: records: List of TestRecord objects to render. metadata: Optional matrix metadata for IEC 62304 5.7.5 compliance. Returns: A string containing pretty-printed JSON with a ``metadata`` object (if provided), a ``summary`` object (totals and pass rate), and a ``tests`` array. """ # Calculate stats total = len(records) passed = sum(1 for r in records if r.outcome == "passed") failed = sum(1 for r in records if r.outcome == "failed") skipped = sum(1 for r in records if r.outcome == "skipped") error = sum(1 for r in records if r.outcome == "error") pass_rate = (100 * passed / total) if total else 0 data: dict[str, Any] = { "summary": { "total_tests": total, "passed": passed, "failed": failed, "skipped": skipped, "error": error, "pass_rate": pass_rate, }, "tests": [], } # Add metadata if provided if metadata: env = metadata.environment env_data = None if env: env_data = { "os_name": env.os_name, "os_version": env.os_version, "python_version": env.python_version, "platform": env.platform, "processor": env.processor, "hostname": env.hostname, "cpu_count": env.cpu_count, } data["metadata"] = { "software_version": metadata.software_version, "tester_id": metadata.tester_id, "execution_timestamp": metadata.execution_timestamp, "environment": env_data, "test_tools": env.test_tools if env else None, } for rec in records: data["tests"].append( { "test_id": rec.test_id, "test_name": rec.test_name, "test_nodeid": rec.test_nodeid, "outcome": rec.outcome, "requirements": rec.requirements, "test_actions": rec.test_actions, "expected_results": rec.expected_results, "actual_results": rec.actual_results, "notes": rec.notes, "execution_timestamp": rec.execution_timestamp, } ) return json.dumps(data, indent=2)
[docs] def render_full_chain_json( matrices: list[FullChainMatrix], tc_mapping: dict[str, str] | None = None, ) -> str: """Render full chain trace matrices as JSON. Args: matrices: List of FullChainMatrix objects to render. tc_mapping: Optional mapping from test nodeid to TC ID for display. Returns: A string containing pretty-printed JSON with all matrices. """ tc_mapping = tc_mapping or {} # Overall summary total = sum(m.summary.get("total", 0) for m in matrices) passed = sum(m.summary.get("passed", 0) for m in matrices) failed = sum(m.summary.get("failed", 0) for m in matrices) not_covered = sum(m.summary.get("not_covered", 0) for m in matrices) data: dict[str, Any] = { "summary": { "total_items": total, "passed": passed, "failed": failed, "not_covered": not_covered, }, "matrices": [], } for matrix in matrices: matrix_data: dict[str, Any] = { "path_name": matrix.path_name, "document_hierarchy": matrix.document_hierarchy, "include_ancestors": matrix.include_ancestors, "summary": matrix.summary, "rows": [], } for row in matrix.rows: row_data: dict[str, Any] = { "chain": {}, "rollup_status": row.rollup_status, "ancestor_uids": row.ancestor_uids, "tests": [], } # Document columns for prefix in matrix.document_hierarchy: item = row.chain.get(prefix) if item: row_data["chain"][prefix] = { "uid": item.uid, "text": item.full_display_text, "header": item.header, } else: row_data["chain"][prefix] = None # Tests for test in row.descendant_tests: tc_id = tc_mapping.get(test.test_nodeid, "") row_data["tests"].append( { "test_id": tc_id, "nodeid": test.test_nodeid, "outcome": test.test_outcome, "item_uid": test.item_uid, } ) matrix_data["rows"].append(row_data) data["matrices"].append(matrix_data) return json.dumps(data, indent=2)