Some checks failed
judge / Build judge (push) Successful in 10s
judge / Linux / gcc / Debug (push) Successful in 9s
judge / Linux / clang / Release (push) Successful in 12s
judge / Linux / clang / Sanitized (push) Successful in 11s
judge / Linux / gcc / Release (push) Successful in 11s
judge / Linux / gcc / Sanitized (push) Successful in 10s
judge / Linux / gcc / Debug (valgrind) (push) Successful in 17s
judge / Windows / clang / Debug (push) Successful in 1m28s
judge / Windows / clang / Release (push) Successful in 1m24s
judge / Windows / msvc / Release (push) Successful in 1m28s
judge / Windows / msvc / Debug (push) Successful in 1m33s
judge / SUMMARY (push) Failing after 4s
108 lines
2.3 KiB
Go
108 lines
2.3 KiB
Go
package main
|
|
|
|
import (
|
|
"flag"
|
|
"fmt"
|
|
"os"
|
|
|
|
"github.com/Mond1c/judge/dsl"
|
|
"github.com/Mond1c/judge/reporter"
|
|
"github.com/Mond1c/judge/runner"
|
|
)
|
|
|
|
const usage = `judge — CI/CD testing system for student solutions
|
|
|
|
Usage:
|
|
judge <tests.jdg> <solution-dir> [flags]
|
|
judge aggregate <reports-dir>
|
|
|
|
Flags:
|
|
--json output as JSON instead of text
|
|
--wrapper <cmd> exec wrapper (e.g. "valgrind --error-exitcode=99")
|
|
--binary <name> name of executable produced by build (overrides .jdg)
|
|
--help show help
|
|
|
|
Example:
|
|
judge lab1.jdg ./student-solution
|
|
judge lab1.jdg ./student-solution --json
|
|
judge aggregate reports/
|
|
`
|
|
|
|
func main() {
|
|
if len(os.Args) >= 2 && os.Args[1] == "aggregate" {
|
|
runAggregate()
|
|
return
|
|
}
|
|
|
|
fs := flag.NewFlagSet("judge", flag.ContinueOnError)
|
|
fs.SetOutput(os.Stderr)
|
|
fs.Usage = func() { fmt.Fprint(os.Stderr, usage) }
|
|
|
|
jsonOutput := fs.Bool("json", false, "output as JSON")
|
|
wrapper := fs.String("wrapper", "", "exec wrapper command")
|
|
binary := fs.String("binary", "", "binary name override")
|
|
|
|
if err := fs.Parse(os.Args[1:]); err != nil {
|
|
os.Exit(2)
|
|
}
|
|
|
|
if fs.NArg() < 2 {
|
|
fmt.Fprintf(os.Stderr, "error: need <tests.jdg> and <solution-dir>\n\n%s", usage)
|
|
os.Exit(1)
|
|
}
|
|
|
|
testFile := fs.Arg(0)
|
|
solutionDir := fs.Arg(1)
|
|
|
|
src, err := os.ReadFile(testFile)
|
|
if err != nil {
|
|
fatalf("cannot read %q: %v", testFile, err)
|
|
}
|
|
|
|
f, warns, err := dsl.Parse(string(src))
|
|
if err != nil {
|
|
fatalf("parse error in %q:\n %v", testFile, err)
|
|
}
|
|
for _, w := range warns {
|
|
fmt.Fprintf(os.Stderr, "warning: %s\n", w)
|
|
}
|
|
|
|
if _, err := os.Stat(solutionDir); err != nil {
|
|
fatalf("solution dir %q not found: %v", solutionDir, err)
|
|
}
|
|
|
|
r := runner.New(f, runner.Config{
|
|
WorkDir: solutionDir,
|
|
BinaryName: *binary,
|
|
Wrapper: *wrapper,
|
|
})
|
|
result := r.Run()
|
|
|
|
if *jsonOutput {
|
|
if err := reporter.JSON(os.Stdout, result); err != nil {
|
|
fatalf("json output error: %v", err)
|
|
}
|
|
} else {
|
|
reporter.Text(os.Stdout, result)
|
|
}
|
|
|
|
if result.TotalScore < 0.9999 {
|
|
os.Exit(1)
|
|
}
|
|
}
|
|
|
|
func runAggregate() {
|
|
if len(os.Args) < 3 {
|
|
fatalf("usage: judge aggregate <reports-dir>")
|
|
}
|
|
dir := os.Args[2]
|
|
if err := reporter.Aggregate(os.Stdout, dir); err != nil {
|
|
fatalf("%v", err)
|
|
}
|
|
}
|
|
|
|
func fatalf(msg string, args ...any) {
|
|
fmt.Fprintf(os.Stderr, "error: "+msg+"\n", args...)
|
|
os.Exit(1)
|
|
}
|