// 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" "io" "strings" "testing" ) func TestLineReader(t *testing.T) { type nextLine struct { line string incomplete bool // next call before this line should return incomplete } complete := func(s string) nextLine { return nextLine{line: s} } incomplete := func(s string) nextLine { return nextLine{line: s, incomplete: true} } const scratchSize = 8 tests := []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"), }, }, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { r := strings.NewReader(tc.contents) read := func(fd int, b []byte) (int, uintptr) { n, err := r.Read(b) if err != nil && err != io.EOF { const dummyErrno = 42 return n, dummyErrno } return n, 0 } var scratch [scratchSize]byte l := cgroup.NewLineReader(0, scratch[:], read) 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) } } }) } }