diff --git a/github_pages/make_site_content.sh b/github_pages/make_site_content.sh index bbb1f1f8c2..566a7462a9 100755 --- a/github_pages/make_site_content.sh +++ b/github_pages/make_site_content.sh @@ -11,6 +11,4 @@ mkdir ./public cp -r ./monitoring/github_pages/static/* ./public mkdir -p ./public/artifacts/uss_qualifier/reports -cp -r ./artifacts/uss_qualifier/output/sequence_uspace ./public/artifacts/uss_qualifier/reports/sequence_uspace -cp -r ./artifacts/uss_qualifier/output/tested_requirements_uspace ./public/artifacts/uss_qualifier/reports/tested_requirements_uspace -cp -r ./artifacts/uss_qualifier/output/capabilities_uspace.html ./public/artifacts/uss_qualifier/reports/capabilities_uspace.html +cp -r ./artifacts/uss_qualifier/output ./public/artifacts/uss_qualifier/reports diff --git a/github_pages/static/index.md b/github_pages/static/index.md index 036b0aa6c1..02cd5b3ce0 100644 --- a/github_pages/static/index.md +++ b/github_pages/static/index.md @@ -4,10 +4,32 @@ This site contains content automatically generated by actions in the [InterUSS]( ## uss_qualifier [reports](https://github.com/interuss/monitoring/tree/main/monitoring/uss_qualifier/reports) -These reports were generated during continuous integration for the most recent PR merged to the main branch. +These reports were generated during continuous integration for the most recent PR merged to the main branch. The test configurations producing this output are in [monitoring/uss_qualifier/configurations/dev](https://github.com/interuss/monitoring/tree/main/monitoring/uss_qualifier/configurations/dev). -### [U-space developer](https://github.com/interuss/monitoring/blob/main/monitoring/uss_qualifier/configurations/dev/uspace.yaml) [test configuration](https://github.com/interuss/monitoring/tree/main/monitoring/uss_qualifier/configurations) +### [U-space test configuration](https://github.com/interuss/monitoring/blob/main/monitoring/uss_qualifier/configurations/dev/uspace.yaml) -* [Sequence view](./artifacts/uss_qualifier/reports/sequence_uspace) -* [Tested requirements](./artifacts/uss_qualifier/reports/tested_requirements_uspace) -* [Demonstrated capabilities](./artifacts/uss_qualifier/reports/capabilities_uspace.html) +* [Sequence view](./artifacts/uss_qualifier/reports/uspace/sequence) +* [Tested requirements](./artifacts/uss_qualifier/reports/uspace/requirements) +* [Demonstrated capabilities](./artifacts/uss_qualifier/reports/uspace/capabilities.html) +* [Raw report](./artifacts/uss_qualifier/reports/uspace/report.json) (large) + +### [No-op test configuration](https://github.com/interuss/monitoring/blob/main/monitoring/uss_qualifier/configurations/dev/noop.yaml) + +* [Raw report](./artifacts/uss_qualifier/reports/noop/report.json) (indented to be human-readable) +* [Interactive report](./artifacts/uss_qualifier/reports/noop/report.html) +* [Sequence view](./artifacts/uss_qualifier/reports/noop/sequence) + +### [ASTM F3548-21 test configuration](https://github.com/interuss/monitoring/blob/main/monitoring/uss_qualifier/configurations/dev/f3548_self_contained.yaml) + +* [Sequence view](./artifacts/uss_qualifier/reports/f3548/sequence) +* [Tested requirements](./artifacts/uss_qualifier/reports/f3548/requirements) + +### [ASTM F3411-22a test configuration](https://github.com/interuss/monitoring/blob/main/monitoring/uss_qualifier/configurations/dev/netrid_v22a.yaml) + +* [Sequence view](./artifacts/uss_qualifier/reports/netrid_v22a/sequence) +* [Tested requirements](./artifacts/uss_qualifier/reports/netrid_v22a/requirements) + +### [DSS integration test configuration](https://github.com/interuss/monitoring/blob/main/monitoring/uss_qualifier/configurations/dev/dss_probing.yaml) + +* [Sequence view](./artifacts/uss_qualifier/reports/dss_probing/sequence) +* [Tested requirements](./artifacts/uss_qualifier/reports/dss_probing/requirements) diff --git a/monitoring/uss_qualifier/configurations/configuration.py b/monitoring/uss_qualifier/configurations/configuration.py index e3c679323e..cd6526cd2f 100644 --- a/monitoring/uss_qualifier/configurations/configuration.py +++ b/monitoring/uss_qualifier/configurations/configuration.py @@ -27,18 +27,13 @@ class TestConfiguration(ImplicitDict): """Declarations for resources used by the test suite""" -class TestedRolesConfiguration(ImplicitDict): - report_path: str - """Path of folder to write HTML files containing a fulfilled-requirements-based view of the test report""" - - TestedRequirementsCollectionIdentifier = str """Identifier for a requirements collection, local to a TestedRequirementsConfiguration artifact configuration.""" class TestedRequirementsConfiguration(ImplicitDict): - output_path: str - """Path of a folder into which report HTML files should be written""" + report_name: str + """Name of subfolder in output path to contain the rendered templated report""" requirement_collections: Optional[ Dict[TestedRequirementsCollectionIdentifier, RequirementCollection] @@ -52,13 +47,13 @@ class TestedRequirementsConfiguration(ImplicitDict): class SequenceViewConfiguration(ImplicitDict): - output_path: str - """Path of a folder into which report HTML files should be written""" + redact_access_tokens: bool = True + """When True, look for instances of "Authorization" keys in the report with values starting "Bearer " and redact the signature from those access tokens""" class ReportHTMLConfiguration(ImplicitDict): - html_path: str - """Path of HTML file to contain an HTML rendering of the raw test report object""" + redact_access_tokens: bool = True + """When True, look for instances of "Authorization" keys in the report with values starting "Bearer " and redact the signature from those access tokens""" class TemplatedReportInjectedConfiguration(ImplicitDict): @@ -69,44 +64,36 @@ class TemplatedReportConfiguration(ImplicitDict): template_url: str """Url of the template to download from""" - output_path: str - """Path of HTML file to contain the rendered templated report""" + report_name: str + """Name of HTML file (without extension) to contain the rendered templated report""" configuration: Optional[TemplatedReportInjectedConfiguration] = None """Configuration to be injected in the templated report""" -class GraphConfiguration(ImplicitDict): - gv_path: str - """Path of GraphViz (.gv) text file to contain a visualization of the test run""" - +class RawReportConfiguration(ImplicitDict): + redact_access_tokens: bool = True + """When True, look for instances of "Authorization" keys in the report with values starting "Bearer " and redact the signature from those access tokens""" -class ReportConfiguration(ImplicitDict): - report_path: str - """File name of the report to write (if test_config provided) or read (if test_config not provided)""" + indent: Optional[int] = None + """To pretty-print JSON content, specify an indent level (generally 2), or omit or set to None to write compactly.""" class ArtifactsConfiguration(ImplicitDict): - redact_access_tokens: bool = True - """When True, look for instances of "Authorization" keys in the report with values starting "Bearer " and redact the signature from those access tokens""" + output_path: str + """Path to folder where artifacts should be written.""" - report: Optional[ReportConfiguration] = None - """Configuration for report generation""" + raw_report: Optional[RawReportConfiguration] = None + """Configuration for raw report generation""" report_html: Optional[ReportHTMLConfiguration] = None - """If specified, configuration describing how an HTML version of the report should be generated""" + """If specified, configuration describing how an HTML version of the raw report should be generated""" - templated_reports: List[TemplatedReportConfiguration] = [] + templated_reports: Optional[List[TemplatedReportConfiguration]] = None """List of report templates to be rendered""" - graph: Optional[GraphConfiguration] = None - """If specified, configuration describing a desired graph visualization summarizing the test run""" - - tested_roles: Optional[TestedRolesConfiguration] = None - """If specified, configuration describing a desired report summarizing tested requirements for each specified participant and role""" - - tested_requirements: Optional[TestedRequirementsConfiguration] = None - """If specified, configuration describing a desired report summarizing all tested requirements for each participant""" + tested_requirements: Optional[List[TestedRequirementsConfiguration]] = None + """If specified, list of configurations describing desired reports summarizing tested requirements for each participant""" sequence_view: Optional[SequenceViewConfiguration] = None """If specified, configuration describing a desired report describing the sequence of events that occurred during the test""" diff --git a/monitoring/uss_qualifier/configurations/dev/dss_probing.yaml b/monitoring/uss_qualifier/configurations/dev/dss_probing.yaml index 6b458f94b0..af807b8ab7 100644 --- a/monitoring/uss_qualifier/configurations/dev/dss_probing.yaml +++ b/monitoring/uss_qualifier/configurations/dev/dss_probing.yaml @@ -26,19 +26,20 @@ v1: service_area: kentland_service_area problematically_big_area: kentland_problematically_big_area artifacts: - report: - report_path: output/report_dss_probing.json + output_path: output/dss_probing + raw_report: {} + sequence_view: {} tested_requirements: - output_path: output/tested_requirements_dss_probing - requirement_collections: - all_astm_dss_requirements: - requirement_collections: - - requirement_sets: - - astm.f3411.v22a.dss_provider - - astm.f3411.v19.dss_provider - participant_requirements: - uss1: all_astm_dss_requirements - uss2: all_astm_dss_requirements + - report_name: requirements + requirement_collections: + all_astm_dss_requirements: + requirement_collections: + - requirement_sets: + - astm.f3411.v22a.dss_provider + - astm.f3411.v19.dss_provider + participant_requirements: + uss1: all_astm_dss_requirements + uss2: all_astm_dss_requirements validation: criteria: - full_success: {} diff --git a/monitoring/uss_qualifier/configurations/dev/f3548_self_contained.yaml b/monitoring/uss_qualifier/configurations/dev/f3548_self_contained.yaml index 413fbf3cfc..cc74bc3a10 100644 --- a/monitoring/uss_qualifier/configurations/dev/f3548_self_contained.yaml +++ b/monitoring/uss_qualifier/configurations/dev/f3548_self_contained.yaml @@ -93,23 +93,23 @@ v1: # relative to where uss_qualifier is executed from, and are located inside the # Docker container executing uss_qualifier. artifacts: + # Write artifacts to this folder (relative to uss_qualifier) + output_path: output/f3548 + # Write out full report content - report: - # Path to main report output - report_path: output/report_f3548_self_contained.json + raw_report: {} - # Write out a human-readable report of the requirements tested + # Write out a human-readable report of the F3548-21 requirements tested tested_requirements: - output_path: output/tested_requirements_f3548_self_contained - requirement_collections: - scd: - requirement_collections: - - requirement_sets: - - astm.f3548.v21.scd - participant_requirements: - uss1: scd - uss2: scd + - report_name: requirements + requirement_collections: + scd: + requirement_collections: + - requirement_sets: + - astm.f3548.v21.scd + participant_requirements: + uss1: scd + uss2: scd # Write out a human-readable report showing the sequence of events of the test - sequence_view: - output_path: output/sequence_f3548_self_contained + sequence_view: {} diff --git a/monitoring/uss_qualifier/configurations/dev/general_flight_auth.yaml b/monitoring/uss_qualifier/configurations/dev/general_flight_auth.yaml index f9126e75d4..eda18dbd56 100644 --- a/monitoring/uss_qualifier/configurations/dev/general_flight_auth.yaml +++ b/monitoring/uss_qualifier/configurations/dev/general_flight_auth.yaml @@ -10,7 +10,7 @@ v1: resources: table: example_flight_check_table artifacts: - report: - report_path: output/report_general_flight_auth.json + output_path: output/general_flight_auth + raw_report: {} validation: $ref: ./library/validation.yaml#/normal_test diff --git a/monitoring/uss_qualifier/configurations/dev/geoawareness_cis.yaml b/monitoring/uss_qualifier/configurations/dev/geoawareness_cis.yaml index e44ba54145..373b631fea 100644 --- a/monitoring/uss_qualifier/configurations/dev/geoawareness_cis.yaml +++ b/monitoring/uss_qualifier/configurations/dev/geoawareness_cis.yaml @@ -13,9 +13,7 @@ v1: resources: source_document: source_document artifacts: - report: - report_path: output/report_geoawareness_cis.json - graph: - gv_path: output/report_geoawareness_cis.gv + output_path: output/geoawareness_cis + raw_report: {} validation: $ref: ./library/validation.yaml#/normal_test diff --git a/monitoring/uss_qualifier/configurations/dev/geospatial_comprehension.yaml b/monitoring/uss_qualifier/configurations/dev/geospatial_comprehension.yaml index d96d005596..d48799efb5 100644 --- a/monitoring/uss_qualifier/configurations/dev/geospatial_comprehension.yaml +++ b/monitoring/uss_qualifier/configurations/dev/geospatial_comprehension.yaml @@ -10,7 +10,7 @@ v1: resources: table: example_feature_check_table artifacts: - report: - report_path: output/report_geospatial_comprehension.json + output_path: output/geospatial_comprehension + raw_report: {} validation: $ref: ./library/validation.yaml#/normal_test diff --git a/monitoring/uss_qualifier/configurations/dev/library/resources.yaml b/monitoring/uss_qualifier/configurations/dev/library/resources.yaml index 5837c2da8f..bdc07830bd 100644 --- a/monitoring/uss_qualifier/configurations/dev/library/resources.yaml +++ b/monitoring/uss_qualifier/configurations/dev/library/resources.yaml @@ -101,13 +101,13 @@ adjacent_circular_storage_config: $content_schema: monitoring/uss_qualifier/resources/definitions/ResourceDeclaration.json resource_type: resources.netrid.FlightDataStorageResource specification: - flight_record_collection_path: "./output/test_data.che.netrid.circular_flights.json" + flight_record_collection_path: "./output/generate_rid_test_data/flight_data/test_data.che.netrid.circular_flights.json" kml_storage_config: $content_schema: monitoring/uss_qualifier/resources/definitions/ResourceDeclaration.json resource_type: resources.netrid.FlightDataStorageResource specification: - flight_record_collection_path: "./output/test_data.usa.netrid.dcdemo_flights.json" + flight_record_collection_path: "./output/generate_rid_test_data/flight_data/test_data.usa.netrid.dcdemo_flights.json" # ===== Flight planning intents ===== diff --git a/monitoring/uss_qualifier/configurations/dev/message_signing.yaml b/monitoring/uss_qualifier/configurations/dev/message_signing.yaml index ea33cc7af7..dfb2ec8491 100644 --- a/monitoring/uss_qualifier/configurations/dev/message_signing.yaml +++ b/monitoring/uss_qualifier/configurations/dev/message_signing.yaml @@ -50,5 +50,5 @@ v1: dss: scd_dss artifacts: - report: - report_path: output/report_message_signing.json + output_path: output/message_signing + raw_report: {} diff --git a/monitoring/uss_qualifier/configurations/dev/netrid_v19.yaml b/monitoring/uss_qualifier/configurations/dev/netrid_v19.yaml index 65e3b32062..3a63bd3cb3 100644 --- a/monitoring/uss_qualifier/configurations/dev/netrid_v19.yaml +++ b/monitoring/uss_qualifier/configurations/dev/netrid_v19.yaml @@ -31,26 +31,23 @@ v1: service_area: kentland_service_area problematically_big_area: au_problematically_big_area artifacts: - report: - report_path: output/report_netrid_v19.json - tested_roles: - report_path: output/tested_roles_netrid_v19 + output_path: output/netrid_v19 + raw_report: {} tested_requirements: - output_path: output/tested_requirements_f3411v19 - requirement_collections: - sp_dp_dss: - requirement_sets: - - astm.f3411.v19.service_provider#Tested by automated tests - - astm.f3411.v19.display_provider#Automated verification - - astm.f3411.v19.dss_provider - sp_dss: - requirement_sets: - - astm.f3411.v19.service_provider#Tested by automated tests - - astm.f3411.v19.dss_provider - participant_requirements: - uss1: sp_dp_dss - uss2: sp_dss - sequence_view: - output_path: output/sequence_netrid_v19 + - report_name: requirements + requirement_collections: + sp_dp_dss: + requirement_sets: + - astm.f3411.v19.service_provider#Tested by automated tests + - astm.f3411.v19.display_provider#Automated verification + - astm.f3411.v19.dss_provider + sp_dss: + requirement_sets: + - astm.f3411.v19.service_provider#Tested by automated tests + - astm.f3411.v19.dss_provider + participant_requirements: + uss1: sp_dp_dss + uss2: sp_dss + sequence_view: {} validation: $ref: ./library/validation.yaml#/normal_test diff --git a/monitoring/uss_qualifier/configurations/dev/netrid_v22a.yaml b/monitoring/uss_qualifier/configurations/dev/netrid_v22a.yaml index 7d919e2ebd..5441555c5a 100644 --- a/monitoring/uss_qualifier/configurations/dev/netrid_v22a.yaml +++ b/monitoring/uss_qualifier/configurations/dev/netrid_v22a.yaml @@ -31,26 +31,23 @@ v1: service_area: kentland_service_area problematically_big_area: au_problematically_big_area artifacts: - report: - report_path: output/report_netrid_v22a.json - tested_roles: - report_path: output/tested_roles_netrid_v22a + output_path: output/netrid_v22a + raw_report: {} tested_requirements: - output_path: output/tested_requirements_f3411v22a - requirement_collections: - sp_dp_dss: - requirement_sets: - - astm.f3411.v22a.service_provider#Mandatory requirements - - astm.f3411.v22a.display_provider#Mandatory requirements - - astm.f3411.v22a.dss_provider - sp_dss: - requirement_sets: - - astm.f3411.v22a.service_provider#Mandatory requirements - - astm.f3411.v22a.dss_provider - participant_requirements: - uss1: sp_dp_dss - uss2: sp_dss - sequence_view: - output_path: output/sequence_netrid_v22a + - report_name: requirements + requirement_collections: + sp_dp_dss: + requirement_sets: + - astm.f3411.v22a.service_provider#Mandatory requirements + - astm.f3411.v22a.display_provider#Mandatory requirements + - astm.f3411.v22a.dss_provider + sp_dss: + requirement_sets: + - astm.f3411.v22a.service_provider#Mandatory requirements + - astm.f3411.v22a.dss_provider + participant_requirements: + uss1: sp_dp_dss + uss2: sp_dss + sequence_view: {} validation: $ref: ./library/validation.yaml#/normal_test diff --git a/monitoring/uss_qualifier/configurations/dev/noop.yaml b/monitoring/uss_qualifier/configurations/dev/noop.yaml index 95af477c6d..be427560a8 100644 --- a/monitoring/uss_qualifier/configurations/dev/noop.yaml +++ b/monitoring/uss_qualifier/configurations/dev/noop.yaml @@ -24,8 +24,10 @@ v1: noop_config: noop_config artifacts: - report: - # Path to main report output - report_path: output/report_noop.json + output_path: output/noop + raw_report: + indent: 2 + sequence_view: {} + report_html: {} validation: $ref: ./library/validation.yaml#/normal_test diff --git a/monitoring/uss_qualifier/configurations/dev/uspace.yaml b/monitoring/uss_qualifier/configurations/dev/uspace.yaml index aa8c389757..8aa10486c9 100644 --- a/monitoring/uss_qualifier/configurations/dev/uspace.yaml +++ b/monitoring/uss_qualifier/configurations/dev/uspace.yaml @@ -83,41 +83,38 @@ v1: service_area: service_area problematically_big_area: problematically_big_area artifacts: - tested_roles: - report_path: output/tested_roles_uspace - report: - report_path: output/report_uspace.json + output_path: output/uspace + raw_report: {} templated_reports: - template_url: https://github.com/Orbitalize/reports/releases/download/v0.0.18/app-v0.0.18.zip - output_path: output/capabilities_uspace.html + report_name: capabilities tested_requirements: - output_path: output/tested_requirements_uspace - requirement_collections: - uspace: - requirement_collections: - - requirement_sets: - - astm.f3411.v22a.service_provider#Mandatory requirements - - astm.f3411.v22a.service_provider#Operator ID provider - - astm.f3411.v22a.service_provider#UAS ID Serial Number provider - - astm.f3411.v22a.service_provider#Height provider - - astm.f3411.v22a.service_provider#Operator Position provider - - astm.f3411.v22a.service_provider#Operational Status provider - - astm.f3411.v22a.display_provider#Mandatory requirements - - astm.f3411.v22a.display_provider#UAS ID Serial Number transmitter - - astm.f3411.v22a.display_provider#Timestamp transmitter - - astm.f3411.v22a.display_provider#Operational Status transmitter - - astm.f3411.v22a.display_provider#Operator ID transmitter - - astm.f3411.v22a.display_provider#Current Position transmitter - - astm.f3411.v22a.display_provider#Height transmitter - - astm.f3411.v22a.display_provider#Track Direction transmitter - - astm.f3411.v22a.display_provider#Speed transmitter - - astm.f3411.v22a.display_provider#Operator Position transmitter - - astm.f3411.v22a.dss_provider - - astm.f3548.v21.scd#Automated verification - participant_requirements: - uss1: uspace - uss2: uspace - sequence_view: - output_path: output/sequence_uspace + - report_name: requirements + requirement_collections: + uspace: + requirement_collections: + - requirement_sets: + - astm.f3411.v22a.service_provider#Mandatory requirements + - astm.f3411.v22a.service_provider#Operator ID provider + - astm.f3411.v22a.service_provider#UAS ID Serial Number provider + - astm.f3411.v22a.service_provider#Height provider + - astm.f3411.v22a.service_provider#Operator Position provider + - astm.f3411.v22a.service_provider#Operational Status provider + - astm.f3411.v22a.display_provider#Mandatory requirements + - astm.f3411.v22a.display_provider#UAS ID Serial Number transmitter + - astm.f3411.v22a.display_provider#Timestamp transmitter + - astm.f3411.v22a.display_provider#Operational Status transmitter + - astm.f3411.v22a.display_provider#Operator ID transmitter + - astm.f3411.v22a.display_provider#Current Position transmitter + - astm.f3411.v22a.display_provider#Height transmitter + - astm.f3411.v22a.display_provider#Track Direction transmitter + - astm.f3411.v22a.display_provider#Speed transmitter + - astm.f3411.v22a.display_provider#Operator Position transmitter + - astm.f3411.v22a.dss_provider + - astm.f3548.v21.scd#Automated verification + participant_requirements: + uss1: uspace + uss2: uspace + sequence_view: {} validation: $ref: ./library/validation.yaml#/normal_test diff --git a/monitoring/uss_qualifier/main.py b/monitoring/uss_qualifier/main.py index 900ca905f6..8351de8b2c 100644 --- a/monitoring/uss_qualifier/main.py +++ b/monitoring/uss_qualifier/main.py @@ -14,7 +14,7 @@ from monitoring.uss_qualifier.configurations.configuration import ( USSQualifierConfiguration, ArtifactsConfiguration, - ReportConfiguration, + RawReportConfiguration, USSQualifierConfigurationV1, ) from monitoring.uss_qualifier.fileio import load_dict_with_references @@ -23,8 +23,6 @@ from monitoring.uss_qualifier.reports.tested_requirements import ( generate_tested_requirements, ) -from monitoring.uss_qualifier.reports.tested_roles import generate_tested_roles -from monitoring.uss_qualifier.reports.graphs import make_graph from monitoring.uss_qualifier.reports.report import TestRunReport, redact_access_tokens from monitoring.uss_qualifier.reports.templates import render_templates from monitoring.uss_qualifier.reports.validation.report_validation import ( @@ -159,10 +157,10 @@ def run_config( if report_path: if not config.artifacts: config.artifacts = ArtifactsConfiguration( - ReportConfiguration(report_path=report_path) + RawReportConfiguration(report_path=report_path) ) elif not config.artifacts.report: - config.artifacts.report = ReportConfiguration(report_path=report_path) + config.artifacts.report = RawReportConfiguration(report_path=report_path) else: config.artifacts.report.report_path = report_path @@ -180,46 +178,70 @@ def run_config( ) if config.artifacts: - if config.artifacts.report and not do_not_save_report: - if config.artifacts.redact_access_tokens: - logger.info("Redacting access tokens in report") - redact_access_tokens(report) - logger.info("Writing report to {}", config.artifacts.report.report_path) - with open(config.artifacts.report.report_path, "w") as f: - json.dump(report, f, indent=2) + os.makedirs(config.artifacts.output_path, exist_ok=True) - if config.artifacts.report_html: - logger.info( - "Writing HTML report to {}", config.artifacts.report_html.html_path - ) - with open(config.artifacts.report_html.html_path, "w") as f: - f.write(make_report_html(report)) - - if len(config.artifacts.templated_reports) > 0: + def _should_redact(cfg) -> bool: + return "redact_access_tokens" in cfg and cfg.redact_access_tokens - render_templates(config.artifacts, report) + logger.info(f"Redacting access tokens from report") + redacted_report = ImplicitDict.parse( + json.loads(json.dumps(report)), TestRunReport + ) + redact_access_tokens(redacted_report) + + if config.artifacts.raw_report and not do_not_save_report: + # Raw report + path = os.path.join(config.artifacts.output_path, "report.json") + logger.info(f"Writing raw report to {path}") + raw_report = config.artifacts.raw_report + report_to_write = redacted_report if _should_redact(raw_report) else report + with open(path, "w") as f: + if "indent" in raw_report and raw_report.indent is not None: + json.dump(report_to_write, f, indent=raw_report.indent) + else: + json.dump(report_to_write, f) - if config.artifacts.graph: - logger.info( - "Writing GraphViz dot source to {}", config.artifacts.graph.gv_path + if config.artifacts.report_html: + # HTML rendering of raw report + path = os.path.join(config.artifacts.output_path, "report.html") + logger.info(f"Writing HTML report to {path}") + report_to_write = ( + redacted_report + if _should_redact(config.artifacts.report_html) + else report + ) + with open(path, "w") as f: + f.write(make_report_html(report_to_write)) + + if config.artifacts.templated_reports: + # Templated reports + render_templates( + config.artifacts.output_path, + config.artifacts.templated_reports, + redacted_report, ) - with open(config.artifacts.graph.gv_path, "w") as f: - f.write(make_graph(report).source) - - if config.artifacts.tested_roles: - path = config.artifacts.tested_roles.report_path - logger.info("Writing tested roles view to {}", path) - generate_tested_roles(report, path) if config.artifacts.tested_requirements: - path = config.artifacts.tested_requirements.output_path - logger.info(f"Writing tested requirements view to {path}") - generate_tested_requirements(report, config.artifacts.tested_requirements) + # Tested requirements view + for tested_reqs_config in config.artifacts.tested_requirements: + path = os.path.join( + config.artifacts.output_path, tested_reqs_config.report_name + ) + logger.info(f"Writing tested requirements view to {path}") + generate_tested_requirements(redacted_report, tested_reqs_config, path) if config.artifacts.sequence_view: - path = config.artifacts.sequence_view.output_path + # Sequence view + path = os.path.join(config.artifacts.output_path, "sequence") logger.info(f"Writing sequence view to {path}") - generate_sequence_view(report, config.artifacts.sequence_view) + report_to_write = ( + redacted_report + if _should_redact(config.artifacts.sequence_view) + else report + ) + generate_sequence_view( + report_to_write, config.artifacts.sequence_view, path + ) if "validation" in config and config.validation: logger.info(f"Validating test run report for configuration '{config_name}'") diff --git a/monitoring/uss_qualifier/reports/graphs.py b/monitoring/uss_qualifier/reports/graphs.py deleted file mode 100644 index a9264faee8..0000000000 --- a/monitoring/uss_qualifier/reports/graphs.py +++ /dev/null @@ -1,317 +0,0 @@ -from typing import Optional, List, Set, Tuple, Dict - -import graphviz -from loguru import logger - -from implicitdict import ImplicitDict - -from monitoring.uss_qualifier.reports.report import ( - ActionGeneratorReport, - TestRunReport, - TestSuiteReport, - TestScenarioReport, -) -from monitoring.uss_qualifier.resources.definitions import ( - ResourceID, - ResourceCollection, -) -from monitoring.uss_qualifier.scenarios.definitions import TestScenarioDeclaration -from monitoring.uss_qualifier.suites.definitions import ( - ActionType, - TestSuiteDeclaration, - TestSuiteDefinition, - ActionGeneratorDefinition, -) - -NodeName = str - - -class Node(ImplicitDict): - """Represents a node to be used in a GraphViz graph.""" - - name: NodeName - label: Optional[str] = None - children: List[NodeName] - attributes: Dict[str, str] - - -class NodeNamer(object): - """Creates and tracks unique, valid names within a GraphViz graph.""" - - names: Set[NodeName] = set() - - def make_name(self, desired_name: NodeName) -> NodeName: - acceptable_name = desired_name.replace(" ", "").replace(".", "_") - actual_name = acceptable_name - i = 2 - while actual_name in self.names: - actual_name = f"{acceptable_name}_{i}" - i += 1 - self.names.add(actual_name) - return actual_name - - def use_name(self, name: NodeName) -> None: - self.names.add(name) - - -def _make_test_scenario_nodes( - declaration: Optional[TestScenarioDeclaration], - report: TestScenarioReport, - nodes_by_id: Dict[ResourceID, Node], - namer: NodeNamer, - include_notes: bool = False, -) -> List[Node]: - nodes: List[Node] = [] - - # Make the scenario node - scenario_type = report.scenario_type - if scenario_type.startswith("scenarios."): - scenario_type = scenario_type[len("scenarios.") :] - label_elements = [report.name, scenario_type] - if include_notes and "notes" in report: - label_elements += [f"{k}={v.message}" for k, v in report.notes.items()] - scenario_node = Node( - name=namer.make_name(report.scenario_type), - label="\n".join(label_elements), - children=[], - attributes={ - "shape": "component", - "fillcolor": "lightgreen" if report.successful else "lightpink", - "style": "filled", - }, - ) - - # Mark the scenario node a child of the appropriate resources - if declaration is not None: - for local_id, node in nodes_by_id.items(): - node.children.append(scenario_node.name) - - # Add failed checks and error below - parent_node = scenario_node - for _, failed_check in report.query_failed_checks(): - check_node = Node( - name=namer.make_name(report.scenario_type + "FailedCheck"), - label=failed_check.summary, - children=[], - attributes={ - "shape": "octagon", - "fillcolor": "lightpink", - "style": "filled", - }, - ) - nodes.append(check_node) - parent_node.children.append(check_node.name) - parent_node = check_node - if "execution_error" in report: - error_node = Node( - name=namer.make_name(report.scenario_type + "ExecutionError"), - label=report.execution_error.message, - children=[], - attributes={ - "shape": "octagon", - "fillcolor": "lightpink", - "style": "filled", - "color": "red", - }, - ) - nodes.append(error_node) - parent_node.children.append(error_node.name) - - # Add the scenario node last - nodes.append(scenario_node) - - return nodes - - -def _make_test_suite_nodes( - declaration: TestSuiteDeclaration, - report: TestSuiteReport, - nodes_by_id: Dict[ResourceID, Node], - namer: NodeNamer, -) -> List[Node]: - # Make child nodes for each action in the suite - new_nodes = None - nodes: List[Node] = [] - children: List[NodeName] = [] - definition = TestSuiteDefinition.load_from_declaration(declaration) - for action_report, action in zip(report.actions, definition.actions): - action_nodes = _translate_ids(nodes_by_id, action.get_resource_links()) - if "test_suite" in action_report: - new_nodes = _make_test_suite_nodes( - action.test_suite, - action_report.test_suite, - action_nodes, - namer, - ) - children.append(new_nodes[-1].name) - elif "test_scenario" in action_report: - new_nodes = _make_test_scenario_nodes( - action.test_scenario, action_report.test_scenario, action_nodes, namer - ) - nodes.extend(new_nodes) - children.append(new_nodes[-1].name) - elif "action_generator" in action_report: - new_nodes = _make_action_generator_nodes( - action.action_generator, - action_report.action_generator, - action_nodes, - namer, - ) - children.append(new_nodes[-1].name) - else: - ActionType.raise_invalid_action_declaration() - nodes.extend(new_nodes) - - # Make the suite node itself - suite_type = report.suite_type - if suite_type.startswith("suites."): - suite_type = suite_type[len("suites.") :] - suite_node = Node( - name=namer.make_name(report.suite_type), - label=report.name + "\n" + suite_type, - children=children, - attributes={"shape": "folder"}, - ) - nodes.append(suite_node) - return nodes - - -def _make_action_generator_nodes( - definition: ActionGeneratorDefinition, - report: ActionGeneratorReport, - nodes_by_id: Dict[ResourceID, Node], - namer: NodeNamer, -) -> List[Node]: - # Make the action generator node - nodes: List[Node] = [] - children: List[NodeName] = [] - for action in report.actions: - if "test_suite" in action: - # TODO: Support visualization of test suite actions with an action generator - logger.warning( - "test_suite action is being omitted from the action generator because graph representation of a test suite from an action generator is not yet supported" - ) - new_nodes = [] - elif "test_scenario" in action: - new_nodes = _make_test_scenario_nodes( - None, action.test_scenario, nodes_by_id, namer, True - ) - nodes.extend(new_nodes) - children.append(new_nodes[-1].name) - elif "action_generator" in action: - raise NotImplementedError() - else: - raise NotImplementedError() - nodes.extend(new_nodes) - generator_type = report.generator_type - if generator_type.startswith("action_generators."): - generator_type = generator_type[len("action_generators.") :] - generator_node = Node( - name=namer.make_name(report.generator_type), - label="Action generator\n" + generator_type, - children=children, - attributes={"shape": "box3d"}, - ) - nodes.append(generator_node) - - # Point all resources used by children of the action generator to the action generator - for local_id, node in _translate_ids(nodes_by_id, definition.resources).items(): - node.children.append(generator_node.name) - - return nodes - - -def _make_resource_nodes( - resources: ResourceCollection, namer: NodeNamer -) -> Tuple[List[Node], Dict[ResourceID, Node]]: - nodes: List[Node] = [] - nodes_by_id: Dict[ResourceID, Node] = {} - added = 1 - while added > 0: - added = 0 - for resource_id, declaration in resources.resource_declarations.items(): - # Skip resources that already have nodes - if resource_id in namer.names: - continue - - # Identify prerequisite resources not yet created - prereqs = [] - for child_param, parent_id in declaration.dependencies.items(): - if parent_id not in nodes_by_id: - prereqs.append(parent_id) - - # Create this resource node if there are no uncreated prerequisites - if not prereqs: - resource_type = declaration.resource_type - if resource_type.startswith("resources."): - resource_type = resource_type[len("resources.") :] - resource_node = Node( - name=namer.make_name(resource_id), - label=f"{resource_id}\n{resource_type}", - children=[], - attributes={ - "shape": "note", - "fillcolor": "lightskyblue1", - "style": "filled", - }, - ) - namer.use_name(resource_id) - nodes.append(resource_node) - nodes_by_id[resource_id] = resource_node - for child_param, parent_id in declaration.dependencies.items(): - nodes_by_id[parent_id].children.append(resource_node.name) - added += 1 - return nodes, nodes_by_id - - -def _translate_ids( - nodes_by_id: Dict[ResourceID, Node], local_resources: Dict[ResourceID, ResourceID] -) -> Dict[ResourceID, Node]: - local_id_by_parent_id = {v: k for k, v in local_resources.items()} - return { - local_id_by_parent_id[parent_id]: node - for parent_id, node in nodes_by_id.items() - if parent_id in local_id_by_parent_id - } - - -def make_graph(report: TestRunReport) -> graphviz.Digraph: - namer = NodeNamer() - - # Make nodes for resources - nodes, nodes_by_id = _make_resource_nodes(report.configuration.resources, namer) - - action_type = report.configuration.action.get_action_type() - if action_type == ActionType.TestSuite: - test_suite = report.configuration.action.test_suite - test_suite_report = report.report.test_suite - - # Translate resource names into the action frame - suite_nodes_by_id = _translate_ids(nodes_by_id, test_suite.resources) - - # Make nodes for the suite - nodes.extend( - _make_test_suite_nodes( - test_suite, test_suite_report, suite_nodes_by_id, namer - ) - ) - elif action_type == ActionType.TestScenario: - test_scenario = report.configuration.action.test_scenario - test_scenario_report = report.report.test_scenario - scenario_nodes_by_id = _translate_ids(nodes_by_id, test_scenario.resources) - nodes.extend( - _make_test_scenario_nodes( - test_scenario, test_scenario_report, scenario_nodes_by_id, namer - ) - ) - else: - raise NotImplementedError() - - # Translate nodes into GraphViz - dot = graphviz.Digraph(node_attr={"shape": "box"}) - for node in nodes: - dot.node(name=node.name, label=node.label, **node.attributes) - for child in node.children: - dot.edge(node.name, child) - - return dot diff --git a/monitoring/uss_qualifier/reports/sequence_view.py b/monitoring/uss_qualifier/reports/sequence_view.py index cba2d75efa..c87b3f52a4 100644 --- a/monitoring/uss_qualifier/reports/sequence_view.py +++ b/monitoring/uss_qualifier/reports/sequence_view.py @@ -597,7 +597,7 @@ def _enumerate_all_participants(node: ActionNode) -> List[ParticipantID]: def _generate_scenario_pages( - node: ActionNode, config: SequenceViewConfiguration + node: ActionNode, config: SequenceViewConfiguration, output_path: str ) -> None: if node.node_type == ActionNodeType.Scenario: all_participants = list(node.scenario.participants) @@ -606,7 +606,7 @@ def _generate_scenario_pages( all_participants.remove(UNATTRIBUTED_PARTICIPANT) all_participants.append(UNATTRIBUTED_PARTICIPANT) scenario_file = os.path.join( - config.output_path, f"s{node.scenario.scenario_index}.html" + output_path, f"s{node.scenario.scenario_index}.html" ) template = jinja_env.get_template("sequence_view/scenario.html") with open(scenario_file, "w") as f: @@ -623,16 +623,16 @@ def _generate_scenario_pages( ) else: for child in node.children: - _generate_scenario_pages(child, config) + _generate_scenario_pages(child, config, output_path) def generate_sequence_view( - report: TestRunReport, config: SequenceViewConfiguration + report: TestRunReport, config: SequenceViewConfiguration, output_path: str ) -> None: node = _compute_action_node(report.report, Indexer()) - os.makedirs(config.output_path, exist_ok=True) - _generate_scenario_pages(node, config) + os.makedirs(output_path, exist_ok=True) + _generate_scenario_pages(node, config, output_path) overview_rows = list(_compute_overview_rows(node)) _align_overview_rows(overview_rows) @@ -642,7 +642,7 @@ def generate_sequence_view( if UNATTRIBUTED_PARTICIPANT in all_participants: all_participants.remove(UNATTRIBUTED_PARTICIPANT) all_participants.append(UNATTRIBUTED_PARTICIPANT) - overview_file = os.path.join(config.output_path, "index.html") + overview_file = os.path.join(output_path, "index.html") template = jinja_env.get_template("sequence_view/overview.html") with open(overview_file, "w") as f: f.write( diff --git a/monitoring/uss_qualifier/reports/templates.py b/monitoring/uss_qualifier/reports/templates.py index 5a1b36ae66..5ac2a05472 100644 --- a/monitoring/uss_qualifier/reports/templates.py +++ b/monitoring/uss_qualifier/reports/templates.py @@ -1,6 +1,7 @@ import json import shutil import os +from typing import List from loguru import logger @@ -57,10 +58,12 @@ def _download_template(self) -> pathlib.Path: logger.debug(f"{url} extracted to {path}") return path - def render(self): + def render(self, base_path: str): # Copy template src = pathlib.Path(self._download_template(), "index.html") - dst = pathlib.Path(self._template.output_path) + dst = pathlib.Path( + os.path.join(base_path, self._template.report_name + ".html") + ) # Configure application rendered_configuration = json.dumps( @@ -82,7 +85,11 @@ def render(self): logger.info(f"Templated report rendered to {dst}") -def render_templates(config: ArtifactsConfiguration, report: TestRunReport): +def render_templates( + base_path: str, + templated_reports: List[TemplatedReportConfiguration], + report: TestRunReport, +): pathlib.Path(CACHE_TEMPLATE_PATH).mkdir(parents=True, exist_ok=True) - for template in config.templated_reports: - TemplateRenderer(template, report).render() + for template in templated_reports: + TemplateRenderer(template, report).render(base_path) diff --git a/monitoring/uss_qualifier/reports/templates/tested_roles/capability_evaluation_report.html b/monitoring/uss_qualifier/reports/templates/tested_roles/capability_evaluation_report.html deleted file mode 100644 index b057e70fa8..0000000000 --- a/monitoring/uss_qualifier/reports/templates/tested_roles/capability_evaluation_report.html +++ /dev/null @@ -1,101 +0,0 @@ - -
- - - -Capability | Evaluation | -|||||||||
---|---|---|---|---|---|---|---|---|---|---|
{{ capability.name }} | -Requirement | -Test check | -Result | -|||||||
{{ req.requirement_id }} | - {% endif %} - -{{ pc }} ({{ req.passed_checks[pc] }}x) | - -PASS | -||||||||
{{ req.requirement_id }} | - {% endif %} - -{{ fc.name }} | - -FAIL | -||||||||
{{ req.requirement_id }} | -Not tested | -|||||||||
{{ capability.name }} | - {% endif %} -Capability | -Result | -||||||||
{{ child_cap.capability_id }} | -{{ 'VERIFIED' if child_cap.verified else 'MISSING' if child_cap.missing else 'UNVERIFIED' }} | -