package reader import ( "reflect" "testing" "time" ) func TestORPIndex(t *testing.T) { tests := []struct { name string word string expected int }{ {"empty", "", 0}, {"one character", "a", 0}, {"two characters", "go", 1}, {"three characters", "cat", 1}, {"five characters", "hello", 1}, {"six characters", "reader", 2}, {"nine characters", "wonderful", 2}, {"ten characters", "technology", 3}, {"long word", "extraordinary", 3}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got := ORPIndex([]rune(tt.word)) if got != tt.expected { t.Errorf("ORPIndex(%q) = %d, want %d", tt.word, got, tt.expected) } }) } } func TestSplitWord(t *testing.T) { tests := []struct { name string input string expected Word }{ { name: "normal word", input: "hello", expected: Word{ Text: "hello", Prefix: "h", Focus: "e", Suffix: "llo", }, }, { name: "unicode word", input: "café", expected: Word{ Text: "café", Prefix: "c", Focus: "a", Suffix: "fé", }, }, { name: "emoji", input: "🙂hello", expected: Word{ Text: "🙂hello", Prefix: "🙂h", Focus: "e", Suffix: "llo", }, }, { name: "empty word", input: "", expected: Word{}, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got := SplitWord(tt.input) if !reflect.DeepEqual(got, tt.expected) { t.Errorf("SplitWord(%q) = %+v, want %+v", tt.input, got, tt.expected) } }) } } func TestTokenize(t *testing.T) { tests := []struct { name string input string expectedCount int }{ {"normal text", "Hello world", 2}, {"multiple spaces", "Hello world", 2}, {"newlines", "Hello\nworld", 2}, {"tabs", "Hello\tworld", 2}, {"mixed whitespace", " Hello\n world\tagain ", 3}, {"empty input", "", 0}, {"whitespace only", " \n\t ", 0}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got := Tokenize(tt.input) if len(got) != tt.expectedCount { t.Errorf("Tokenize(%q) returned %d words, want %d", tt.input, len(got), tt.expectedCount) } }) } } func TestTokenizePreservesWords(t *testing.T) { input := "Hello, world!" words := Tokenize(input) expected := []string{"Hello,", "world!"} for i, word := range words { if word.Text != expected[i] { t.Errorf("word %d = %q, want %q", i, word.Text, expected[i]) } } } func TestIsSentenceEnding(t *testing.T) { tests := []struct { word string expected bool }{ {"hello.", true}, {"hello!", true}, {"hello?", true}, {"hello…", true}, {`hello."`, true}, {"hello!)", true}, {"hello", false}, {"hello,", false}, {"", false}, } for _, tt := range tests { t.Run(tt.word, func(t *testing.T) { got := IsSentenceEnding(tt.word) if got != tt.expected { t.Errorf("IsSentenceEnding(%q) = %t, want %t", tt.word, got, tt.expected) } }) } } func TestIsPausePunctuation(t *testing.T) { tests := []struct { word string expected bool }{ {"hello,", true}, {"hello;", true}, {"hello:", true}, {`hello,"`, true}, {"hello", false}, {"hello.", false}, {"", false}, } for _, tt := range tests { t.Run(tt.word, func(t *testing.T) { got := IsPausePunctuation(tt.word) if got != tt.expected { t.Errorf("IsPausePunctuation(%q) = %t, want %t", tt.word, got, tt.expected) } }) } } func TestWordDuration(t *testing.T) { tests := []struct { name string word string wpm int expected time.Duration }{ { name: "normal word", word: "hello", wpm: 300, expected: 200 * time.Millisecond, }, { name: "sentence ending", word: "hello.", wpm: 300, expected: 500 * time.Millisecond, }, { name: "comma pause", word: "hello,", wpm: 300, expected: 350 * time.Millisecond, }, { name: "long word", word: "extraordinary", wpm: 300, expected: 250 * time.Millisecond, }, { name: "sentence ending and long word", word: "extraordinary.", wpm: 300, expected: 550 * time.Millisecond, }, { name: "minimum WPM clamp", word: "hello", wpm: 50, expected: 600 * time.Millisecond, }, { name: "maximum WPM clamp", word: "hello", wpm: 1500, expected: 60 * time.Millisecond, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got := WordDuration(tt.word, tt.wpm) if got != tt.expected { t.Errorf("WordDuration(%q, %d) = %v, want %v", tt.word, tt.wpm, got, tt.expected) } }) } } func TestRuneLength(t *testing.T) { tests := []struct { word string expected int }{ {"hello", 5}, {"café", 4}, {"🙂", 1}, {"🙂hello", 6}, {"", 0}, } for _, tt := range tests { t.Run(tt.word, func(t *testing.T) { got := RuneLength(tt.word) if got != tt.expected { t.Errorf("RuneLength(%q) = %d, want %d", tt.word, got, tt.expected) } }) } }