package reporter import ( "encoding/json" "fmt" "io" "os" "path/filepath" "sort" "strings" "github.com/Mond1c/judge/runner" ) func Text(w io.Writer, result *runner.SuiteResult) { if result.BuildLog != "" { fmt.Fprintf(w, "=== BUILD LOG ===\n%s\n", result.BuildLog) } for _, gr := range result.Groups { passed := gr.Passed total := gr.Total pct := 0.0 if total > 0 { pct = float64(passed) / float64(total) * 100 } fmt.Fprintf(w, "\n┌─ group %q weight=%.2f score=%.4f\n", gr.Name, gr.Weight, gr.Score) fmt.Fprintf(w, "│ %d/%d passed (%.0f%%)\n", passed, total, pct) for _, tr := range gr.Tests { icon := "✓" if tr.Status != runner.StatusPass { icon = "✗" } fmt.Fprintf(w, "│ %s [%s] %s (%dms)\n", icon, tr.Status, tr.Name, tr.Elapsed.Milliseconds()) for _, f := range tr.Failures { for _, line := range strings.Split(f, "\n") { fmt.Fprintf(w, "│ %s\n", line) } } } fmt.Fprintf(w, "└─\n") } fmt.Fprintf(w, "\n══ TOTAL SCORE: %.4f / 1.0000 ══\n", result.TotalScore) } func JSON(w io.Writer, result *runner.SuiteResult) error { enc := json.NewEncoder(w) enc.SetIndent("", " ") return enc.Encode(jsonResult(result)) } type jsonSuiteResult struct { TotalScore float64 `json:"total_score"` BuildLog string `json:"build_log,omitempty"` Groups []jsonGroupResult `json:"groups"` } type jsonGroupResult struct { Name string `json:"name"` Weight float64 `json:"weight"` Score float64 `json:"score"` Passed int `json:"passed"` Total int `json:"total"` Tests []jsonTestResult `json:"tests"` } type jsonTestResult struct { Name string `json:"name"` Status string `json:"status"` ElapsedMs int64 `json:"elapsed_ms"` Failures []string `json:"failures,omitempty"` } func Aggregate(w io.Writer, dir string) error { files, err := filepath.Glob(filepath.Join(dir, "*", "*.json")) if err != nil { return fmt.Errorf("glob reports: %w", err) } if len(files) == 0 { files, _ = filepath.Glob(filepath.Join(dir, "*.json")) } if len(files) == 0 { return fmt.Errorf("no JSON reports found in %s", dir) } sort.Strings(files) type entry struct { Config string Score float64 } var entries []entry allPassed := true for _, f := range files { data, err := os.ReadFile(f) if err != nil { return fmt.Errorf("read %s: %w", f, err) } var report jsonSuiteResult if err := json.Unmarshal(data, &report); err != nil { return fmt.Errorf("parse %s: %w", f, err) } cfg := filepath.Base(filepath.Dir(f)) cfg = strings.TrimPrefix(cfg, "report_") entries = append(entries, entry{Config: cfg, Score: report.TotalScore}) if report.TotalScore < 0.9999 { allPassed = false } } fmt.Fprintln(w, "# Judge results") fmt.Fprintln(w) fmt.Fprintln(w, "| Configuration | Score |") fmt.Fprintln(w, "|---|---|") for _, e := range entries { fmt.Fprintf(w, "| %s | %.4f |\n", e.Config, e.Score) } if !allPassed { return fmt.Errorf("one or more configurations scored below 1.0") } return nil } func jsonResult(r *runner.SuiteResult) jsonSuiteResult { res := jsonSuiteResult{ TotalScore: r.TotalScore, BuildLog: r.BuildLog, } for _, gr := range r.Groups { jgr := jsonGroupResult{ Name: gr.Name, Weight: gr.Weight, Score: gr.Score, Passed: gr.Passed, Total: gr.Total, } for _, tr := range gr.Tests { jgr.Tests = append(jgr.Tests, jsonTestResult{ Name: tr.Name, Status: tr.Status.String(), ElapsedMs: tr.Elapsed.Milliseconds(), Failures: tr.Failures, }) } res.Groups = append(res.Groups, jgr) } return res }