diff options
| author | nsfisis <nsfisis@gmail.com> | 2026-02-28 17:00:35 +0900 |
|---|---|---|
| committer | nsfisis <nsfisis@gmail.com> | 2026-02-28 17:00:35 +0900 |
| commit | b15f06ece6a9dac01a06688be39e437cfa8ed257 (patch) | |
| tree | 2a713f2079d7055f5777c84db29f9c25bf867e33 | |
| parent | 10f0cd9dbc408b171fbd6a96ed1b552407046b13 (diff) | |
| download | phperkaigi-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.
| -rw-r--r-- | backend/game/hub.go | 11 | ||||
| -rw-r--r-- | backend/game/hub_test.go | 53 |
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 { |
