Source file src/internal/runtime/cgroup/line_reader_test.go

     1  // Copyright 2025 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package cgroup_test
     6  
     7  import (
     8  	"internal/runtime/cgroup"
     9  	"io"
    10  	"strings"
    11  	"testing"
    12  )
    13  
    14  func TestLineReader(t *testing.T) {
    15  	type nextLine struct {
    16  		line       string
    17  		incomplete bool // next call before this line should return incomplete
    18  	}
    19  	complete := func(s string) nextLine {
    20  		return nextLine{line: s}
    21  	}
    22  	incomplete := func(s string) nextLine {
    23  		return nextLine{line: s, incomplete: true}
    24  	}
    25  
    26  	const scratchSize = 8
    27  
    28  	tests := []struct {
    29  		name     string
    30  		contents string
    31  		want     []nextLine
    32  	}{
    33  		{
    34  			name:     "empty",
    35  			contents: "",
    36  		},
    37  		{
    38  			name:     "single",
    39  			contents: "1234\n",
    40  			want: []nextLine{
    41  				complete("1234"),
    42  			},
    43  		},
    44  		{
    45  			name:     "single-incomplete",
    46  			contents: "1234",
    47  			want: []nextLine{
    48  				incomplete("1234"),
    49  			},
    50  		},
    51  		{
    52  			name:     "single-exact",
    53  			contents: "1234567\n",
    54  			want: []nextLine{
    55  				complete("1234567"),
    56  			},
    57  		},
    58  		{
    59  			name:     "single-exact-incomplete",
    60  			contents: "12345678",
    61  			want: []nextLine{
    62  				incomplete("12345678"),
    63  			},
    64  		},
    65  		{
    66  			name: "multi",
    67  			contents: `1234
    68  5678
    69  `,
    70  			want: []nextLine{
    71  				complete("1234"),
    72  				complete("5678"),
    73  			},
    74  		},
    75  		{
    76  			name: "multi-short",
    77  			contents: `12
    78  34
    79  56
    80  78
    81  `,
    82  			want: []nextLine{
    83  				complete("12"),
    84  				complete("34"),
    85  				complete("56"),
    86  				complete("78"),
    87  			},
    88  		},
    89  		{
    90  			name: "multi-notrailingnewline",
    91  			contents: `1234
    92  5678`,
    93  			want: []nextLine{
    94  				complete("1234"),
    95  				incomplete("5678"),
    96  			},
    97  		},
    98  		{
    99  			name: "middle-too-long",
   100  			contents: `1234
   101  1234567890
   102  5678
   103  `,
   104  			want: []nextLine{
   105  				complete("1234"),
   106  				incomplete("12345678"),
   107  				complete("5678"),
   108  			},
   109  		},
   110  		{
   111  			// Multiple reads required to find newline.
   112  			name: "middle-way-too-long",
   113  			contents: `1234
   114  12345678900000000000000000000000000000000000000000000000000
   115  5678
   116  `,
   117  			want: []nextLine{
   118  				complete("1234"),
   119  				incomplete("12345678"),
   120  				complete("5678"),
   121  			},
   122  		},
   123  	}
   124  
   125  	for _, tc := range tests {
   126  		t.Run(tc.name, func(t *testing.T) {
   127  			r := strings.NewReader(tc.contents)
   128  			read := func(fd int, b []byte) (int, uintptr) {
   129  				n, err := r.Read(b)
   130  				if err != nil && err != io.EOF {
   131  					const dummyErrno = 42
   132  					return n, dummyErrno
   133  				}
   134  				return n, 0
   135  			}
   136  
   137  			var scratch [scratchSize]byte
   138  			l := cgroup.NewLineReader(0, scratch[:], read)
   139  
   140  			var got []nextLine
   141  			for {
   142  				err := l.Next()
   143  				if err == cgroup.ErrEOF {
   144  					break
   145  				} else if err == cgroup.ErrIncompleteLine {
   146  					got = append(got, incomplete(string(l.Line())))
   147  				} else if err != nil {
   148  					t.Fatalf("next got err %v", err)
   149  				} else {
   150  					got = append(got, complete(string(l.Line())))
   151  				}
   152  			}
   153  
   154  			if len(got) != len(tc.want) {
   155  				t.Logf("got lines %+v", got)
   156  				t.Logf("want lines %+v", tc.want)
   157  				t.Fatalf("lineReader got %d lines, want %d", len(got), len(tc.want))
   158  			}
   159  
   160  			for i := range got {
   161  				if got[i].line != tc.want[i].line {
   162  					t.Errorf("line %d got %q want %q", i, got[i].line, tc.want[i].line)
   163  				}
   164  				if got[i].incomplete != tc.want[i].incomplete {
   165  					t.Errorf("line %d got incomplete %v want %v", i, got[i].incomplete, tc.want[i].incomplete)
   166  				}
   167  			}
   168  		})
   169  	}
   170  }
   171  

View as plain text