After commit c018f5fd29 Fenrir is being switched over to go. If you do not want to be part of the testing process, please switch to master.
This commit is contained in:
1 parent
c018f5fd29
commit
0e50de4c76
1047 files changed
+58364
-63747
No files matched your search
@@ -0,0 +1,213 @@
|
||||
// Copyright (c) 2026 Storm Dragon
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
package review
|
||||
|
||||
import (
|
||||
"git.stormux.org/storm/fenrir/internal/command"
|
||||
"git.stormux.org/storm/fenrir/internal/presentation"
|
||||
"git.stormux.org/storm/fenrir/internal/screen"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestCharacterCrossesPhysicalRowsAndKeepsCharacterMetadata(t *testing.T) {
|
||||
for _, feedback := range []bool{false, true} {
|
||||
var r Review
|
||||
r.Configure(feedback, feedback, true)
|
||||
s := screen.Snapshot{Active: true, Rows: 3, Cols: 3, Cells: []rune("abÉ 界cd"), X: 2}
|
||||
r.Update(s)
|
||||
for _, trial := range []struct {
|
||||
id command.ID
|
||||
text string
|
||||
x, y int
|
||||
crossing bool
|
||||
}{
|
||||
{command.NextChar, "space", 0, 1, true},
|
||||
{command.PreviousChar, "É", 2, 0, true},
|
||||
{command.NextChar, "space", 0, 1, true},
|
||||
{command.NextChar, "space", 1, 1, false},
|
||||
{command.NextChar, "space", 2, 1, false},
|
||||
{command.NextChar, "界", 0, 2, true},
|
||||
} {
|
||||
result := r.Execute(trial.id)
|
||||
if got := result.Text; got != trial.text || r.x != trial.x || r.y != trial.y || result.Cue != "" {
|
||||
t.Fatalf("%s: %q at %d,%d", trial.id, got, r.x, r.y)
|
||||
}
|
||||
if result.Character == nil {
|
||||
t.Fatal("lost character metadata")
|
||||
}
|
||||
if (result.After.Text == "line break") != (feedback && trial.crossing) || (result.After.Cue == presentation.LineEnd) != (feedback && trial.crossing) {
|
||||
t.Fatal(result.After.Text, result.After.Cue)
|
||||
}
|
||||
}
|
||||
r.Execute(command.ScreenLastChar)
|
||||
result := r.Execute(command.NextChar)
|
||||
text := result.Text
|
||||
if feedback && (text != "end of screen" || result.Cue != presentation.Bottom) || !feedback && (text != "" || result.Cue != "") {
|
||||
t.Fatal(text, result.Cue)
|
||||
}
|
||||
if r.x != 2 || r.y != 2 || result.After.Text != "" {
|
||||
t.Fatal("screen edge moved cursor or retained line break")
|
||||
}
|
||||
r.Execute(command.Top)
|
||||
text = r.Execute(command.PreviousChar).Text
|
||||
if feedback && text != "start of screen" || !feedback && text != "" {
|
||||
t.Fatal(text)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestWordSelectionFromInsideAndBetweenWords(t *testing.T) {
|
||||
for _, trial := range []struct {
|
||||
x, y int
|
||||
id command.ID
|
||||
text string
|
||||
targetX, targetY int
|
||||
crossing bool
|
||||
}{
|
||||
{4, 0, command.CurrentWord, "alpha", 2, 0, false},
|
||||
{4, 0, command.PreviousWord, "start of screen", 4, 0, false},
|
||||
{9, 0, command.PreviousWord, "alpha", 2, 0, false},
|
||||
{8, 0, command.CurrentWord, "alpha", 2, 0, false},
|
||||
{8, 0, command.NextWord, "beta!", 9, 0, false},
|
||||
{0, 1, command.CurrentWord, "beta!", 9, 0, true},
|
||||
{0, 1, command.PreviousWord, "alpha", 2, 0, true},
|
||||
{9, 0, command.NextWord, "界é", 1, 2, true},
|
||||
{1, 2, command.PreviousWord, "beta!", 9, 0, true},
|
||||
{0, 0, command.CurrentWord, "start of screen", 0, 0, false},
|
||||
{8, 2, command.NextWord, "end of screen", 8, 2, false},
|
||||
} {
|
||||
var r Review
|
||||
s := fixture(" alpha beta!", "", " 界é last")
|
||||
s.X, s.Y = trial.x, trial.y
|
||||
r.Update(s)
|
||||
result := r.Execute(trial.id)
|
||||
got := result.Text
|
||||
if got != trial.text || r.x != trial.targetX || r.y != trial.targetY || (result.After.Text == "line break") != trial.crossing {
|
||||
t.Fatalf("%+v: %q at %d,%d after=%q", trial, got, r.x, r.y, result.After.Text)
|
||||
}
|
||||
if s.X != r.screen.X || s.Y != r.screen.Y {
|
||||
t.Fatal("moved application cursor")
|
||||
}
|
||||
}
|
||||
var r Review
|
||||
r.Update(fixture("", ""))
|
||||
for _, id := range []command.ID{command.CurrentWord, command.PreviousWord, command.NextWord} {
|
||||
result := r.Execute(id)
|
||||
if got := result.Text; got != "blank" || result.Cue != presentation.Blank {
|
||||
t.Fatal(id, got, result.Cue)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestReviewLineEndpointsAndTopBottom(t *testing.T) {
|
||||
var r Review
|
||||
s := fixture(" Éx!", "\t\u2003", "last")
|
||||
s.X = 3
|
||||
r.Update(s)
|
||||
for _, trial := range []struct {
|
||||
id command.ID
|
||||
text, after string
|
||||
x, y int
|
||||
}{
|
||||
{command.FirstText, "É", "first character in line indent 2", 2, 0},
|
||||
{command.LastText, "bang", "last character in line", 4, 0},
|
||||
{command.NextLine, "\t\u2003", "", 4, 1},
|
||||
{command.FirstText, "line is empty", "", 4, 1},
|
||||
{command.LastText, "line is empty", "", 4, 1},
|
||||
{command.LineStart, "blank", "beginning of line", 0, 1},
|
||||
{command.LineEnd, "space", "end of line", 19, 1},
|
||||
{command.CurrentLine, "\t\u2003", "", 0, 1},
|
||||
{command.Bottom, "Bottom", "", 0, 2},
|
||||
{command.NextChar, "a", "", 1, 2},
|
||||
{command.Top, "Top", "", 0, 0},
|
||||
} {
|
||||
// Whitespace-only row content is presented as blank, including Unicode.
|
||||
want := trial.text
|
||||
if trial.id == command.NextLine || trial.id == command.CurrentLine {
|
||||
want = "blank"
|
||||
}
|
||||
result := r.Execute(trial.id)
|
||||
if got := result.Text; got != want || result.After.Text != trial.after || r.x != trial.x || r.y != trial.y {
|
||||
t.Fatalf("%s: %q after=%q at %d,%d", trial.id, got, result.After.Text, r.x, r.y)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestTableBoundaryContextAndModeSynchronization(t *testing.T) {
|
||||
var r Review
|
||||
r.Update(fixture("Name|Subject|Empty", "José|É!|"))
|
||||
r.Set_table_mode(true)
|
||||
r.Execute(command.CurrentWord)
|
||||
r.Set_mark()
|
||||
r.Execute(command.NextLine)
|
||||
for _, trial := range []struct {
|
||||
id command.ID
|
||||
text, after string
|
||||
cue presentation.Cue
|
||||
}{
|
||||
{command.NextWord, "É! Subject", "", ""},
|
||||
{command.PreviousChar, "É", "start of cell", presentation.LineStart},
|
||||
{command.NextChar, "bang", "", ""},
|
||||
{command.NextChar, "bang", "end of cell", presentation.LineEnd},
|
||||
{command.NextWord, "blank Empty", "", ""},
|
||||
{command.NextChar, "blank", "end of cell", presentation.LineEnd},
|
||||
{command.NextWord, "blank Empty", "end of line", presentation.LineEnd},
|
||||
{command.LineStart, "José Name", "first cell", ""},
|
||||
{command.PreviousWord, "José Name", "start of line", presentation.LineStart},
|
||||
{command.LineEnd, "blank Empty", "last cell", ""},
|
||||
} {
|
||||
result := r.Execute(trial.id)
|
||||
if got := result.Text; got != trial.text || result.After.Text != trial.after || result.After.Cue != trial.cue {
|
||||
t.Fatalf("%s: %q after=%q cue=%q", trial.id, got, result.After.Text, result.After.Cue)
|
||||
}
|
||||
}
|
||||
r.Execute(command.Top)
|
||||
if got := r.Execute(command.CurrentWord).Text; got != "Name Name" {
|
||||
t.Fatal("Top kept old table column", got)
|
||||
}
|
||||
r.Execute(command.LineEnd)
|
||||
r.Execute(command.Bottom)
|
||||
if got := r.Execute(command.CurrentWord).Text; got != "José Name" {
|
||||
t.Fatal("Bottom kept old table column", got)
|
||||
}
|
||||
r.Execute(command.FollowCursor)
|
||||
r.Set_table_mode(false)
|
||||
r.Execute(command.LineEnd)
|
||||
r.Set_table_mode(true)
|
||||
if got := r.Execute(command.CurrentWord).Text; got != "Empty Column 3" {
|
||||
t.Fatal("mode change did not resynchronize", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestReviewScreenChangePolicyAndClamping(t *testing.T) {
|
||||
for _, leave := range []bool{false, true} {
|
||||
var r Review
|
||||
r.Configure(true, true, leave)
|
||||
s := fixture("header|value", "data|cell")
|
||||
r.Update(s)
|
||||
r.Set_table_mode(true)
|
||||
r.Execute(command.CurrentWord)
|
||||
r.Set_mark()
|
||||
r.Execute(command.Bottom)
|
||||
r.Execute(command.LineEnd)
|
||||
s.Terminal++
|
||||
s.Generation++
|
||||
r.Update(s)
|
||||
if r.Active() == leave || len(r.tableHeaders) != 0 {
|
||||
t.Fatal("wrong review/header ownership", r.Active())
|
||||
}
|
||||
if leave && (r.x != 0 || r.y != 0) || !leave && (r.x != 5 || r.y != 1) {
|
||||
t.Fatal(r.x, r.y)
|
||||
}
|
||||
// A resized screen keeps the anchor only when explicitly requested,
|
||||
// and bounds it before any action can index cells.
|
||||
r.Execute(command.ScreenLastChar)
|
||||
r.Update(fixture("x"))
|
||||
r.Execute(command.CurrentChar)
|
||||
r.Update(screen.Snapshot{})
|
||||
if r.Active() {
|
||||
t.Fatal("inactive session retained review")
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user