79 lines
2.9 KiB
Go
79 lines
2.9 KiB
Go
// Copyright (c) 2026 Storm Dragon
|
|
// Authors: Storm Dragon, Angus Kola
|
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
|
|
package review
|
|
|
|
import (
|
|
"context"
|
|
"strings"
|
|
"testing"
|
|
|
|
"git.stormux.org/storm/cthulhu/internal/atspi"
|
|
)
|
|
|
|
type blockCaretReader struct {
|
|
caretTextReader
|
|
calls, blocks int
|
|
malformed bool
|
|
}
|
|
|
|
func (reader *blockCaretReader) ReadDocumentCaret(ctx context.Context, object atspi.ObjectReference, offset int, unit atspi.DocumentCaretUnit) (atspi.DocumentCaretText, error) {
|
|
reader.calls++
|
|
return reader.caretTextReader.ReadDocumentCaret(ctx, object, offset, unit)
|
|
}
|
|
|
|
func (reader *blockCaretReader) ReadDocumentCharacters(_ context.Context, object atspi.ObjectReference, offset int) (atspi.DocumentCaretText, error) {
|
|
reader.calls++
|
|
reader.blocks++
|
|
runes := []rune(reader.caretTextReader[object.ID()])
|
|
start := offset / 128 * 128
|
|
end := min(len(runes), start+128)
|
|
raw := string(runes[start:end])
|
|
if reader.malformed {
|
|
raw = ""
|
|
}
|
|
return atspi.DocumentCaretText{Start: start, End: end, RawText: raw, CharacterCount: len(runes)}, nil
|
|
}
|
|
|
|
func TestDocumentWordSpacesUseBoundedBlocks(t *testing.T) {
|
|
values := caretTextReader{webID("/text"): "A" + strings.Repeat(" \t\n\u2003", 250) + "猫"}
|
|
cursor := newCaretTextCursor(t, values)
|
|
reader := &blockCaretReader{caretTextReader: values}
|
|
for _, step := range []struct {
|
|
command DocumentCaretCommand
|
|
text string
|
|
}{{CaretNextWord, "猫"}, {CaretPreviousWord, "猫"}, {CaretPreviousWord, "A \t\n\u2003"}} {
|
|
move, err := cursor.MoveCaret(context.Background(), reader, step.command)
|
|
// Native word providers include varying trailing whitespace. Retain
|
|
// their text and verify the actual requested word and Unicode offset.
|
|
if err != nil || !strings.HasPrefix(move.Text, strings.TrimSpace(step.text)) {
|
|
t.Fatalf("%s: %+v error=%v", step.command, move, err)
|
|
}
|
|
}
|
|
t.Logf("three word commands across 1000 whitespace characters: %d reads, %d blocks", reader.calls, reader.blocks)
|
|
if reader.blocks == 0 || reader.calls > 50 {
|
|
t.Fatalf("word traversal used %d reads (%d blocks)", reader.calls, reader.blocks)
|
|
}
|
|
}
|
|
|
|
func TestDocumentCharacterMovementDoesNotReadBlocks(t *testing.T) {
|
|
values := caretTextReader{webID("/text"): "A B"}
|
|
cursor := newCaretTextCursor(t, values)
|
|
reader := &blockCaretReader{caretTextReader: values}
|
|
move, err := cursor.MoveCaret(context.Background(), reader, CaretNextCharacter)
|
|
if err != nil || move.Text != " " || reader.blocks != 0 || reader.calls != 1 {
|
|
t.Fatalf("move=%+v error=%v calls=%d blocks=%d", move, err, reader.calls, reader.blocks)
|
|
}
|
|
}
|
|
|
|
func TestDocumentWordMalformedBlockCannotSkipText(t *testing.T) {
|
|
values := caretTextReader{webID("/text"): "A B"}
|
|
cursor := newCaretTextCursor(t, values)
|
|
reader := &blockCaretReader{caretTextReader: values, malformed: true}
|
|
move, err := cursor.MoveCaret(context.Background(), reader, CaretNextWord)
|
|
if err != nil || move.Text != "B" || move.Position.Offset != 4 {
|
|
t.Fatalf("move=%+v error=%v", move, err)
|
|
}
|
|
}
|