1. New build system
All checks were successful
build-dsl-smoke / Build judge (push) Successful in 12s
build-dsl-smoke / debug / clang / linux (push) Successful in 6s
build-dsl-smoke / debug / gcc / linux (push) Successful in 8s
build-dsl-smoke / release / clang / linux (push) Successful in 8s
build-dsl-smoke / release / gcc / linux (push) Successful in 6s
build-dsl-smoke / sanitized / clang / linux (push) Successful in 8s
build-dsl-smoke / sanitized / gcc / linux (push) Successful in 7s
build-dsl-smoke / debug / clang / windows (push) Successful in 13s
build-dsl-smoke / debug-valgrind / gcc / linux (push) Successful in 14s
build-dsl-smoke / release / clang / windows (push) Successful in 16s
build-dsl-smoke / debug / msvc / windows (push) Successful in 18s
build-dsl-smoke / release / msvc / windows (push) Successful in 17s
build-dsl-smoke / SUMMARY (push) Successful in 4s
Release / Build & publish (push) Successful in 48s
All checks were successful
build-dsl-smoke / Build judge (push) Successful in 12s
build-dsl-smoke / debug / clang / linux (push) Successful in 6s
build-dsl-smoke / debug / gcc / linux (push) Successful in 8s
build-dsl-smoke / release / clang / linux (push) Successful in 8s
build-dsl-smoke / release / gcc / linux (push) Successful in 6s
build-dsl-smoke / sanitized / clang / linux (push) Successful in 8s
build-dsl-smoke / sanitized / gcc / linux (push) Successful in 7s
build-dsl-smoke / debug / clang / windows (push) Successful in 13s
build-dsl-smoke / debug-valgrind / gcc / linux (push) Successful in 14s
build-dsl-smoke / release / clang / windows (push) Successful in 16s
build-dsl-smoke / debug / msvc / windows (push) Successful in 18s
build-dsl-smoke / release / msvc / windows (push) Successful in 17s
build-dsl-smoke / SUMMARY (push) Successful in 4s
Release / Build & publish (push) Successful in 48s
Reviewed-on: #1
This commit was merged in pull request #1.
This commit is contained in:
293
runner/compiler_test.go
Normal file
293
runner/compiler_test.go
Normal file
@@ -0,0 +1,293 @@
|
||||
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
|
||||
}
|
||||
Reference in New Issue
Block a user