From e9f07dc47b3c9f8dac8d81468501e9d553c12d85 Mon Sep 17 00:00:00 2001 From: Mikhail Kornilovich Date: Fri, 10 Apr 2026 23:41:37 +0300 Subject: [PATCH] test memory limit --- .gitea/workflows/memory.yml | 126 +++++++++++++++++++++++++++++++++++ example/mem-limit/mem.jdg | 35 ++++++++++ example/mem-limit/solution.c | 27 ++++++++ 3 files changed, 188 insertions(+) create mode 100644 .gitea/workflows/memory.yml create mode 100644 example/mem-limit/mem.jdg create mode 100644 example/mem-limit/solution.c diff --git a/.gitea/workflows/memory.yml b/.gitea/workflows/memory.yml new file mode 100644 index 0000000..5088e99 --- /dev/null +++ b/.gitea/workflows/memory.yml @@ -0,0 +1,126 @@ +name: memory-limit +run-name: "memory_limit enforcement check" + +on: + push: + branches: [main] + pull_request: + workflow_dispatch: + +env: + SUITE_FILE: mem.jdg + EXAMPLE_DIR: example/mem-limit + +jobs: + build_judge: + name: Build judge + runs-on: Linux-Runner + timeout-minutes: 10 + steps: + - uses: actions/checkout@v4 + + - name: Set up Go + uses: actions/setup-go@v5 + with: + go-version: '1.22' + + - name: Cross-compile judge + shell: bash + run: | + mkdir -p dist + CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o dist/judge-linux-amd64 ./cmd/cli + CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build -o dist/judge-windows-amd64.exe ./cmd/cli + + - name: Upload judge binaries + uses: https://github.com/christopherHX/gitea-upload-artifact@v4 + with: + name: judge-bin-mem + path: dist/ + retention-days: 1 + + check: + needs: build_judge + name: "${{ matrix.toolchain.system }} / ${{ matrix.toolchain.use_compiler }}" + + strategy: + fail-fast: false + matrix: + toolchain: + - { system: Linux, use_compiler: gcc } + - { system: Linux, use_compiler: clang } + - { system: Windows, use_compiler: clang } + - { system: Windows, use_compiler: msvc } + + runs-on: ${{ matrix.toolchain.system }}-Runner + timeout-minutes: 10 + + env: + CC: ${{ matrix.toolchain.use_compiler }} + + steps: + - name: Checkout judge harness + uses: actions/checkout@v4 + + - name: Set up MSVC environment + if: matrix.toolchain.use_compiler == 'msvc' + uses: ilammy/msvc-dev-cmd@v1 + + - name: Download judge binary + uses: https://github.com/christopherHX/gitea-download-artifact@v4 + with: + name: judge-bin-mem + path: judge-bin + + - name: Install judge on PATH + shell: bash + run: | + mkdir -p bin + if [ "${{ matrix.toolchain.system }}" = "Windows" ]; then + cp judge-bin/judge-windows-amd64.exe bin/judge.exe + else + cp judge-bin/judge-linux-amd64 bin/judge + chmod +x bin/judge + fi + echo "$PWD/bin" >> "$GITHUB_PATH" + + - name: Install jq (Linux) + if: matrix.toolchain.system == 'Linux' + run: sudo apt-get update && sudo apt-get install -y jq + + - name: Run judge and capture JSON + shell: bash + working-directory: ${{ env.EXAMPLE_DIR }} + run: | + # `|| true` — the "exceeds_limit" group is *expected* to have a + # failing test, so judge will exit non-zero. + judge "$SUITE_FILE" . --json > report.json || true + cat report.json + + - name: Assert enforcement + shell: bash + working-directory: ${{ env.EXAMPLE_DIR }} + run: | + set -euo pipefail + + pass_status=$(jq -r '.groups[] | select(.name=="within_limit") | .tests[] | select(.name=="allocate_16mb") | .status' report.json) + fail_status=$(jq -r '.groups[] | select(.name=="exceeds_limit") | .tests[] | select(.name=="allocate_256mb") | .status' report.json) + pass_peak=$(jq -r '.groups[] | select(.name=="within_limit") | .tests[] | select(.name=="allocate_16mb") | .peak_memory_kb // 0' report.json) + fail_peak=$(jq -r '.groups[] | select(.name=="exceeds_limit") | .tests[] | select(.name=="allocate_256mb") | .peak_memory_kb // 0' report.json) + + echo "within_limit/allocate_16mb: status=$pass_status peak=${pass_peak} KiB" + echo "exceeds_limit/allocate_256mb: status=$fail_status peak=${fail_peak} KiB" + + rc=0 + if [ "$pass_status" != "PASS" ]; then + echo "FAIL: within_limit test should PASS, got $pass_status" + rc=1 + fi + if [ "$fail_status" != "MLE" ]; then + echo "FAIL: exceeds_limit test should be MLE, got $fail_status" + rc=1 + fi + if [ "${pass_peak:-0}" -le 0 ]; then + echo "FAIL: within_limit peak memory not reported (got $pass_peak)" + rc=1 + fi + exit $rc diff --git a/example/mem-limit/mem.jdg b/example/mem-limit/mem.jdg new file mode 100644 index 0000000..11ac580 --- /dev/null +++ b/example/mem-limit/mem.jdg @@ -0,0 +1,35 @@ +// End-to-end check that memory_limit is actually enforced by the runner. +// The solution allocates N MiB (argv[1]) and touches every page. +// +// - "within_limit" allocates 16 MiB under a 256 MiB cap → expected PASS +// - "exceeds_limit" allocates 256 MiB under a 64 MiB cap → expected MLE +// +// The workflow runs judge --json and asserts these statuses via jq. + +build "$CC -O2 -std=c11 -Wall -Wextra solution.c -o solution" +build_windows "if /I \"%CC%\"==\"msvc\" (cl /nologo /O2 /W3 solution.c /Fe:solution.exe) else (%CC% -O2 -std=c11 -Wall -Wextra solution.c -o solution.exe)" + +binary = "solution" +timeout 10s + +normalize_crlf = true + +group("within_limit") { + weight = 1.0 + memory_limit = 256M + + test("allocate_16mb") { + args = ["16"] + stdout = "ok 16\n" + } +} + +group("exceeds_limit") { + weight = 0.0 // score doesn't matter — we assert the status in CI + memory_limit = 64M + + test("allocate_256mb") { + args = ["256"] + // no stdout matcher: process is expected to be killed before it prints + } +} diff --git a/example/mem-limit/solution.c b/example/mem-limit/solution.c new file mode 100644 index 0000000..4ccb7a4 --- /dev/null +++ b/example/mem-limit/solution.c @@ -0,0 +1,27 @@ +#include +#include +#include + +int main(int argc, char **argv) { + if (argc < 2) { + fprintf(stderr, "usage: %s \n", argv[0]); + return 1; + } + long mb = strtol(argv[1], NULL, 10); + if (mb <= 0) return 1; + + size_t bytes = (size_t)mb * 1024 * 1024; + char *p = (char *)malloc(bytes); + if (!p) { + fprintf(stderr, "malloc failed\n"); + return 2; + } + for (size_t i = 0; i < bytes; i += 4096) { + p[i] = (char)(i & 0xff); + } + volatile char sink = p[bytes - 1]; + (void)sink; + + printf("ok %ld\n", mb); + return 0; +}