S03-H3 CLOSED baseline
This commit is contained in:
+185
@@ -0,0 +1,185 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Export the approved S03 binary record layout to a derived CSV artifact."""
|
||||
|
||||
import argparse
|
||||
import csv
|
||||
import hashlib
|
||||
import os
|
||||
import struct
|
||||
import sys
|
||||
import tempfile
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
UINT32_TYPE_HEX = "0408"
|
||||
SIGNAL_HEADER_BYTES = 38
|
||||
EXPECTED_SIGNALS = (
|
||||
("Counter", 1),
|
||||
("Time", 1),
|
||||
("State1_Thread1_CycleTime", 1),
|
||||
("SentinelScalarA", 1),
|
||||
("SentinelScalarB", 1),
|
||||
("SentinelVector", 3),
|
||||
)
|
||||
EXPECTED_HEADER_BYTES = 4 + len(EXPECTED_SIGNALS) * SIGNAL_HEADER_BYTES
|
||||
EXPECTED_RECORD_BYTES = 32
|
||||
CSV_COLUMNS = (
|
||||
"Counter",
|
||||
"Time",
|
||||
"CycleTime",
|
||||
"SentinelScalarA",
|
||||
"SentinelScalarB",
|
||||
"SentinelVector0",
|
||||
"SentinelVector1",
|
||||
"SentinelVector2",
|
||||
)
|
||||
|
||||
|
||||
class ExportError(Exception):
|
||||
"""A fail-closed binary or publication error."""
|
||||
|
||||
|
||||
def decode_name(raw):
|
||||
try:
|
||||
return raw.split(b"\x00", 1)[0].decode("ascii")
|
||||
except UnicodeDecodeError as exc:
|
||||
raise ExportError("NON_ASCII_SIGNAL_NAME") from exc
|
||||
|
||||
|
||||
def read_records(binary_path):
|
||||
try:
|
||||
data = binary_path.read_bytes()
|
||||
except OSError as exc:
|
||||
raise ExportError(f"BINARY_READ_FAILED:{exc}") from exc
|
||||
|
||||
if len(data) < EXPECTED_HEADER_BYTES:
|
||||
raise ExportError(
|
||||
f"HEADER_TOO_SMALL:EXPECTED_{EXPECTED_HEADER_BYTES}:OBSERVED_{len(data)}"
|
||||
)
|
||||
|
||||
signal_count = struct.unpack_from("<I", data, 0)[0]
|
||||
if signal_count != len(EXPECTED_SIGNALS):
|
||||
raise ExportError(
|
||||
f"SIGNAL_COUNT_MISMATCH:EXPECTED_{len(EXPECTED_SIGNALS)}:OBSERVED_{signal_count}"
|
||||
)
|
||||
|
||||
offset = 4
|
||||
observed = []
|
||||
for _ in range(signal_count):
|
||||
type_hex = data[offset : offset + 2].hex()
|
||||
offset += 2
|
||||
name = decode_name(data[offset : offset + 32])
|
||||
offset += 32
|
||||
elements = struct.unpack_from("<I", data, offset)[0]
|
||||
offset += 4
|
||||
observed.append((name, type_hex, elements))
|
||||
|
||||
expected = tuple(
|
||||
(name, UINT32_TYPE_HEX, elements) for name, elements in EXPECTED_SIGNALS
|
||||
)
|
||||
if tuple(observed) != expected:
|
||||
raise ExportError(
|
||||
"HEADER_CONTRACT_MISMATCH:"
|
||||
f"EXPECTED_{expected!r}:OBSERVED_{tuple(observed)!r}"
|
||||
)
|
||||
if offset != EXPECTED_HEADER_BYTES:
|
||||
raise ExportError(
|
||||
f"HEADER_SIZE_MISMATCH:EXPECTED_{EXPECTED_HEADER_BYTES}:OBSERVED_{offset}"
|
||||
)
|
||||
|
||||
payload = data[offset:]
|
||||
remainder = len(payload) % EXPECTED_RECORD_BYTES
|
||||
if remainder != 0:
|
||||
raise ExportError(
|
||||
f"PAYLOAD_ALIGNMENT_ERROR:REMAINDER_{remainder}:RECORD_{EXPECTED_RECORD_BYTES}"
|
||||
)
|
||||
if not payload:
|
||||
raise ExportError("NO_COMPLETE_RECORDS")
|
||||
|
||||
records = [
|
||||
struct.unpack_from("<8I", payload, record_offset)
|
||||
for record_offset in range(0, len(payload), EXPECTED_RECORD_BYTES)
|
||||
]
|
||||
return data, records
|
||||
|
||||
|
||||
def write_csv_atomic(output_path, records):
|
||||
output_path.parent.mkdir(parents=True, exist_ok=True)
|
||||
if output_path.exists() and not output_path.is_file():
|
||||
raise ExportError(f"OUTPUT_NOT_REGULAR_FILE:{output_path}")
|
||||
|
||||
descriptor, temporary_name = tempfile.mkstemp(
|
||||
prefix=f".{output_path.name}.",
|
||||
suffix=".tmp",
|
||||
dir=output_path.parent,
|
||||
text=True,
|
||||
)
|
||||
temporary_path = Path(temporary_name)
|
||||
try:
|
||||
with os.fdopen(descriptor, "w", encoding="utf-8", newline="") as stream:
|
||||
writer = csv.writer(stream, lineterminator="\n")
|
||||
writer.writerow(CSV_COLUMNS)
|
||||
writer.writerows(records)
|
||||
stream.flush()
|
||||
os.fsync(stream.fileno())
|
||||
|
||||
if output_path.exists():
|
||||
if temporary_path.read_bytes() == output_path.read_bytes():
|
||||
temporary_path.unlink()
|
||||
return "ALREADY_VALID"
|
||||
raise ExportError(f"OUTPUT_EXISTS_BUT_DIFFERS:{output_path}")
|
||||
|
||||
os.chmod(temporary_path, 0o644)
|
||||
os.replace(temporary_path, output_path)
|
||||
return "CREATED"
|
||||
finally:
|
||||
if temporary_path.exists():
|
||||
temporary_path.unlink()
|
||||
|
||||
|
||||
def sha256_bytes(data):
|
||||
return hashlib.sha256(data).hexdigest()
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(
|
||||
description=(
|
||||
"Create a human-readable CSV from the S03 FileWriter binary. "
|
||||
"The binary remains the primary evidence."
|
||||
)
|
||||
)
|
||||
parser.add_argument("binary", type=Path)
|
||||
parser.add_argument("csv_output", type=Path, nargs="?")
|
||||
args = parser.parse_args()
|
||||
|
||||
output_path = args.csv_output
|
||||
if output_path is None:
|
||||
output_path = args.binary.with_suffix(".csv")
|
||||
|
||||
try:
|
||||
binary_data, records = read_records(args.binary)
|
||||
publication = write_csv_atomic(output_path, records)
|
||||
csv_data = output_path.read_bytes()
|
||||
except ExportError as exc:
|
||||
print("CSV_EXPORT_STATUS=FAIL")
|
||||
print(f"CSV_EXPORT_ERROR={exc}")
|
||||
return 40
|
||||
except OSError as exc:
|
||||
print("CSV_EXPORT_STATUS=FAIL")
|
||||
print(f"CSV_EXPORT_ERROR=OS_ERROR:{exc}")
|
||||
return 41
|
||||
|
||||
print("PRIMARY_EVIDENCE=s03_output.bin")
|
||||
print("DERIVED_ARTIFACT=s03_output.csv")
|
||||
print(f"BINARY_PATH={args.binary}")
|
||||
print(f"BINARY_SHA256={sha256_bytes(binary_data)}")
|
||||
print(f"CSV_PATH={output_path}")
|
||||
print(f"CSV_SHA256={sha256_bytes(csv_data)}")
|
||||
print(f"CSV_RECORD_COUNT={len(records)}")
|
||||
print(f"CSV_PUBLICATION={publication}")
|
||||
print("CSV_EXPORT_STATUS=PASS")
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
+414
@@ -0,0 +1,414 @@
|
||||
#!/usr/bin/env bash
|
||||
set -u
|
||||
|
||||
MINIMUM_SAMPLES="50"
|
||||
EXPECTED_BASELINE_PARSER_SHA256="91c4d66770197e1986ed91eb6a4870e7da7df5a522ffdf4329b6d9250f238307"
|
||||
EXPECTED_TEST_CONFIG_SHA256="d037561b32760fbd0e2aec9f1eefc5efda27b1918eca4742c89092a8a2f7e92e"
|
||||
EXPECTED_DIAGNOSTIC_CONFIG_SHA256="cd999ff249452298d650a22c8fe5b0b03bf48c0735f4630befdec8209cbb13a3"
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
PACKAGE_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
|
||||
PARSER="${S03_BASELINE_PARSER:-$PACKAGE_ROOT/baseline/parse_s03_binary.py}"
|
||||
CONFIG_VERIFIER="$SCRIPT_DIR/verify_s03h1_config.py"
|
||||
|
||||
MODE="NOT_PROVIDED"
|
||||
MODE_STATUS="NOT_DEMONSTRATED"
|
||||
PROFILE_CONFIG_STATUS="NOT_DEMONSTRATED"
|
||||
OBSERVABILITY_STATUS="NOT_DEMONSTRATED"
|
||||
FUNCTIONAL_STATUS="NOT_DEMONSTRATED"
|
||||
SEMANTIC_STATUS="NOT_DEMONSTRATED"
|
||||
TIMING_STATUS="NOT_EVALUATED"
|
||||
MEASUREMENT_ELIGIBILITY="NO"
|
||||
|
||||
emit_contract() {
|
||||
printf "MODE=%s\n" "$MODE"
|
||||
printf "MODE_STATUS=%s\n" "$MODE_STATUS"
|
||||
printf "PROFILE_CONFIG_STATUS=%s\n" "$PROFILE_CONFIG_STATUS"
|
||||
printf "OBSERVABILITY_STATUS=%s\n" "$OBSERVABILITY_STATUS"
|
||||
printf "FUNCTIONAL_STATUS=%s\n" "$FUNCTIONAL_STATUS"
|
||||
printf "SEMANTIC_STATUS=%s\n" "$SEMANTIC_STATUS"
|
||||
printf "TIMING_STATUS=%s\n" "$TIMING_STATUS"
|
||||
printf "MEASUREMENT_ELIGIBILITY=%s\n" "$MEASUREMENT_ELIGIBILITY"
|
||||
}
|
||||
|
||||
fail_early() {
|
||||
code="$1"
|
||||
shift
|
||||
printf "[NON DIMOSTRATO] %s\n" "$*" >&2
|
||||
emit_contract
|
||||
printf "VALIDATION_RESULT=FAIL_REQUIRED_INPUT\n"
|
||||
exit "$code"
|
||||
}
|
||||
|
||||
if [ "$#" -ne 2 ]; then
|
||||
printf "Uso: %s TEST|DIAGNOSTIC <directory-run|run.log>\n" "$0" >&2
|
||||
emit_contract
|
||||
exit 2
|
||||
fi
|
||||
|
||||
MODE="$1"
|
||||
case "$MODE" in
|
||||
TEST)
|
||||
EXPECTED_PROFILE_CONFIG_SHA256="$EXPECTED_TEST_CONFIG_SHA256"
|
||||
;;
|
||||
DIAGNOSTIC)
|
||||
EXPECTED_PROFILE_CONFIG_SHA256="$EXPECTED_DIAGNOSTIC_CONFIG_SHA256"
|
||||
;;
|
||||
*)
|
||||
MODE_STATUS="FAIL"
|
||||
PROFILE_CONFIG_STATUS="FAIL"
|
||||
OBSERVABILITY_STATUS="FAIL"
|
||||
printf "UNKNOWN_MODE=%s\n" "$MODE" >&2
|
||||
emit_contract
|
||||
printf "VALIDATION_RESULT=FAIL_UNKNOWN_MODE\n"
|
||||
exit 2
|
||||
;;
|
||||
esac
|
||||
|
||||
INPUT_PATH="$2"
|
||||
if [ -d "$INPUT_PATH" ]; then
|
||||
RUN_DIR="$(cd "$INPUT_PATH" && pwd)"
|
||||
RUN_LOG="$RUN_DIR/run.log"
|
||||
elif [ -f "$INPUT_PATH" ]; then
|
||||
RUN_LOG="$(cd "$(dirname "$INPUT_PATH")" && pwd)/$(basename "$INPUT_PATH")"
|
||||
RUN_DIR="$(dirname "$RUN_LOG")"
|
||||
else
|
||||
fail_early 21 "Percorso di validazione non trovato: $INPUT_PATH"
|
||||
fi
|
||||
|
||||
RUN_CONFIG="$RUN_DIR/configuration.marte"
|
||||
OUTPUT_FILE="$RUN_DIR/s03_output.bin"
|
||||
RUNNER_SUMMARY="$RUN_DIR/runner_summary.txt"
|
||||
RUN_HASHES="$RUN_DIR/hashes.sha256"
|
||||
MODE_FILE="$RUN_DIR/s03h1_mode.txt"
|
||||
|
||||
if [ ! -f "$PARSER" ]; then
|
||||
fail_early 21 "Parser baseline S03 assente: $PARSER"
|
||||
fi
|
||||
PARSER_SHA256="$(sha256sum -- "$PARSER" | awk '{print $1}')"
|
||||
if [ "$PARSER_SHA256" != "$EXPECTED_BASELINE_PARSER_SHA256" ]; then
|
||||
fail_early 21 "Parser baseline S03 con hash inatteso: $PARSER_SHA256"
|
||||
fi
|
||||
if [ ! -x "$CONFIG_VERIFIER" ]; then
|
||||
fail_early 21 "Verificatore configurazione assente o non eseguibile: $CONFIG_VERIFIER"
|
||||
fi
|
||||
|
||||
for required_file in \
|
||||
"$RUN_LOG" \
|
||||
"$RUN_CONFIG" \
|
||||
"$OUTPUT_FILE" \
|
||||
"$RUNNER_SUMMARY" \
|
||||
"$RUN_HASHES" \
|
||||
"$MODE_FILE"
|
||||
do
|
||||
if [ ! -f "$required_file" ]; then
|
||||
fail_early 21 "File richiesto assente: $required_file"
|
||||
fi
|
||||
done
|
||||
|
||||
if [ ! -s "$RUN_HASHES" ]; then
|
||||
fail_early 21 "Manifest hash assente o vuoto: $RUN_HASHES"
|
||||
fi
|
||||
|
||||
count_lines() {
|
||||
pattern="$1"
|
||||
file="$2"
|
||||
grep -Ec "$pattern" "$file" || true
|
||||
}
|
||||
|
||||
report_value() {
|
||||
key="$1"
|
||||
file="$2"
|
||||
sed -n "s/^${key}=//p" "$file" | tail -n 1
|
||||
}
|
||||
|
||||
MODE_DECLARATION_COUNT="$(count_lines "^MODE=${MODE}$" "$MODE_FILE")"
|
||||
SCENARIO_DECLARATION_COUNT="$(count_lines '^SCENARIO=S03H1$' "$MODE_FILE")"
|
||||
CONFIG_HASH_DECLARATION_COUNT="$(
|
||||
count_lines "^CANDIDATE_CONFIG_SHA256=${EXPECTED_PROFILE_CONFIG_SHA256}$" "$MODE_FILE"
|
||||
)"
|
||||
INSTALLATION_GATE_DECLARATION_COUNT="$(
|
||||
count_lines '^S03_H1_INSTALLATION_AUTHORIZED=NO$' "$MODE_FILE"
|
||||
)"
|
||||
if [ "$MODE_DECLARATION_COUNT" -eq 1 ] &&
|
||||
[ "$SCENARIO_DECLARATION_COUNT" -eq 1 ] &&
|
||||
[ "$CONFIG_HASH_DECLARATION_COUNT" -eq 1 ] &&
|
||||
[ "$INSTALLATION_GATE_DECLARATION_COUNT" -eq 1 ]; then
|
||||
MODE_STATUS="PASS"
|
||||
else
|
||||
MODE_STATUS="FAIL"
|
||||
fi
|
||||
|
||||
START_LINES="$(count_lines '^\[Information - RealTimeLoader\.cpp:[0-9]+\]: Started application in state State1[[:space:]]*$' "$RUN_LOG")"
|
||||
APPLICATION_START_LINES="$(count_lines '^\[Information - MARTeApp\.cpp:[0-9]+\]: Application starting[[:space:]]*$' "$RUN_LOG")"
|
||||
FILE_OPEN_LINES="$(count_lines '^\[Information - FileWriter\.cpp:[0-9]+\]: Going to open file with name ' "$RUN_LOG")"
|
||||
SIGINT_LINES="$(count_lines '^\[Information - Bootstrap\.cpp:[0-9]+\]: Application recieved SIGINT\.[[:space:]]*$' "$RUN_LOG")"
|
||||
STOP_OK_LINES="$(count_lines '^\[Information - Bootstrap\.cpp:[0-9]+\]: Application successfully stopped\.[[:space:]]*$' "$RUN_LOG")"
|
||||
TERMINATED_LINES="$(count_lines '^\[NoError - MARTeApp\.cpp:[0-9]+\]: Application terminated[[:space:]]*$' "$RUN_LOG")"
|
||||
|
||||
STARTUP_ERROR_LINES="$(count_lines 'Failed dlopen|Failed CreateByName|Failed to Initialise object|Failed to initialise the ObjectRegistryDatabase|Could not Initialise the loader' "$RUN_LOG")"
|
||||
FATAL_ERROR_LINES="$(count_lines '^\[FatalError ' "$RUN_LOG")"
|
||||
OS_ERROR_LINES="$(count_lines '^\[OSError ' "$RUN_LOG")"
|
||||
ERROR_LINES="$(count_lines '^\[Error ' "$RUN_LOG")"
|
||||
INITIALISATION_ERROR_LINES="$(count_lines '^\[InitialisationEr ' "$RUN_LOG")"
|
||||
WARNING_LINES="$(count_lines '^\[Warning ' "$RUN_LOG")"
|
||||
|
||||
PRIORITY_CLIP_LINES="$(count_lines '^\[Warning - Threads\.cpp:[0-9]+\]: Requested a thread priority that is higher than ' "$RUN_LOG")"
|
||||
PRIORITY_FAIL_LINES="$(count_lines '^\[Warning - Threads\.cpp:[0-9]+\]: Failed to change the thread priority' "$RUN_LOG")"
|
||||
LINUX_TIMER_DEFAULT_WARNING_LINES="$(count_lines '^\[Warning - LinuxTimer\.cpp:[0-9]+\]: (ExecutionMode|CPUMask|StackSize) not specified using: ' "$RUN_LOG")"
|
||||
CONFIG_DB_NODE_WARNING_LINES="$(count_lines '^\[Warning - MARTeApp\.cpp:[0-9]+\]: \[ConfigurationDatabaseNode\] - instances: [0-9]+[[:space:]]*$' "$RUN_LOG")"
|
||||
|
||||
UNCLASSIFIED_WARNING_LINES=$((
|
||||
WARNING_LINES -
|
||||
PRIORITY_CLIP_LINES -
|
||||
PRIORITY_FAIL_LINES -
|
||||
LINUX_TIMER_DEFAULT_WARNING_LINES -
|
||||
CONFIG_DB_NODE_WARNING_LINES
|
||||
))
|
||||
if [ "$UNCLASSIFIED_WARNING_LINES" -lt 0 ]; then
|
||||
UNCLASSIFIED_WARNING_LINES=0
|
||||
fi
|
||||
|
||||
DOUBLE_STOP_LINES="$(count_lines '^\[FatalError - RealTimeApplication\.cpp:[0-9]+\]: Could not stop the RealTimeApplication\. Was it ever started\?[[:space:]]*$' "$RUN_LOG")"
|
||||
ASYNC_EVENTSEM_CLOSE_LINES="$(count_lines '^\[FatalError - MemoryMapAsyncOutputBroker\.cpp:[0-9]+\]: Could not Close the EventSem\.[[:space:]]*$' "$RUN_LOG")"
|
||||
PTHREAD_MUTEX_DESTROY_LINES="$(count_lines '^\[OSError - EventSem\.cpp:[0-9]+\]: Error: pthread_mutex_destroy\(\)[[:space:]]*$' "$RUN_LOG")"
|
||||
|
||||
UNCLASSIFIED_FATAL_LINES=$((FATAL_ERROR_LINES - DOUBLE_STOP_LINES - ASYNC_EVENTSEM_CLOSE_LINES))
|
||||
UNCLASSIFIED_OS_ERROR_LINES=$((OS_ERROR_LINES - PTHREAD_MUTEX_DESTROY_LINES))
|
||||
if [ "$UNCLASSIFIED_FATAL_LINES" -lt 0 ]; then
|
||||
UNCLASSIFIED_FATAL_LINES=0
|
||||
fi
|
||||
if [ "$UNCLASSIFIED_OS_ERROR_LINES" -lt 0 ]; then
|
||||
UNCLASSIFIED_OS_ERROR_LINES=0
|
||||
fi
|
||||
|
||||
LOGGER_SIGNAL_LINES="$(count_lines '^\[Information - LoggerBroker\.cpp:[0-9]+\]: (Counter|Time|State1_Thread1_CycleTime|SentinelScalarA|SentinelScalarB|SentinelVector) \[[0-9]+:[0-9]+\]:' "$RUN_LOG")"
|
||||
LOGGER_COUNTER_LINES="$(count_lines '^\[Information - LoggerBroker\.cpp:[0-9]+\]: Counter \[0:0\]:' "$RUN_LOG")"
|
||||
LOGGER_TIME_LINES="$(count_lines '^\[Information - LoggerBroker\.cpp:[0-9]+\]: Time \[0:0\]:' "$RUN_LOG")"
|
||||
LOGGER_CYCLE_TIME_LINES="$(count_lines '^\[Information - LoggerBroker\.cpp:[0-9]+\]: State1_Thread1_CycleTime \[0:0\]:' "$RUN_LOG")"
|
||||
LOGGER_SCALAR_A_LINES="$(count_lines '^\[Information - LoggerBroker\.cpp:[0-9]+\]: SentinelScalarA \[0:0\]:' "$RUN_LOG")"
|
||||
LOGGER_SCALAR_B_LINES="$(count_lines '^\[Information - LoggerBroker\.cpp:[0-9]+\]: SentinelScalarB \[0:0\]:' "$RUN_LOG")"
|
||||
LOGGER_VECTOR_LINES="$(count_lines '^\[Information - LoggerBroker\.cpp:[0-9]+\]: SentinelVector \[0:2\]:' "$RUN_LOG")"
|
||||
|
||||
case "$MODE" in
|
||||
TEST)
|
||||
if [ "$LOGGER_SIGNAL_LINES" -eq 0 ] &&
|
||||
[ "$LOGGER_COUNTER_LINES" -eq 0 ] &&
|
||||
[ "$LOGGER_TIME_LINES" -eq 0 ] &&
|
||||
[ "$LOGGER_CYCLE_TIME_LINES" -eq 0 ] &&
|
||||
[ "$LOGGER_SCALAR_A_LINES" -eq 0 ] &&
|
||||
[ "$LOGGER_SCALAR_B_LINES" -eq 0 ] &&
|
||||
[ "$LOGGER_VECTOR_LINES" -eq 0 ]; then
|
||||
OBSERVABILITY_STATUS="PASS"
|
||||
else
|
||||
OBSERVABILITY_STATUS="FAIL"
|
||||
fi
|
||||
;;
|
||||
DIAGNOSTIC)
|
||||
if [ "$LOGGER_COUNTER_LINES" -ge 1 ] &&
|
||||
[ "$LOGGER_TIME_LINES" -ge 1 ] &&
|
||||
[ "$LOGGER_CYCLE_TIME_LINES" -ge 1 ] &&
|
||||
[ "$LOGGER_SCALAR_A_LINES" -ge 1 ] &&
|
||||
[ "$LOGGER_SCALAR_B_LINES" -ge 1 ] &&
|
||||
[ "$LOGGER_VECTOR_LINES" -ge 1 ]; then
|
||||
OBSERVABILITY_STATUS="PASS"
|
||||
else
|
||||
OBSERVABILITY_STATUS="FAIL"
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
|
||||
CONFIG_REPORT="$(mktemp "${TMPDIR:-/tmp}/s03h1_validator_config_XXXXXX.log")" || exit 22
|
||||
BINARY_REPORT="$(mktemp "${TMPDIR:-/tmp}/s03h1_validator_binary_XXXXXX.log")" || exit 22
|
||||
HASH_REPORT="$(mktemp "${TMPDIR:-/tmp}/s03h1_validator_hashes_XXXXXX.log")" || exit 22
|
||||
trap 'rm -f -- "$CONFIG_REPORT" "$BINARY_REPORT" "$HASH_REPORT"' EXIT HUP INT TERM
|
||||
|
||||
EXPECTED_FILENAME="$RUN_DIR/s03_output.bin"
|
||||
PYTHONHASHSEED=0 "$CONFIG_VERIFIER" \
|
||||
"$MODE" \
|
||||
"$RUN_CONFIG" \
|
||||
"$EXPECTED_FILENAME" \
|
||||
>"$CONFIG_REPORT" 2>&1
|
||||
CONFIG_RC=$?
|
||||
PROFILE_CONFIG_STATUS="$(
|
||||
report_value CONFIG_PROFILE_VERIFICATION_STATUS "$CONFIG_REPORT"
|
||||
)"
|
||||
if [ -z "$PROFILE_CONFIG_STATUS" ]; then
|
||||
PROFILE_CONFIG_STATUS="FAIL"
|
||||
fi
|
||||
|
||||
PYTHONHASHSEED=0 python3 "$PARSER" "$OUTPUT_FILE" --min-samples "$MINIMUM_SAMPLES" \
|
||||
>"$BINARY_REPORT" 2>&1
|
||||
PARSER_RC=$?
|
||||
|
||||
sha256sum -c --strict "$RUN_HASHES" >"$HASH_REPORT" 2>&1
|
||||
HASH_RC=$?
|
||||
|
||||
FUNCTIONAL_BINARY_STATUS="$(report_value FUNCTIONAL_BINARY_STATUS "$BINARY_REPORT")"
|
||||
SEMANTIC_BINARY_STATUS="$(report_value SEMANTIC_BINARY_STATUS "$BINARY_REPORT")"
|
||||
BINARY_STATUS="$(report_value BINARY_STATUS "$BINARY_REPORT")"
|
||||
FIRST_ERROR_CODE="$(report_value FIRST_ERROR_CODE "$BINARY_REPORT")"
|
||||
PROFILE_ERROR_CODE="$(report_value PROFILE_ERROR_CODE "$CONFIG_REPORT")"
|
||||
COMPLETE_SAMPLE_COUNT="$(report_value COMPLETE_SAMPLE_COUNT "$BINARY_REPORT")"
|
||||
HEADER_BYTES="$(report_value OBSERVED_HEADER_BYTES "$BINARY_REPORT")"
|
||||
RECORD_BYTES="$(report_value OBSERVED_RECORD_BYTES_FROM_HEADER "$BINARY_REPORT")"
|
||||
PAYLOAD_BYTES="$(report_value PAYLOAD_BYTES "$BINARY_REPORT")"
|
||||
PAYLOAD_REMAINDER_BYTES="$(report_value PAYLOAD_REMAINDER_BYTES "$BINARY_REPORT")"
|
||||
|
||||
MARTE_EXIT_CODE="$(sed -n 's/^Exit code MARTe:[[:space:]]*//p' "$RUNNER_SUMMARY" | tail -n 1)"
|
||||
TEE_EXIT_CODE="$(sed -n 's/^Exit code tee:[[:space:]]*//p' "$RUNNER_SUMMARY" | tail -n 1)"
|
||||
if [ -z "$MARTE_EXIT_CODE" ]; then
|
||||
MARTE_EXIT_CODE="NOT_AVAILABLE"
|
||||
fi
|
||||
if [ -z "$TEE_EXIT_CODE" ]; then
|
||||
TEE_EXIT_CODE="NOT_AVAILABLE"
|
||||
fi
|
||||
|
||||
case "$MARTE_EXIT_CODE" in
|
||||
137)
|
||||
TIMING_STATUS="NOT_VALID_EXIT_137_SIGKILL"
|
||||
;;
|
||||
*)
|
||||
if [ "$PRIORITY_FAIL_LINES" -ge 1 ]; then
|
||||
TIMING_STATUS="NOT_VALID_PRIORITY_NOT_APPLIED"
|
||||
else
|
||||
TIMING_STATUS="UNASSESSED_DT_SEMANTICS"
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
|
||||
FUNCTIONAL_OK=0
|
||||
if [ "$START_LINES" -ge 1 ] &&
|
||||
[ "$APPLICATION_START_LINES" -ge 1 ] &&
|
||||
[ "$FILE_OPEN_LINES" -ge 1 ] &&
|
||||
[ "$STARTUP_ERROR_LINES" -eq 0 ] &&
|
||||
[ "$MODE_STATUS" = "PASS" ] &&
|
||||
[ "$CONFIG_RC" -eq 0 ] &&
|
||||
[ "$PROFILE_CONFIG_STATUS" = "PASS" ] &&
|
||||
[ "$OBSERVABILITY_STATUS" = "PASS" ] &&
|
||||
[ "$FUNCTIONAL_BINARY_STATUS" = "PASS" ] &&
|
||||
[ "$HASH_RC" -eq 0 ] &&
|
||||
[ "$TEE_EXIT_CODE" = "0" ] &&
|
||||
[ "$UNCLASSIFIED_FATAL_LINES" -eq 0 ] &&
|
||||
[ "$UNCLASSIFIED_OS_ERROR_LINES" -eq 0 ] &&
|
||||
[ "$ERROR_LINES" -eq 0 ] &&
|
||||
[ "$INITIALISATION_ERROR_LINES" -eq 0 ]; then
|
||||
FUNCTIONAL_OK=1
|
||||
fi
|
||||
|
||||
if [ "$FUNCTIONAL_OK" -eq 1 ]; then
|
||||
FUNCTIONAL_STATUS="PASS"
|
||||
fi
|
||||
|
||||
if [ "$MODE_STATUS" = "PASS" ] &&
|
||||
[ "$PROFILE_CONFIG_STATUS" = "PASS" ] &&
|
||||
[ "$SEMANTIC_BINARY_STATUS" = "PASS" ]; then
|
||||
SEMANTIC_STATUS="PASS"
|
||||
elif [ "$SEMANTIC_BINARY_STATUS" = "FAIL" ]; then
|
||||
SEMANTIC_STATUS="FAIL"
|
||||
else
|
||||
SEMANTIC_STATUS="NOT_DEMONSTRATED"
|
||||
fi
|
||||
|
||||
printf "============================================================\n"
|
||||
printf "VALIDAZIONE S03-H1 - EXECUTION PROFILES\n"
|
||||
printf "============================================================\n"
|
||||
printf "Mode: %s\n" "$MODE"
|
||||
printf "Run directory: %s\n" "$RUN_DIR"
|
||||
printf "Configurazione: %s\n" "$RUN_CONFIG"
|
||||
printf "File binario: %s\n" "$OUTPUT_FILE"
|
||||
printf "Manifest hash: %s\n" "$RUN_HASHES"
|
||||
printf "Parser baseline SHA-256: %s\n" "$PARSER_SHA256"
|
||||
printf "Mode file: %s\n" "$MODE_FILE"
|
||||
printf "Mode declaration count: %s\n" "$MODE_DECLARATION_COUNT"
|
||||
printf "Scenario declaration count: %s\n" "$SCENARIO_DECLARATION_COUNT"
|
||||
printf "Config hash declaration count: %s\n" "$CONFIG_HASH_DECLARATION_COUNT"
|
||||
printf "Installation gate count: %s\n" "$INSTALLATION_GATE_DECLARATION_COUNT"
|
||||
printf "Configurazione profilo: %s (rc=%s)\n" "$PROFILE_CONFIG_STATUS" "$CONFIG_RC"
|
||||
printf "Errore profilo: %s\n" "${PROFILE_ERROR_CODE:-NOT_AVAILABLE}"
|
||||
printf "Osservabilita' profilo: %s\n" "$OBSERVABILITY_STATUS"
|
||||
printf "Logger signal lines: %s\n" "$LOGGER_SIGNAL_LINES"
|
||||
printf "Logger Counter lines: %s\n" "$LOGGER_COUNTER_LINES"
|
||||
printf "Logger Time lines: %s\n" "$LOGGER_TIME_LINES"
|
||||
printf "Logger CycleTime lines: %s\n" "$LOGGER_CYCLE_TIME_LINES"
|
||||
printf "Logger ScalarA lines: %s\n" "$LOGGER_SCALAR_A_LINES"
|
||||
printf "Logger ScalarB lines: %s\n" "$LOGGER_SCALAR_B_LINES"
|
||||
printf "Logger Vector[3] lines: %s\n" "$LOGGER_VECTOR_LINES"
|
||||
printf "Verifica manifest hash: %s (rc=%s)\n" "$( [ "$HASH_RC" -eq 0 ] && printf PASS || printf FAIL )" "$HASH_RC"
|
||||
printf "Parser binario: %s (rc=%s)\n" "${BINARY_STATUS:-NOT_AVAILABLE}" "$PARSER_RC"
|
||||
printf "Primo errore parser: %s\n" "${FIRST_ERROR_CODE:-NOT_AVAILABLE}"
|
||||
printf "Header osservato: %s byte\n" "${HEADER_BYTES:-NOT_AVAILABLE}"
|
||||
printf "Record da header: %s byte\n" "${RECORD_BYTES:-NOT_AVAILABLE}"
|
||||
printf "Payload: %s byte\n" "${PAYLOAD_BYTES:-NOT_AVAILABLE}"
|
||||
printf "Residuo payload: %s byte\n" "${PAYLOAD_REMAINDER_BYTES:-NOT_AVAILABLE}"
|
||||
printf "Campioni completi: %s\n" "${COMPLETE_SAMPLE_COUNT:-NOT_AVAILABLE}"
|
||||
printf "Avvii State1: %s\n" "$START_LINES"
|
||||
printf "Application starting: %s\n" "$APPLICATION_START_LINES"
|
||||
printf "Aperture FileWriter: %s\n" "$FILE_OPEN_LINES"
|
||||
printf "Exit code MARTe: %s\n" "$MARTE_EXIT_CODE"
|
||||
printf "Exit code tee: %s\n" "$TEE_EXIT_CODE"
|
||||
printf "Warning priorita' non applic.: %s\n" "$PRIORITY_FAIL_LINES"
|
||||
printf "Warning non classificati: %s\n" "$UNCLASSIFIED_WARNING_LINES"
|
||||
printf "FatalError non classificati: %s\n" "$UNCLASSIFIED_FATAL_LINES"
|
||||
printf "OSError non classificati: %s\n" "$UNCLASSIFIED_OS_ERROR_LINES"
|
||||
printf "Error non classificati: %s\n" "$ERROR_LINES"
|
||||
printf "InitialisationError: %s\n" "$INITIALISATION_ERROR_LINES"
|
||||
printf "UNCLASSIFIED_WARNING_LINES=%s\n" "$UNCLASSIFIED_WARNING_LINES"
|
||||
printf "LOGGER_SIGNAL_LINES=%s\n" "$LOGGER_SIGNAL_LINES"
|
||||
printf "LOGGER_COUNTER_LINES=%s\n" "$LOGGER_COUNTER_LINES"
|
||||
printf "LOGGER_TIME_LINES=%s\n" "$LOGGER_TIME_LINES"
|
||||
printf "LOGGER_CYCLE_TIME_LINES=%s\n" "$LOGGER_CYCLE_TIME_LINES"
|
||||
printf "LOGGER_SCALAR_A_LINES=%s\n" "$LOGGER_SCALAR_A_LINES"
|
||||
printf "LOGGER_SCALAR_B_LINES=%s\n" "$LOGGER_SCALAR_B_LINES"
|
||||
printf "LOGGER_VECTOR_LINES=%s\n" "$LOGGER_VECTOR_LINES"
|
||||
|
||||
printf "\n--- DETTAGLIO CONFIGURAZIONE PROFILE-AWARE ---\n"
|
||||
cat -- "$CONFIG_REPORT"
|
||||
printf "\n--- DETTAGLIO PARSER BINARIO BASELINE ---\n"
|
||||
cat -- "$BINARY_REPORT"
|
||||
printf "\n--- VERIFICA HASH ---\n"
|
||||
cat -- "$HASH_REPORT"
|
||||
|
||||
printf "\n--- CONTRATTO S03-H1 ---\n"
|
||||
emit_contract
|
||||
|
||||
if [ "$FUNCTIONAL_STATUS" != "PASS" ]; then
|
||||
printf "VALIDATION_RESULT=FUNCTIONAL_NOT_DEMONSTRATED\n"
|
||||
exit 20
|
||||
fi
|
||||
|
||||
if [ "$SEMANTIC_STATUS" != "PASS" ]; then
|
||||
printf "VALIDATION_RESULT=SEMANTIC_VALIDATION_FAILED\n"
|
||||
exit 20
|
||||
fi
|
||||
|
||||
if [ "$SIGINT_LINES" -ge 1 ] &&
|
||||
[ "$STOP_OK_LINES" -ge 1 ] &&
|
||||
[ "$TERMINATED_LINES" -ge 1 ] &&
|
||||
[ "$FATAL_ERROR_LINES" -eq 0 ] &&
|
||||
[ "$OS_ERROR_LINES" -eq 0 ] &&
|
||||
[ "$ERROR_LINES" -eq 0 ] &&
|
||||
[ "$INITIALISATION_ERROR_LINES" -eq 0 ]; then
|
||||
printf "VALIDATION_RESULT=PASS_CLEAN_SHUTDOWN\n"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
KNOWN_SHUTDOWN_TOTAL=$((
|
||||
DOUBLE_STOP_LINES +
|
||||
ASYNC_EVENTSEM_CLOSE_LINES +
|
||||
PTHREAD_MUTEX_DESTROY_LINES
|
||||
))
|
||||
if [ "$SIGINT_LINES" -ge 1 ] &&
|
||||
[ "$STOP_OK_LINES" -ge 1 ] &&
|
||||
[ "$TERMINATED_LINES" -ge 1 ] &&
|
||||
[ "$KNOWN_SHUTDOWN_TOTAL" -ge 1 ] &&
|
||||
[ "$UNCLASSIFIED_FATAL_LINES" -eq 0 ] &&
|
||||
[ "$UNCLASSIFIED_OS_ERROR_LINES" -eq 0 ] &&
|
||||
[ "$ERROR_LINES" -eq 0 ] &&
|
||||
[ "$INITIALISATION_ERROR_LINES" -eq 0 ]; then
|
||||
printf "VALIDATION_RESULT=PASS_KNOWN_SHUTDOWN_ANOMALY\n"
|
||||
exit 10
|
||||
fi
|
||||
|
||||
printf "VALIDATION_RESULT=PASS_UNCLASSIFIED_SHUTDOWN_ANOMALY\n"
|
||||
exit 11
|
||||
+267
@@ -0,0 +1,267 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Fail-closed, profile-aware verifier for S03-H1 runtime configurations."""
|
||||
|
||||
import argparse
|
||||
import hashlib
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
EXPECTED_PROFILE_HASHES = {
|
||||
"TEST": "d037561b32760fbd0e2aec9f1eefc5efda27b1918eca4742c89092a8a2f7e92e",
|
||||
"DIAGNOSTIC": "cd999ff249452298d650a22c8fe5b0b03bf48c0735f4630befdec8209cbb13a3",
|
||||
}
|
||||
EXPECTED_DISPLAY_CLASSES = {
|
||||
"TEST": "GAMDataSource",
|
||||
"DIAGNOSTIC": "LoggerDataSource",
|
||||
}
|
||||
EXPECTED_GAMDISPLAY_BLOCK_SHA256 = (
|
||||
"13b14b504dac43df5febf230cd069c5093d951726382b1d5dee8edf43d6814d0"
|
||||
)
|
||||
EXPECTED_IOGAM_WRITER_BLOCK_SHA256 = (
|
||||
"b3d54ad2fae08e6640bfa0be39ead9841906b4573a3b407e5ecf21340b5c5717"
|
||||
)
|
||||
EXPECTED_FILEWRITER_BLOCK_SHA256 = (
|
||||
"0bb949b9d72fa758bdfd8b2039336bc1fda071ecf038304402032de63f4fc455"
|
||||
)
|
||||
PLACEHOLDER_LINE = ' Filename = "__MARTE_RUN_DIR__/s03_output.bin"'
|
||||
|
||||
|
||||
def digest(text):
|
||||
return hashlib.sha256(text.encode("utf-8")).hexdigest()
|
||||
|
||||
|
||||
def extract_block(text, start_marker, end_marker):
|
||||
if text.count(start_marker) != 1:
|
||||
raise ValueError(f"START_MARKER_COUNT:{start_marker!r}:{text.count(start_marker)}")
|
||||
start = text.index(start_marker)
|
||||
try:
|
||||
end = text.index(end_marker, start)
|
||||
except ValueError as exc:
|
||||
raise ValueError(f"END_MARKER_MISSING:{end_marker!r}") from exc
|
||||
return text[start:end]
|
||||
|
||||
|
||||
class Verification:
|
||||
def __init__(self):
|
||||
self.checks = {}
|
||||
self.first_error = "NONE"
|
||||
|
||||
def check(self, name, condition, error_code):
|
||||
self.checks[name] = "PASS" if condition else "FAIL"
|
||||
if not condition and self.first_error == "NONE":
|
||||
self.first_error = error_code
|
||||
|
||||
def emit(self):
|
||||
for name in sorted(self.checks):
|
||||
print(f"{name}={self.checks[name]}")
|
||||
print(f"PROFILE_ERROR_CODE={self.first_error}")
|
||||
|
||||
|
||||
def verify(mode, config_path, expected_filename):
|
||||
report = Verification()
|
||||
try:
|
||||
text = config_path.read_text(encoding="utf-8")
|
||||
except OSError as exc:
|
||||
print("CONFIG_READ_STATUS=FAIL")
|
||||
print(f"CONFIG_READ_ERROR={exc}")
|
||||
print("PROFILE_ERROR_CODE=CONFIG_READ_FAILED")
|
||||
print("CONFIG_PROFILE_VERIFICATION_STATUS=FAIL")
|
||||
return 1
|
||||
|
||||
expected_line = f' Filename = "{expected_filename}"'
|
||||
filename_count = sum(1 for line in text.splitlines() if line == expected_line)
|
||||
placeholder_count = text.count("__MARTE_RUN_DIR__")
|
||||
|
||||
canonical = text
|
||||
if filename_count == 1:
|
||||
canonical = text.replace(expected_line, PLACEHOLDER_LINE, 1)
|
||||
|
||||
canonical_hash = digest(canonical)
|
||||
expected_profile_hash = EXPECTED_PROFILE_HASHES[mode]
|
||||
expected_display_class = EXPECTED_DISPLAY_CLASSES[mode]
|
||||
|
||||
print(f"VERIFIED_MODE={mode}")
|
||||
print(f"EXPECTED_FILENAME={expected_filename}")
|
||||
print(f"EXPECTED_FILENAME_COUNT={filename_count}")
|
||||
print(f"PLACEHOLDER_COUNT={placeholder_count}")
|
||||
print(f"CANONICAL_CONFIG_SHA256={canonical_hash}")
|
||||
print(f"EXPECTED_PROFILE_CONFIG_SHA256={expected_profile_hash}")
|
||||
|
||||
report.check(
|
||||
"RUNTIME_FILENAME_STATUS",
|
||||
filename_count == 1 and placeholder_count == 0,
|
||||
"RUNTIME_FILENAME_MISMATCH",
|
||||
)
|
||||
report.check(
|
||||
"GAMDISPLAY_OBJECT_STATUS",
|
||||
text.count("+GAMDisplay =") == 1,
|
||||
"GAMDISPLAY_OBJECT_COUNT_MISMATCH",
|
||||
)
|
||||
report.check(
|
||||
"THREAD_ORDER_STATUS",
|
||||
text.count(
|
||||
"Functions = {GAMTimer SentinelProducer GAMDisplay IOGAM_Writer}"
|
||||
)
|
||||
== 1,
|
||||
"THREAD_ORDER_MISMATCH",
|
||||
)
|
||||
|
||||
try:
|
||||
gamdisplay = extract_block(
|
||||
canonical,
|
||||
" +GAMDisplay = {\n",
|
||||
" +IOGAM_Writer = {\n",
|
||||
)
|
||||
except ValueError as exc:
|
||||
print(f"GAMDISPLAY_EXTRACTION_ERROR={exc}")
|
||||
gamdisplay = ""
|
||||
|
||||
display_checks = {
|
||||
"GAMDISPLAY_CLASS_COUNT": gamdisplay.count("Class = IOGAM") == 1,
|
||||
"GAMDISPLAY_COUNTER_COUNT": gamdisplay.count(
|
||||
" Counter = {\n"
|
||||
)
|
||||
== 2,
|
||||
"GAMDISPLAY_TIME_COUNT": gamdisplay.count(" Time = {\n") == 2,
|
||||
"GAMDISPLAY_CYCLETIME_COUNT": gamdisplay.count(
|
||||
"State1_Thread1_CycleTime ="
|
||||
)
|
||||
== 2,
|
||||
"GAMDISPLAY_SCALAR_A_COUNT": gamdisplay.count("SentinelScalarA =") == 2,
|
||||
"GAMDISPLAY_SCALAR_B_COUNT": gamdisplay.count("SentinelScalarB =") == 2,
|
||||
"GAMDISPLAY_VECTOR_COUNT": gamdisplay.count("SentinelVector =") == 2,
|
||||
"GAMDISPLAY_VECTOR_DIMENSION_COUNT": gamdisplay.count(
|
||||
"NumberOfDimensions = 1"
|
||||
)
|
||||
== 2,
|
||||
"GAMDISPLAY_VECTOR_ELEMENTS_COUNT": gamdisplay.count(
|
||||
"NumberOfElements = 3"
|
||||
)
|
||||
== 2,
|
||||
"GAMDISPLAY_OUTPUT_DATASOURCE_COUNT": gamdisplay.count(
|
||||
"DataSource = Display"
|
||||
)
|
||||
== 6,
|
||||
}
|
||||
for name, passed in display_checks.items():
|
||||
report.check(name, passed, f"{name}_MISMATCH")
|
||||
|
||||
gamdisplay_hash = digest(gamdisplay)
|
||||
print(f"GAMDISPLAY_BLOCK_SHA256={gamdisplay_hash}")
|
||||
print(
|
||||
"EXPECTED_GAMDISPLAY_BLOCK_SHA256="
|
||||
f"{EXPECTED_GAMDISPLAY_BLOCK_SHA256}"
|
||||
)
|
||||
report.check(
|
||||
"GAMDISPLAY_BLOCK_STATUS",
|
||||
gamdisplay_hash == EXPECTED_GAMDISPLAY_BLOCK_SHA256,
|
||||
"GAMDISPLAY_BLOCK_HASH_MISMATCH",
|
||||
)
|
||||
|
||||
try:
|
||||
writer = extract_block(
|
||||
canonical,
|
||||
" +IOGAM_Writer = {\n",
|
||||
" }\n +Data = {\n",
|
||||
)
|
||||
except ValueError as exc:
|
||||
print(f"IOGAM_WRITER_EXTRACTION_ERROR={exc}")
|
||||
writer = ""
|
||||
writer_hash = digest(writer)
|
||||
print(f"IOGAM_WRITER_BLOCK_SHA256={writer_hash}")
|
||||
print(
|
||||
"EXPECTED_IOGAM_WRITER_BLOCK_SHA256="
|
||||
f"{EXPECTED_IOGAM_WRITER_BLOCK_SHA256}"
|
||||
)
|
||||
report.check(
|
||||
"IOGAM_WRITER_INVARIANT_STATUS",
|
||||
writer_hash == EXPECTED_IOGAM_WRITER_BLOCK_SHA256,
|
||||
"IOGAM_WRITER_BLOCK_HASH_MISMATCH",
|
||||
)
|
||||
|
||||
try:
|
||||
filewriter = extract_block(
|
||||
canonical,
|
||||
" +FileWriter = {\n",
|
||||
" }\n +States = {\n",
|
||||
)
|
||||
except ValueError as exc:
|
||||
print(f"FILEWRITER_EXTRACTION_ERROR={exc}")
|
||||
filewriter = ""
|
||||
filewriter_hash = digest(filewriter)
|
||||
print(f"FILEWRITER_BLOCK_SHA256={filewriter_hash}")
|
||||
print(
|
||||
"EXPECTED_FILEWRITER_BLOCK_SHA256="
|
||||
f"{EXPECTED_FILEWRITER_BLOCK_SHA256}"
|
||||
)
|
||||
report.check(
|
||||
"FILEWRITER_INVARIANT_STATUS",
|
||||
filewriter_hash == EXPECTED_FILEWRITER_BLOCK_SHA256,
|
||||
"FILEWRITER_BLOCK_HASH_MISMATCH",
|
||||
)
|
||||
|
||||
structural_expectations = {
|
||||
"CONSTANT_GAM_COUNT": (text.count("Class = ConstantGAM"), 1),
|
||||
"SENTINEL_SCALAR_A_DECLARATION_COUNT": (
|
||||
text.count("SentinelScalarA ="),
|
||||
6,
|
||||
),
|
||||
"SENTINEL_SCALAR_B_DECLARATION_COUNT": (
|
||||
text.count("SentinelScalarB ="),
|
||||
6,
|
||||
),
|
||||
"SENTINEL_VECTOR_DECLARATION_COUNT": (
|
||||
text.count("SentinelVector ="),
|
||||
6,
|
||||
),
|
||||
"VECTOR_DIMENSION_COUNT": (text.count("NumberOfDimensions = 1"), 6),
|
||||
"VECTOR_CARDINALITY_COUNT": (text.count("NumberOfElements = 3"), 6),
|
||||
"FILEWRITER_CLASS_COUNT": (
|
||||
text.count("Class = FileDataSource::FileWriter"),
|
||||
1,
|
||||
),
|
||||
"PLAIN_FILEWRITER_CLASS_COUNT": (text.count("Class = FileWriter"), 0),
|
||||
}
|
||||
for name, (observed, expected) in structural_expectations.items():
|
||||
print(f"{name}={observed}")
|
||||
print(f"EXPECTED_{name}={expected}")
|
||||
report.check(
|
||||
f"{name}_STATUS",
|
||||
observed == expected,
|
||||
f"{name}_MISMATCH",
|
||||
)
|
||||
|
||||
expected_display_block = (
|
||||
" +Display = {\n"
|
||||
f" Class = {expected_display_class}\n"
|
||||
" }\n"
|
||||
)
|
||||
report.check(
|
||||
"DISPLAY_DATASOURCE_CLASS_STATUS",
|
||||
text.count(expected_display_block) == 1,
|
||||
"DISPLAY_DATASOURCE_CLASS_MISMATCH",
|
||||
)
|
||||
report.check(
|
||||
"PROFILE_CANONICAL_HASH_STATUS",
|
||||
canonical_hash == expected_profile_hash,
|
||||
"PROFILE_CANONICAL_HASH_MISMATCH",
|
||||
)
|
||||
|
||||
report.emit()
|
||||
passed = all(value == "PASS" for value in report.checks.values())
|
||||
print(f"CONFIG_PROFILE_VERIFICATION_STATUS={'PASS' if passed else 'FAIL'}")
|
||||
return 0 if passed else 1
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument("mode", choices=sorted(EXPECTED_PROFILE_HASHES))
|
||||
parser.add_argument("configuration", type=Path)
|
||||
parser.add_argument("expected_filename")
|
||||
args = parser.parse_args()
|
||||
return verify(args.mode, args.configuration, args.expected_filename)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
Reference in New Issue
Block a user