// Fenrir console screen reader. // Copyright (c) 2026 Storm Dragon // SPDX-License-Identifier: GPL-3.0-or-later // // This program is free software: you can redistribute it and/or modify it // under the terms of the GNU General Public License as published by the Free // Software Foundation, either version 3 of the License, or (at your option) // any later version. // // This program is distributed in the hope that it will be useful, but WITHOUT // ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or // FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for // more details. // // You should have received a copy of the GNU General Public License along with // this program. If not, see . //go:build linux && cgo package console import ( "bytes" "errors" "io" "testing" ) type unicodeReader struct { *bytes.Reader requests []int limit int } func (r *unicodeReader) Read(data []byte) (int, error) { r.requests = append(r.requests, len(data)) if r.limit > 0 && len(data) > r.limit { data = data[:r.limit] } return r.Reader.Read(data) } func TestUnicodeReadBoundedAlignedAndComplete(t *testing.T) { const cells = 80 * 25 for _, size := range []int{0, 4, cells*4 - 4, cells * 4, cells*4 + 4, cells * 8} { for _, limit := range []int{0, 128} { reader := &unicodeReader{Reader: bytes.NewReader(make([]byte, size)), limit: limit} data, err := read_unicode(reader, cells) if err != nil || len(data) != min(size, (cells+1)*4) { t.Fatalf("size %d limit %d: got %d bytes, %v", size, limit, len(data), err) } for _, request := range reader.requests { if request%4 != 0 { t.Fatalf("unaligned request: %d", request) } } if limit == 0 && len(reader.requests) > 2 { t.Fatalf("unnecessary reads: %v", reader.requests) } } } } type failedReader struct{} func (failedReader) Read([]byte) (int, error) { return 0, io.ErrClosedPipe } func TestUnicodeReadPreservesIOError(t *testing.T) { if _, err := read_unicode(failedReader{}, 80*25); !errors.Is(err, io.ErrClosedPipe) { t.Fatalf("lost I/O error: %v", err) } }