Files
judge/runner/compiler_test.go
Mikhail Kornilovich 128a64a609
All checks were successful
build-dsl-smoke / Discover matrix (push) Successful in 8s
build-dsl-smoke / Build judge (push) Successful in 11s
build-dsl-smoke / ${{ matrix.cell.build }} / ${{ matrix.cell.toolchain }} / ${{ matrix.cell.platform }} (push) Successful in 5s
memory-limit / Build judge (pull_request) Successful in 10s
build-dsl-smoke / SUMMARY (push) Successful in 3s
memory-limit / Linux / gcc (pull_request) Successful in 9s
memory-limit / Linux / clang (pull_request) Successful in 13s
memory-limit / Windows / clang (pull_request) Successful in 16s
memory-limit / Windows / msvc (pull_request) Successful in 17s
add new build system
2026-04-11 01:51:38 +03:00

294 lines
7.9 KiB
Go

package runner
import (
"reflect"
"strings"
"testing"
"github.com/Mond1c/judge/dsl"
)
func TestResolveToolchainKnown(t *testing.T) {
cases := []struct {
in string
wantClass CompilerClass
wantName string
}{
{"gcc", CompilerGNU, "gcc"},
{"g++", CompilerGNU, "gcc"},
{"clang", CompilerGNU, "clang"},
{"clang++", CompilerGNU, "clang"},
{"cl", CompilerMSVC, "msvc"},
{"cl.exe", CompilerMSVC, "msvc"},
{"msvc", CompilerMSVC, "msvc"},
{"clang-cl", CompilerMSVC, "clang-cl"},
{"cc", CompilerGNU, "cc"},
}
for _, c := range cases {
t.Run(c.in, func(t *testing.T) {
tc := ResolveToolchain(c.in)
if tc.Class != c.wantClass {
t.Errorf("class: got %v, want %v", tc.Class, c.wantClass)
}
if tc.Name != c.wantName {
t.Errorf("name: got %q, want %q", tc.Name, c.wantName)
}
})
}
}
func TestResolveToolchainSpec(t *testing.T) {
cases := []struct {
name string
spec dsl.ToolchainSpec
wantClass CompilerClass
wantBin string
wantName string
}{
{
"gcc inferred",
dsl.ToolchainSpec{Name: "gcc", Platforms: []string{"linux"}},
CompilerGNU, "gcc", "gcc",
},
{
"msvc inferred",
dsl.ToolchainSpec{Name: "msvc", Platforms: []string{"windows"}},
CompilerMSVC, "cl", "msvc",
},
{
"nvcc with explicit class",
dsl.ToolchainSpec{Name: "nvcc", Platforms: []string{"linux"}, Class: "gnu"},
CompilerGNU, "nvcc", "nvcc",
},
{
"custom binary override",
dsl.ToolchainSpec{Name: "clang", Platforms: []string{"linux", "windows"}, Binary: "clang-17"},
CompilerGNU, "clang-17", "clang",
},
{
"unknown name, explicit class",
dsl.ToolchainSpec{Name: "hipcc", Platforms: []string{"linux"}, Class: "gnu"},
CompilerGNU, "hipcc", "hipcc",
},
}
for _, c := range cases {
t.Run(c.name, func(t *testing.T) {
got := ResolveToolchainSpec(&c.spec)
if got.Class != c.wantClass {
t.Errorf("class: got %v, want %v", got.Class, c.wantClass)
}
if got.Binary != c.wantBin {
t.Errorf("binary: got %q, want %q", got.Binary, c.wantBin)
}
if got.Name != c.wantName {
t.Errorf("name: got %q, want %q", got.Name, c.wantName)
}
})
}
}
func TestResolveToolchainUnknownFallsBackToGNU(t *testing.T) {
tc := ResolveToolchain("gcc-13")
if tc.Class != CompilerGNU {
t.Errorf("unknown compiler should fall back to GNU, got %v", tc.Class)
}
if tc.Binary != "gcc-13" {
t.Errorf("binary should preserve the original string, got %q", tc.Binary)
}
if tc.Name != "gcc-13" {
t.Errorf("name should be the lowercased original, got %q", tc.Name)
}
}
func TestCompileGNURelease(t *testing.T) {
cfg := dsl.BuildConfig{
Language: "c",
Standard: "c11",
Sources: []string{"solution.c"},
Profile: dsl.ProfileRelease,
Warnings: dsl.WarningsStrict,
}
tc := Toolchain{Class: CompilerGNU, Binary: "gcc", Name: "gcc"}
argv, err := Compile(cfg, tc, "solution")
if err != nil {
t.Fatal(err)
}
want := []string{"gcc", "-std=c11", "-O2", "-Wall", "-Wextra", "solution.c", "-o", "solution"}
if !reflect.DeepEqual(argv, want) {
t.Errorf("argv =\n %v\nwant\n %v", argv, want)
}
}
func TestCompileGNUDebug(t *testing.T) {
cfg := dsl.BuildConfig{
Standard: "c11",
Sources: []string{"a.c", "b.c"},
Profile: dsl.ProfileDebug,
}
tc := Toolchain{Class: CompilerGNU, Binary: "gcc"}
argv, _ := Compile(cfg, tc, "out")
if !containsSubsequence(argv, []string{"-O0", "-g"}) {
t.Errorf("debug flags missing: %v", argv)
}
if !containsSubsequence(argv, []string{"a.c", "b.c", "-o", "out"}) {
t.Errorf("sources and output order wrong: %v", argv)
}
}
func TestCompileGNUSanitized(t *testing.T) {
cfg := dsl.BuildConfig{
Standard: "c11",
Sources: []string{"s.c"},
Profile: dsl.ProfileSanitized,
Sanitize: []string{"address", "undefined"},
}
tc := Toolchain{Class: CompilerGNU, Binary: "clang"}
argv, _ := Compile(cfg, tc, "s")
joined := strings.Join(argv, " ")
if !strings.Contains(joined, "-fsanitize=address,undefined") {
t.Errorf("sanitize flag missing: %v", argv)
}
if !strings.Contains(joined, "-O1") {
t.Errorf("-O1 missing for sanitized profile: %v", argv)
}
}
func TestCompileGNUIncludesAndDefinesAndLink(t *testing.T) {
cfg := dsl.BuildConfig{
Sources: []string{"m.c"},
Includes: []string{"include", "vendor/inc"},
Defines: map[string]string{"FOO": "1", "BAR": ""},
Link: []string{"m", "pthread"},
Extra: []string{"-fno-strict-aliasing"},
}
tc := Toolchain{Class: CompilerGNU, Binary: "gcc"}
argv, _ := Compile(cfg, tc, "out")
joined := strings.Join(argv, " ")
for _, want := range []string{"-Iinclude", "-Ivendor/inc", "-DFOO=1", "-DBAR", "-lm", "-lpthread", "-fno-strict-aliasing"} {
if !strings.Contains(joined, want) {
t.Errorf("missing %q in %v", want, argv)
}
}
oIdx := indexOf(argv, "-o")
lmIdx := indexOf(argv, "-lm")
if oIdx == -1 || lmIdx == -1 || lmIdx < oIdx {
t.Errorf("-lm must come after -o: %v", argv)
}
}
func TestCompileGNUDefinesOrderDeterministic(t *testing.T) {
cfg := dsl.BuildConfig{
Sources: []string{"s.c"},
Defines: map[string]string{"Z": "1", "A": "2", "M": "3"},
}
tc := Toolchain{Class: CompilerGNU, Binary: "gcc"}
argv1, _ := Compile(cfg, tc, "s")
for i := 0; i < 20; i++ {
argv2, _ := Compile(cfg, tc, "s")
if !reflect.DeepEqual(argv1, argv2) {
t.Fatalf("defines order not deterministic:\n %v\n %v", argv1, argv2)
}
}
}
func TestCompileMSVCRelease(t *testing.T) {
cfg := dsl.BuildConfig{
Standard: "c11",
Sources: []string{"solution.c"},
Profile: dsl.ProfileRelease,
Warnings: dsl.WarningsStrict,
}
tc := Toolchain{Class: CompilerMSVC, Binary: "cl", Name: "msvc"}
argv, _ := Compile(cfg, tc, "solution.exe")
want := []string{"cl", "/nologo", "/std:c11", "/O2", "/W4", "solution.c", "/Fe:solution.exe"}
if !reflect.DeepEqual(argv, want) {
t.Errorf("argv =\n %v\nwant\n %v", argv, want)
}
}
func TestCompileMSVCDebug(t *testing.T) {
cfg := dsl.BuildConfig{
Sources: []string{"s.c"},
Profile: dsl.ProfileDebug,
}
tc := Toolchain{Class: CompilerMSVC, Binary: "cl"}
argv, _ := Compile(cfg, tc, "s.exe")
joined := strings.Join(argv, " ")
for _, want := range []string{"/Od", "/Zi"} {
if !strings.Contains(joined, want) {
t.Errorf("missing %q in %v", want, argv)
}
}
}
func TestCompileMSVCSanitizedAddressOnly(t *testing.T) {
cfg := dsl.BuildConfig{
Sources: []string{"s.c"},
Profile: dsl.ProfileSanitized,
Sanitize: []string{"address", "undefined", "thread"},
}
tc := Toolchain{Class: CompilerMSVC, Binary: "cl"}
argv, _ := Compile(cfg, tc, "s.exe")
joined := strings.Join(argv, " ")
if !strings.Contains(joined, "/fsanitize=address") {
t.Errorf("msvc should emit /fsanitize=address for sanitized profile: %v", argv)
}
if strings.Contains(joined, "undefined") || strings.Contains(joined, "thread") {
t.Errorf("msvc should drop unsupported sanitizers, got: %v", argv)
}
}
func TestCompileMSVCIncludesAndDefines(t *testing.T) {
cfg := dsl.BuildConfig{
Sources: []string{"m.c"},
Includes: []string{"include"},
Defines: map[string]string{"FOO": "1", "BAR": ""},
}
tc := Toolchain{Class: CompilerMSVC, Binary: "cl"}
argv, _ := Compile(cfg, tc, "m.exe")
joined := strings.Join(argv, " ")
for _, want := range []string{"/Iinclude", "/DFOO=1", "/DBAR"} {
if !strings.Contains(joined, want) {
t.Errorf("missing %q in %v", want, argv)
}
}
}
func TestCompileUnknownClassErrors(t *testing.T) {
cfg := dsl.BuildConfig{Sources: []string{"s.c"}}
tc := Toolchain{Class: CompilerUnknown, Binary: "weird"}
if _, err := Compile(cfg, tc, "s"); err == nil {
t.Error("expected error for unknown compiler class")
}
}
func containsSubsequence(haystack, needle []string) bool {
if len(needle) == 0 {
return true
}
for i := 0; i+len(needle) <= len(haystack); i++ {
match := true
for j := range needle {
if haystack[i+j] != needle[j] {
match = false
break
}
}
if match {
return true
}
}
return false
}
func indexOf(xs []string, x string) int {
for i, v := range xs {
if v == x {
return i
}
}
return -1
}