de140e564e
P1 of the auto-FMEA build plan: establish the public-domain methodology foundation (no AIAG-VDA/SAE/IEC tables reproduced). - fmea_data_sources.go: MIL-STD-882E severity (Cat I-IV→1-10) + probability (A-F→1-10 with per-hour λ bands), OccurrenceFromRate(λp·α), SeverityForCategory, MIL-STD-1629A CriticalityCm = λp·α·β·t. Own 1-10 projection, government-anchored. - 4 versioned source docs (MIL-STD-1629A, MIL-STD-882E, NASA RCM, FMD-91/NPRD-91) ingested into the new RAG collection bp_iace_fmea_kb (whitelisted). - Tests for all scales/mappings/criticality (green). Next (P1 step 2): fetch FMD-91/NPRD-91 bulk λ/α tables from DTIC. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
94 lines
2.5 KiB
Go
94 lines
2.5 KiB
Go
package iace
|
|
|
|
import (
|
|
"math"
|
|
"testing"
|
|
)
|
|
|
|
func TestMILStd882Severity_OrderedAndComplete(t *testing.T) {
|
|
bands := MILStd882Severity()
|
|
if len(bands) != 4 {
|
|
t.Fatalf("expected 4 severity categories, got %d", len(bands))
|
|
}
|
|
wantCat := []string{"I", "II", "III", "IV"}
|
|
prev := 11
|
|
for i, b := range bands {
|
|
if b.Category != wantCat[i] {
|
|
t.Errorf("band %d: category %q, want %q", i, b.Category, wantCat[i])
|
|
}
|
|
if b.LabelDE == "" || b.Desc == "" {
|
|
t.Errorf("band %s: empty label/desc", b.Category)
|
|
}
|
|
if b.Severity < 1 || b.Severity > 10 {
|
|
t.Errorf("band %s: severity %d out of 1-10", b.Category, b.Severity)
|
|
}
|
|
if b.Severity >= prev {
|
|
t.Errorf("severity not strictly descending at %s (%d >= %d)", b.Category, b.Severity, prev)
|
|
}
|
|
prev = b.Severity
|
|
}
|
|
}
|
|
|
|
func TestMILStd882Probability_OrderedBands(t *testing.T) {
|
|
bands := MILStd882Probability()
|
|
if len(bands) != 6 {
|
|
t.Fatalf("expected 6 probability levels, got %d", len(bands))
|
|
}
|
|
prevOcc, prevLambda := 11, math.Inf(1)
|
|
for _, b := range bands {
|
|
if b.Occurrence < 1 || b.Occurrence > 10 {
|
|
t.Errorf("%s: occurrence %d out of 1-10", b.Level, b.Occurrence)
|
|
}
|
|
if b.Occurrence >= prevOcc {
|
|
t.Errorf("occurrence not descending at %s", b.Level)
|
|
}
|
|
if b.LambdaMax >= prevLambda {
|
|
t.Errorf("lambda_max not descending at %s", b.Level)
|
|
}
|
|
prevOcc, prevLambda = b.Occurrence, b.LambdaMax
|
|
}
|
|
}
|
|
|
|
func TestOccurrenceFromRate(t *testing.T) {
|
|
cases := []struct {
|
|
rate float64
|
|
want int
|
|
}{
|
|
{1.0, 10}, // A frequent
|
|
{0.05, 8}, // B probable (1e-2..1e-1)
|
|
{0.005, 6}, // C occasional (1e-3..1e-2)
|
|
{1e-4, 4}, // D remote (1e-6..1e-3)
|
|
{1e-8, 2}, // E improbable (<1e-6)
|
|
{0, 1}, // F eliminated
|
|
{-5, 1}, // guard
|
|
}
|
|
for _, c := range cases {
|
|
if got := OccurrenceFromRate(c.rate); got != c.want {
|
|
t.Errorf("OccurrenceFromRate(%g) = %d, want %d", c.rate, got, c.want)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestSeverityForCategory(t *testing.T) {
|
|
if got := SeverityForCategory("I"); got != 10 {
|
|
t.Errorf("Cat I = %d, want 10", got)
|
|
}
|
|
if got := SeverityForCategory("IV"); got != 2 {
|
|
t.Errorf("Cat IV = %d, want 2", got)
|
|
}
|
|
if got := SeverityForCategory("Z"); got != 0 {
|
|
t.Errorf("unknown cat = %d, want 0", got)
|
|
}
|
|
}
|
|
|
|
func TestCriticalityCm(t *testing.T) {
|
|
// Cm = λp·α·β·t = 1e-5 · 0.3 · 1.0 · 1000 = 3e-3
|
|
got := CriticalityCm(1e-5, 0.3, 1.0, 1000)
|
|
if math.Abs(got-3e-3) > 1e-9 {
|
|
t.Errorf("Cm = %g, want 3e-3", got)
|
|
}
|
|
if CriticalityCm(-1, 0.3, 1, 1000) != 0 {
|
|
t.Error("negative input should yield 0")
|
|
}
|
|
}
|