init:
This commit is contained in:
148
example/c-sum/.gitea/workflows/judge.yml
Normal file
148
example/c-sum/.gitea/workflows/judge.yml
Normal file
@@ -0,0 +1,148 @@
|
||||
name: judge
|
||||
run-name: "Sum tests (${{ inputs.student_url || github.repository }})"
|
||||
|
||||
on:
|
||||
push:
|
||||
pull_request:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
student_url:
|
||||
description: "Student repo (owner/repo), leave empty to use current repo"
|
||||
required: false
|
||||
type: string
|
||||
default: ""
|
||||
student_ref:
|
||||
description: "Ref (branch / tag / SHA)"
|
||||
required: false
|
||||
type: string
|
||||
default: "main"
|
||||
|
||||
env:
|
||||
SUITE_FILE: sum.jdg
|
||||
SOURCES_DIR: __sources__
|
||||
|
||||
jobs:
|
||||
test:
|
||||
name: "${{ matrix.toolchain.system }} / ${{ matrix.toolchain.use_compiler }} / ${{ matrix.toolchain.build_type }}${{ matrix.toolchain.wrapper != 'no' && format(' ({0})', matrix.toolchain.wrapper) || '' }}"
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
toolchain:
|
||||
- { system: Linux, use_compiler: gcc, build_type: Release, cflags: "-O2", wrapper: no, timeout_factor: 1.0 }
|
||||
- { system: Linux, use_compiler: gcc, build_type: Debug, cflags: "-O0 -g", wrapper: no, timeout_factor: 2.5 }
|
||||
- { system: Linux, use_compiler: gcc, build_type: Sanitized, cflags: "-O1 -g -fsanitize=address,undefined", wrapper: no, timeout_factor: 2.5 }
|
||||
- { system: Linux, use_compiler: gcc, build_type: Debug, cflags: "-O0 -g", wrapper: valgrind, timeout_factor: 5.0 }
|
||||
- { system: Linux, use_compiler: clang, build_type: Release, cflags: "-O2", wrapper: no, timeout_factor: 1.0 }
|
||||
- { system: Linux, use_compiler: clang, build_type: Sanitized, cflags: "-O1 -g -fsanitize=address,undefined", wrapper: no, timeout_factor: 2.5 }
|
||||
- { system: Windows, use_compiler: clang, build_type: Release, cflags: "-O2", wrapper: no, timeout_factor: 2.0 }
|
||||
- { system: Windows, use_compiler: clang, build_type: Debug, cflags: "-O0 -g", wrapper: no, timeout_factor: 3.0 }
|
||||
- { system: Windows, use_compiler: msvc, build_type: Release, cflags: "/O2", wrapper: no, timeout_factor: 2.0 }
|
||||
- { system: Windows, use_compiler: msvc, build_type: Debug, cflags: "/Od /Zi", wrapper: no, timeout_factor: 5.0 }
|
||||
|
||||
runs-on: ${{ matrix.toolchain.system }}-Runner
|
||||
timeout-minutes: 10
|
||||
|
||||
env:
|
||||
REPORT_NAME: "report_${{ matrix.toolchain.system }}_${{ matrix.toolchain.use_compiler }}_${{ matrix.toolchain.build_type }}_${{ matrix.toolchain.wrapper }}"
|
||||
CC: ${{ matrix.toolchain.use_compiler }}
|
||||
CFLAGS: ${{ matrix.toolchain.cflags }}
|
||||
|
||||
steps:
|
||||
- name: Checkout judge harness
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Checkout student sources
|
||||
if: ${{ inputs.student_url != '' }}
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
repository: ${{ inputs.student_url }}
|
||||
ref: ${{ inputs.student_ref }}
|
||||
path: ${{ env.SOURCES_DIR }}
|
||||
token: ${{ secrets.VAR_TOKEN }}
|
||||
|
||||
- name: Stage sources (self)
|
||||
if: ${{ inputs.student_url == '' }}
|
||||
shell: bash
|
||||
run: |
|
||||
mkdir -p "${SOURCES_DIR}"
|
||||
cp solution.c "${SOURCES_DIR}/"
|
||||
|
||||
- name: Set up MSVC environment
|
||||
if: matrix.toolchain.use_compiler == 'msvc'
|
||||
uses: ilammy/msvc-dev-cmd@v1
|
||||
|
||||
- name: Install judge
|
||||
shell: bash
|
||||
run: |
|
||||
go install github.com/Mond1c/judge/cmd/cli@latest
|
||||
echo "$HOME/go/bin" >> "$GITHUB_PATH"
|
||||
|
||||
- name: Install valgrind
|
||||
if: matrix.toolchain.wrapper == 'valgrind'
|
||||
run: sudo apt-get update && sudo apt-get install -y valgrind
|
||||
|
||||
- name: Run judge
|
||||
shell: bash
|
||||
working-directory: ${{ env.SOURCES_DIR }}
|
||||
env:
|
||||
WRAPPER: ${{ matrix.toolchain.wrapper }}
|
||||
run: |
|
||||
cp ../${{ env.SUITE_FILE }} .
|
||||
|
||||
WRAPPER_ARG=""
|
||||
case "$WRAPPER" in
|
||||
valgrind) WRAPPER_ARG='--wrapper=valgrind --error-exitcode=99 --leak-check=full -q' ;;
|
||||
no) WRAPPER_ARG="" ;;
|
||||
*) WRAPPER_ARG="--wrapper=$WRAPPER" ;;
|
||||
esac
|
||||
|
||||
# For MSVC the suffixed .exe is produced; runner auto-detects it.
|
||||
judge ${{ env.SUITE_FILE }} . --json $WRAPPER_ARG > "$GITHUB_WORKSPACE/${REPORT_NAME}.json" \
|
||||
|| echo "judge exited non-zero (expected when tests fail)"
|
||||
|
||||
judge ${{ env.SUITE_FILE }} . $WRAPPER_ARG || true
|
||||
|
||||
- name: Upload report
|
||||
if: ${{ always() }}
|
||||
uses: https://github.com/christopherHX/gitea-upload-artifact@v4
|
||||
with:
|
||||
name: ${{ env.REPORT_NAME }}
|
||||
path: ${{ env.REPORT_NAME }}.json
|
||||
retention-days: 7
|
||||
compression-level: 9
|
||||
|
||||
summary:
|
||||
needs: [test]
|
||||
if: ${{ always() }}
|
||||
name: SUMMARY
|
||||
runs-on: Linux-Runner
|
||||
timeout-minutes: 5
|
||||
|
||||
steps:
|
||||
- name: Download all reports
|
||||
uses: https://github.com/christopherHX/gitea-download-artifact@v4
|
||||
with:
|
||||
path: reports
|
||||
pattern: report_*
|
||||
|
||||
- name: Aggregate
|
||||
shell: bash
|
||||
run: |
|
||||
echo "# Judge results" > SUMMARY.md
|
||||
echo "" >> SUMMARY.md
|
||||
echo "| Configuration | Score |" >> SUMMARY.md
|
||||
echo "|---|---|" >> SUMMARY.md
|
||||
for f in reports/*/*.json; do
|
||||
cfg=$(basename "$(dirname "$f")" | sed 's/^report_//')
|
||||
score=$(grep -o '"TotalScore":[^,}]*' "$f" | head -1 | cut -d: -f2)
|
||||
echo "| $cfg | $score |" >> SUMMARY.md
|
||||
done
|
||||
cat SUMMARY.md
|
||||
|
||||
- name: Upload summary
|
||||
uses: https://github.com/christopherHX/gitea-upload-artifact@v4
|
||||
with:
|
||||
name: SUMMARY
|
||||
path: SUMMARY.md
|
||||
retention-days: 7
|
||||
49
example/c-sum/README.md
Normal file
49
example/c-sum/README.md
Normal file
@@ -0,0 +1,49 @@
|
||||
# c-sum — cross-platform C example
|
||||
|
||||
Minimal example: a C program that reads `N` then `N` integers and prints their
|
||||
sum. Tested with `judge` across **gcc / clang / MSVC** on **Linux / Windows**,
|
||||
with optional **valgrind** and **sanitizer** runs.
|
||||
|
||||
## Files
|
||||
|
||||
- `solution.c` — student-facing solution (could be what the student submits)
|
||||
- `sum.jdg` — judge test suite
|
||||
- `.gitea/workflows/judge.yml` — Gitea CI matrix
|
||||
|
||||
## Run locally
|
||||
|
||||
```sh
|
||||
# Linux / macOS
|
||||
CC=gcc judge sum.jdg .
|
||||
CC=clang judge sum.jdg .
|
||||
|
||||
# With valgrind
|
||||
judge sum.jdg . --wrapper="valgrind --error-exitcode=99 --leak-check=full -q"
|
||||
|
||||
# With ASan+UBSan build
|
||||
CC=clang CFLAGS="-O1 -g -fsanitize=address,undefined" judge sum.jdg .
|
||||
```
|
||||
|
||||
On Windows (inside an MSVC dev cmd shell), `build_windows` kicks in and
|
||||
produces `solution.exe`, which the runner auto-detects.
|
||||
|
||||
## Notes about the `.jdg`
|
||||
|
||||
- `normalize_crlf = true` — Windows `printf` emits `\r\n`; we strip `\r` before
|
||||
matching so the same expected outputs work on both platforms.
|
||||
- `trim_trailing_ws = true` — forgives trailing spaces a student's output might
|
||||
pick up (rare but annoying to debug).
|
||||
- `binary = "solution"` — the runner tries `solution` first, then
|
||||
`solution.exe` on Windows automatically.
|
||||
- Per-group `scoring = all_or_none` on `stress` gives weight only if every
|
||||
stress test passes.
|
||||
|
||||
## Adapting the Gitea workflow
|
||||
|
||||
- `runs-on: ${{ matrix.toolchain.system }}-Runner` assumes you have
|
||||
self-hosted Gitea runners labelled `Linux-Runner` / `Windows-Runner` (same
|
||||
naming as your existing `fixed_floating` pipeline).
|
||||
- `secrets.VAR_TOKEN` is only needed when pulling a student repo from a
|
||||
private org.
|
||||
- The summary job shells `grep` over the JSON; swap to `jq` if available on
|
||||
your runners.
|
||||
16
example/c-sum/solution.c
Normal file
16
example/c-sum/solution.c
Normal file
@@ -0,0 +1,16 @@
|
||||
#include <stdio.h>
|
||||
|
||||
int main(void) {
|
||||
int n;
|
||||
if (scanf("%d", &n) != 1) return 1;
|
||||
|
||||
long long sum = 0;
|
||||
for (int i = 0; i < n; i++) {
|
||||
int x;
|
||||
if (scanf("%d", &x) != 1) return 1;
|
||||
sum += x;
|
||||
}
|
||||
|
||||
printf("%lld\n", sum);
|
||||
return 0;
|
||||
}
|
||||
75
example/c-sum/sum.jdg
Normal file
75
example/c-sum/sum.jdg
Normal file
@@ -0,0 +1,75 @@
|
||||
// Cross-platform C solution test suite.
|
||||
// $CC is supplied by CI matrix (gcc / clang / cl).
|
||||
//
|
||||
// Run locally:
|
||||
// CC=gcc judge sum.jdg .
|
||||
// CC=clang judge sum.jdg .
|
||||
//
|
||||
// Under MSVC on CI we use build_windows (cl's CLI is different).
|
||||
|
||||
build "$CC -O2 -std=c11 -Wall -Wextra solution.c -o solution"
|
||||
build_windows "cl /nologo /O2 /W3 solution.c /Fe:solution.exe"
|
||||
|
||||
binary = "solution"
|
||||
timeout 5s
|
||||
|
||||
// Windows printf emits \r\n; normalize so tests are portable.
|
||||
normalize_crlf = true
|
||||
trim_trailing_ws = true
|
||||
|
||||
group("basic") {
|
||||
weight = 0.4
|
||||
timeout = 2s
|
||||
|
||||
test("one number") {
|
||||
stdin = "1\n42\n"
|
||||
stdout = "42\n"
|
||||
}
|
||||
|
||||
test("three numbers") {
|
||||
stdin = "3\n1 2 3\n"
|
||||
stdout = "6\n"
|
||||
}
|
||||
|
||||
test("negatives") {
|
||||
stdin = "4\n-1 -2 3 5\n"
|
||||
stdout = "5\n"
|
||||
}
|
||||
|
||||
test("zero count") {
|
||||
stdin = "0\n"
|
||||
stdout = "0\n"
|
||||
}
|
||||
}
|
||||
|
||||
group("edge") {
|
||||
weight = 0.3
|
||||
|
||||
test("large sum fits in int64") {
|
||||
stdin = "3\n2000000000 2000000000 2000000000\n"
|
||||
stdout = "6000000000\n"
|
||||
}
|
||||
|
||||
test("multiline input") {
|
||||
stdin = """
|
||||
5
|
||||
10
|
||||
20
|
||||
30
|
||||
40
|
||||
50
|
||||
"""
|
||||
stdout = "150\n"
|
||||
}
|
||||
}
|
||||
|
||||
group("stress") {
|
||||
weight = 0.3
|
||||
timeout = 3s
|
||||
scoring = all_or_none
|
||||
|
||||
test("sum of 1..100") {
|
||||
stdin = "100\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100\n"
|
||||
stdout = "5050\n"
|
||||
}
|
||||
}
|
||||
60
example/lab1.jdg
Normal file
60
example/lab1.jdg
Normal file
@@ -0,0 +1,60 @@
|
||||
build "go build -o solution ."
|
||||
timeout 10s
|
||||
|
||||
group("basic") {
|
||||
weight = 0.2
|
||||
timeout = 2s
|
||||
|
||||
test("empty one element") {
|
||||
stdin = "1\n42\n"
|
||||
stdout = "42\n"
|
||||
}
|
||||
|
||||
test("already sorted") {
|
||||
stdin = "3\n1 2 3\n"
|
||||
stdout = "1 2 3\n"
|
||||
}
|
||||
|
||||
test("reverse order") {
|
||||
stdin = "4\n4 3 2 1\n"
|
||||
stdout = "1 2 3 4\n"
|
||||
}
|
||||
}
|
||||
|
||||
group("main") {
|
||||
weight = 0.5
|
||||
|
||||
test("basic") {
|
||||
stdin = "5\n1 3 2 5 4\n"
|
||||
stdout = "1 2 3 4 5\n"
|
||||
}
|
||||
|
||||
test("negative numbers") {
|
||||
stdin = "5\n-3 1 -1 0 2\n"
|
||||
stdout = "-3 -1 0 1 2\n"
|
||||
}
|
||||
|
||||
test("same numbers") {
|
||||
stdin = "4\n5 5 5 5\n"
|
||||
stdout = "5 5 5 5\n"
|
||||
}
|
||||
|
||||
test("multiline stdin") {
|
||||
stdin = """
|
||||
6
|
||||
100 -50 0 75 -25 50
|
||||
"""
|
||||
stdout = "-50 -25 0 50 75 100\n"
|
||||
}
|
||||
}
|
||||
|
||||
group("file-pattern") {
|
||||
weight = 0.3
|
||||
timeout = 5s
|
||||
|
||||
pattern {
|
||||
input = "testdata/*/input.txt"
|
||||
output = "testdata/*/output.txt"
|
||||
}
|
||||
}
|
||||
|
||||
34
example/solution/main.go
Normal file
34
example/solution/main.go
Normal file
@@ -0,0 +1,34 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"os"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func main() {
|
||||
scanner := bufio.NewScanner(os.Stdin)
|
||||
|
||||
scanner.Scan()
|
||||
n, _ := strconv.Atoi(strings.TrimSpace(scanner.Text()))
|
||||
|
||||
scanner.Scan()
|
||||
parts := strings.Fields(scanner.Text())
|
||||
|
||||
nums := make([]int, 0, n)
|
||||
for _, p := range parts {
|
||||
x, _ := strconv.Atoi(p)
|
||||
nums = append(nums, x)
|
||||
}
|
||||
|
||||
sort.Ints(nums)
|
||||
|
||||
out := make([]string, len(nums))
|
||||
for i, v := range nums {
|
||||
out[i] = strconv.Itoa(v)
|
||||
}
|
||||
fmt.Println(strings.Join(out, " "))
|
||||
}
|
||||
Reference in New Issue
Block a user