aboutsummaryrefslogtreecommitdiffhomepage
path: root/backend
diff options
context:
space:
mode:
authornsfisis <nsfisis@gmail.com>2026-02-28 17:00:35 +0900
committernsfisis <nsfisis@gmail.com>2026-02-28 17:00:35 +0900
commitb15f06ece6a9dac01a06688be39e437cfa8ed257 (patch)
tree2a713f2079d7055f5777c84db29f9c25bf867e33 /backend
parent10f0cd9dbc408b171fbd6a96ed1b552407046b13 (diff)
downloadphperkaigi-2026-albatross-b15f06ece6a9dac01a06688be39e437cfa8ed257.tar.gz
phperkaigi-2026-albatross-b15f06ece6a9dac01a06688be39e437cfa8ed257.tar.zst
phperkaigi-2026-albatross-b15f06ece6a9dac01a06688be39e437cfa8ed257.zip
fix(game): normalize CRLF to LF in stdin and code before execution
DB stores stdin/code with CRLF intact, but workers expect LF. Add normalizeCRLF helper and apply it to code and stdin in EnqueueTestTasks. Refactor normalizeTestcaseResultOutput to reuse the same helper.
Diffstat (limited to 'backend')
-rw-r--r--backend/game/hub.go11
-rw-r--r--backend/game/hub_test.go53
2 files changed, 60 insertions, 4 deletions
diff --git a/backend/game/hub.go b/backend/game/hub.go
index 8c0a574..3caa6d4 100644
--- a/backend/game/hub.go
+++ b/backend/game/hub.go
@@ -71,8 +71,8 @@ func (hub *Hub) EnqueueTestTasks(ctx context.Context, submissionID, gameID, user
submissionID,
int(row.TestcaseID),
language,
- code,
- row.Stdin,
+ normalizeCRLF(code),
+ normalizeCRLF(row.Stdin),
row.Stdout,
)
if err != nil {
@@ -175,9 +175,12 @@ func (hub *Hub) processTaskResultRunTestcase(
return nil
}
+func normalizeCRLF(s string) string {
+ return strings.ReplaceAll(strings.ReplaceAll(s, "\r\n", "\n"), "\r", "\n")
+}
+
func normalizeTestcaseResultOutput(s string) string {
- re := regexp.MustCompile(`\r\n|\r`)
- return re.ReplaceAllString(strings.TrimSpace(s), "\n")
+ return normalizeCRLF(strings.TrimSpace(s))
}
func isTestcaseResultCorrect(expectedStdout, actualStdout string) bool {
diff --git a/backend/game/hub_test.go b/backend/game/hub_test.go
index 5aa440b..1d493b9 100644
--- a/backend/game/hub_test.go
+++ b/backend/game/hub_test.go
@@ -249,6 +249,59 @@ func TestNormalizeTestcaseResultOutput(t *testing.T) {
}
}
+func TestNormalizeCRLF(t *testing.T) {
+ tests := []struct {
+ name string
+ input string
+ want string
+ }{
+ {"CRLF to LF", "hello\r\nworld", "hello\nworld"},
+ {"CR to LF", "hello\rworld", "hello\nworld"},
+ {"LF unchanged", "hello\nworld", "hello\nworld"},
+ {"lone CRLF", "\r\n", "\n"},
+ {"empty string", "", ""},
+ {"multiple CRLF", "a\r\nb\r\nc", "a\nb\nc"},
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ got := normalizeCRLF(tt.input)
+ if got != tt.want {
+ t.Errorf("normalizeCRLF(%q) = %q, want %q", tt.input, got, tt.want)
+ }
+ })
+ }
+}
+
+func TestEnqueueTestTasks_NormalizesCRLF(t *testing.T) {
+ testcases := []db.Testcase{
+ {TestcaseID: 1, ProblemID: 10, Stdin: "input\r\n", Stdout: "output"},
+ }
+
+ tq := &mockTaskQueue{}
+ mq := &mockQuerier{
+ listTestcasesByGameIDFunc: func(_ context.Context, _ int32) ([]db.Testcase, error) {
+ return testcases, nil
+ },
+ }
+
+ hub := &Hub{q: mq, taskQueue: tq, ctx: context.Background()}
+
+ err := hub.EnqueueTestTasks(context.Background(), 100, 1, 42, "php", "<?php\r\necho 1;")
+ if err != nil {
+ t.Fatalf("unexpected error: %v", err)
+ }
+
+ if len(tq.enqueued) != 1 {
+ t.Fatalf("expected 1 enqueued task, got %d", len(tq.enqueued))
+ }
+ if tq.enqueued[0].Stdin != "input\n" {
+ t.Errorf("expected stdin CRLF normalized, got %q", tq.enqueued[0].Stdin)
+ }
+ if tq.enqueued[0].Code != "<?php\necho 1;" {
+ t.Errorf("expected code CRLF normalized, got %q", tq.enqueued[0].Code)
+ }
+}
+
func TestCalcCodeSize_PHP(t *testing.T) {
hub := &Hub{}
tests := []struct {