package archtest import ( "fmt" "sort" "strings" "time" ) type Evaluation struct { Unmatched []Finding Excepted []Finding BadExceptions []string Counts map[string]int CountRegressed []string } func Evaluate(findings []Finding, b *Boundaries, now time.Time) Evaluation { ev := Evaluation{Counts: map[string]int{}} used := make([]bool, len(b.Exceptions)) for i, ex := range b.Exceptions { if err := ex.validate(now); err != nil { ev.BadExceptions = append(ev.BadExceptions, fmt.Sprintf("exception[%d] %s %s %s: %v", i, ex.Kind, ex.File, ex.Symbol, err)) } } for _, f := range findings { ev.Counts[f.Kind]++ matched := false for i, ex := range b.Exceptions { if !ex.matches(f) { continue } if err := ex.validate(now); err != nil { continue } used[i] = true matched = true ev.Excepted = append(ev.Excepted, f) break } if !matched { ev.Unmatched = append(ev.Unmatched, f) } } sort.Slice(ev.Unmatched, func(i, j int) bool { return ev.Unmatched[i].Key() < ev.Unmatched[j].Key() }) sort.Slice(ev.Excepted, func(i, j int) bool { return ev.Excepted[i].Key() < ev.Excepted[j].Key() }) for kind, baseline := range b.BaselineCounts { if ev.Counts[kind] > baseline { ev.CountRegressed = append(ev.CountRegressed, fmt.Sprintf("%s: %d > baseline %d", kind, ev.Counts[kind], baseline)) } } sort.Strings(ev.CountRegressed) return ev } func (ev Evaluation) Failed() bool { return len(ev.Unmatched) > 0 || len(ev.BadExceptions) > 0 || len(ev.CountRegressed) > 0 } func (ev Evaluation) Report() string { var b strings.Builder fmt.Fprintf(&b, "archtest findings=%d excepted=%d unmatched=%d\n", ev.Counts[KindImport]+ev.Counts[KindSQLWrite]+ev.Counts[KindPath]+ev.Counts[KindEvent], len(ev.Excepted), len(ev.Unmatched)) for _, kind := range []string{KindImport, KindSQLWrite, KindPath, KindEvent} { fmt.Fprintf(&b, " %s=%d\n", kind, ev.Counts[kind]) } for _, msg := range ev.BadExceptions { fmt.Fprintf(&b, "BAD_EXCEPTION %s\n", msg) } for _, msg := range ev.CountRegressed { fmt.Fprintf(&b, "REGRESSION %s\n", msg) } for _, f := range ev.Unmatched { fmt.Fprintf(&b, "NEW %s %s %s %s (%s)\n", f.Kind, f.Package, f.File, f.Symbol, f.Detail) } for _, f := range ev.Excepted { fmt.Fprintf(&b, "BASELINE %s %s %s %s (%s)\n", f.Kind, f.Package, f.File, f.Symbol, f.Detail) } return b.String() }