| // Copyright 2025 The Go Authors. All rights reserved. | |
| // Use of this source code is governed by a BSD-style | |
| // license that can be found in the LICENSE file. | |
| package cgroup_test | |
| import ( | |
| "internal/runtime/cgroup" | |
| "strings" | |
| "testing" | |
| ) | |
| type nextLine struct { | |
| line string | |
| incomplete bool // next call before this line should return incomplete | |
| } | |
| func complete(s string) nextLine { | |
| return nextLine{line: s} | |
| } | |
| func incomplete(s string) nextLine { | |
| return nextLine{line: s, incomplete: true} | |
| } | |
| const scratchSize = 8 | |
| var readerTests = []struct { | |
| name string | |
| contents string | |
| want []nextLine | |
| }{ | |
| { | |
| name: "empty", | |
| contents: "", | |
| }, | |
| { | |
| name: "single", | |
| contents: "1234\n", | |
| want: []nextLine{ | |
| complete("1234"), | |
| }, | |
| }, | |
| { | |
| name: "single-incomplete", | |
| contents: "1234", | |
| want: []nextLine{ | |
| incomplete("1234"), | |
| }, | |
| }, | |
| { | |
| name: "single-exact", | |
| contents: "1234567\n", | |
| want: []nextLine{ | |
| complete("1234567"), | |
| }, | |
| }, | |
| { | |
| name: "single-exact-incomplete", | |
| contents: "12345678", | |
| want: []nextLine{ | |
| incomplete("12345678"), | |
| }, | |
| }, | |
| { | |
| name: "multi", | |
| contents: `1234 | |
| 5678 | |
| `, | |
| want: []nextLine{ | |
| complete("1234"), | |
| complete("5678"), | |
| }, | |
| }, | |
| { | |
| name: "multi-short", | |
| contents: `12 | |
| 34 | |
| 56 | |
| 78 | |
| `, | |
| want: []nextLine{ | |
| complete("12"), | |
| complete("34"), | |
| complete("56"), | |
| complete("78"), | |
| }, | |
| }, | |
| { | |
| name: "multi-notrailingnewline", | |
| contents: `1234 | |
| 5678`, | |
| want: []nextLine{ | |
| complete("1234"), | |
| incomplete("5678"), | |
| }, | |
| }, | |
| { | |
| name: "middle-too-long", | |
| contents: `1234 | |
| 1234567890 | |
| 5678 | |
| `, | |
| want: []nextLine{ | |
| complete("1234"), | |
| incomplete("12345678"), | |
| complete("5678"), | |
| }, | |
| }, | |
| { | |
| // Multiple reads required to find newline. | |
| name: "middle-way-too-long", | |
| contents: `1234 | |
| 12345678900000000000000000000000000000000000000000000000000 | |
| 5678 | |
| `, | |
| want: []nextLine{ | |
| complete("1234"), | |
| incomplete("12345678"), | |
| complete("5678"), | |
| }, | |
| }, | |
| } | |
| func TestLineReader(t *testing.T) { | |
| for _, tc := range readerTests { | |
| t.Run(tc.name, func(t *testing.T) { | |
| var scratch [scratchSize]byte | |
| l := cgroup.NewLineReader(0, scratch[:], readString(tc.contents)) | |
| var got []nextLine | |
| for { | |
| err := l.Next() | |
| if err == cgroup.ErrEOF { | |
| break | |
| } else if err == cgroup.ErrIncompleteLine { | |
| got = append(got, incomplete(string(l.Line()))) | |
| } else if err != nil { | |
| t.Fatalf("next got err %v", err) | |
| } else { | |
| got = append(got, complete(string(l.Line()))) | |
| } | |
| } | |
| if len(got) != len(tc.want) { | |
| t.Logf("got lines %+v", got) | |
| t.Logf("want lines %+v", tc.want) | |
| t.Fatalf("lineReader got %d lines, want %d", len(got), len(tc.want)) | |
| } | |
| for i := range got { | |
| if got[i].line != tc.want[i].line { | |
| t.Errorf("line %d got %q want %q", i, got[i].line, tc.want[i].line) | |
| } | |
| if got[i].incomplete != tc.want[i].incomplete { | |
| t.Errorf("line %d got incomplete %v want %v", i, got[i].incomplete, tc.want[i].incomplete) | |
| } | |
| } | |
| }) | |
| } | |
| } | |
| func FuzzLineReader(f *testing.F) { | |
| for _, tc := range readerTests { | |
| f.Add(tc.contents) | |
| } | |
| f.Fuzz(func(t *testing.T, input string) { | |
| scratch := make([]byte, scratchSize) | |
| reader := cgroup.NewLineReader(0, scratch, readString(input)) | |
| for expected := range strings.Lines(input) { | |
| err := reader.Next() | |
| line := reader.Line() | |
| var expectedErr error | |
| if len(expected) > scratchSize { | |
| expected = expected[:scratchSize] | |
| expectedErr = cgroup.ErrIncompleteLine | |
| } else if expected[len(expected)-1] == '\n' { | |
| expected = expected[:len(expected)-1] | |
| } else { | |
| expectedErr = cgroup.ErrIncompleteLine | |
| } | |
| if err != expectedErr { | |
| t.Fatalf("got err %v, want %v", err, expectedErr) | |
| } | |
| if string(line) != expected { | |
| t.Fatalf("got %q, want %q", string(line), expected) | |
| } | |
| } | |
| err := reader.Next() | |
| if err != cgroup.ErrEOF { | |
| t.Fatalf("got %v, want EOF", err) | |
| } | |
| }) | |
| } |