The tree was formatted with an older gofmt; go1.27's gofmt additionally wants: EOF exactly one newline (no trailing blank lines), imports sorted alphabetically within a block, mixed-precedence binary expressions re-spaced for grouping ((a+b)/c), single-field composite literals un-aligned, adjacent one-line method signatures aligned, and one-line bodies containing a compound statement expanded. Applied repo-wide (31 files under internal/); pure formatting, no semantic changes — build and the full test suite pass.
265 lines
7.0 KiB
Go
265 lines
7.0 KiB
Go
package browser
|
|
|
|
import (
|
|
"testing"
|
|
)
|
|
|
|
// --- Test: Per-Pixel Scroll Delta Applied Correctly ---
|
|
|
|
func TestPixelScroll_DeltaApplied(t *testing.T) {
|
|
s := NewBrowserState()
|
|
s.TotalEntries = 1000
|
|
s.VisibleCount = 20
|
|
s.EntryHeight = 48.0 // pixels per entry
|
|
s.ScrollOffset = 0
|
|
|
|
// Scroll down by 100 pixels (should be precise, not rounded to entries)
|
|
HandlePixelScroll(s, 100)
|
|
|
|
// ScrollOffset should be 100.0 (exact pixel delta)
|
|
expected := 100.0
|
|
if s.ScrollOffset != expected {
|
|
t.Errorf("expected ScrollOffset=%.1f, got %.1f", expected, s.ScrollOffset)
|
|
}
|
|
|
|
// Scroll up by 50 pixels
|
|
HandlePixelScroll(s, -50)
|
|
|
|
expected = 50.0
|
|
if s.ScrollOffset != expected {
|
|
t.Errorf("expected ScrollOffset=%.1f, got %.1f", expected, s.ScrollOffset)
|
|
}
|
|
}
|
|
|
|
// --- Test: Per-Pixel Scroll Clamped to Minimum ---
|
|
|
|
func TestPixelScroll_ClampMinimum(t *testing.T) {
|
|
s := NewBrowserState()
|
|
s.TotalEntries = 1000
|
|
s.VisibleCount = 20
|
|
s.EntryHeight = 48.0
|
|
s.ScrollOffset = 0
|
|
|
|
// Try to scroll above the top
|
|
HandlePixelScroll(s, -100)
|
|
|
|
// ScrollOffset should be clamped to 0
|
|
if s.ScrollOffset != 0 {
|
|
t.Errorf("expected ScrollOffset=0 (clamped), got %.1f", s.ScrollOffset)
|
|
}
|
|
}
|
|
|
|
// --- Test: Per-Pixel Scroll Clamped to Maximum ---
|
|
|
|
func TestPixelScroll_ClampMaximum(t *testing.T) {
|
|
s := NewBrowserState()
|
|
s.TotalEntries = 1000
|
|
s.VisibleCount = 20
|
|
s.EntryHeight = 48.0
|
|
s.ScrollOffset = 0
|
|
|
|
// Maximum scroll = (TotalEntries - VisibleCount) * EntryHeight
|
|
maxScroll := float64(s.TotalEntries-s.VisibleCount) * s.EntryHeight
|
|
|
|
// Try to scroll past the bottom
|
|
HandlePixelScroll(s, int(maxScroll)+1000)
|
|
|
|
// ScrollOffset should be clamped to max
|
|
if s.ScrollOffset != maxScroll {
|
|
t.Errorf("expected ScrollOffset=%.1f (clamped to max), got %.1f", maxScroll, s.ScrollOffset)
|
|
}
|
|
}
|
|
|
|
// --- Test: Per-Pixel Scroll Preserves Sub-Entry Precision ---
|
|
|
|
func TestPixelScroll_SubEntryPrecision(t *testing.T) {
|
|
s := NewBrowserState()
|
|
s.TotalEntries = 1000
|
|
s.VisibleCount = 20
|
|
s.EntryHeight = 48.0
|
|
s.ScrollOffset = 0
|
|
|
|
// Scroll by a value that doesn't align to entry boundaries
|
|
// 73 pixels = 1 full entry (48px) + 25 pixels into the next entry
|
|
HandlePixelScroll(s, 73)
|
|
|
|
expected := 73.0
|
|
if s.ScrollOffset != expected {
|
|
t.Errorf("expected ScrollOffset=%.1f (sub-entry precision), got %.1f", expected, s.ScrollOffset)
|
|
}
|
|
|
|
// Scroll another 100 pixels (total 173 = 3 entries + 29px)
|
|
HandlePixelScroll(s, 100)
|
|
|
|
expected = 173.0
|
|
if s.ScrollOffset != expected {
|
|
t.Errorf("expected ScrollOffset=%.1f (accumulated sub-entry precision), got %.1f", expected, s.ScrollOffset)
|
|
}
|
|
}
|
|
|
|
// --- Test: Pixel Scroll Index Derived from Offset ---
|
|
|
|
func TestPixelScroll_IndexFromOffset(t *testing.T) {
|
|
s := NewBrowserState()
|
|
s.TotalEntries = 1000
|
|
s.VisibleCount = 20
|
|
s.EntryHeight = 48.0
|
|
s.ScrollOffset = 0
|
|
|
|
// Initial index should be 0
|
|
if s.GetScrollIndex() != 0 {
|
|
t.Errorf("expected initial ScrollIndex=0, got %d", s.GetScrollIndex())
|
|
}
|
|
|
|
// Scroll down by 100 pixels
|
|
HandlePixelScroll(s, 100)
|
|
|
|
// ScrollIndex should be floor(100 / 48) = 2
|
|
expectedIndex := 2
|
|
if s.GetScrollIndex() != expectedIndex {
|
|
t.Errorf("expected ScrollIndex=%d (from offset 100 / height 48), got %d", expectedIndex, s.GetScrollIndex())
|
|
}
|
|
|
|
// Scroll up by 50 pixels (offset now 50)
|
|
HandlePixelScroll(s, -50)
|
|
|
|
// ScrollIndex should be floor(50 / 48) = 1
|
|
expectedIndex = 1
|
|
if s.GetScrollIndex() != expectedIndex {
|
|
t.Errorf("expected ScrollIndex=%d (from offset 50 / height 48), got %d", expectedIndex, s.GetScrollIndex())
|
|
}
|
|
}
|
|
|
|
// --- Test: Pixel Scroll Eviction Triggered ---
|
|
|
|
func TestPixelScroll_EvictionTriggered(t *testing.T) {
|
|
s := NewBrowserState()
|
|
s.TotalEntries = 5000
|
|
s.VisibleCount = 20
|
|
s.EntryHeight = 48.0
|
|
s.ScrollOffset = 0
|
|
|
|
// Load pages at various positions
|
|
entries := make([]Entry, 5000)
|
|
for i := 0; i < 5000; i++ {
|
|
entries[i] = NewEntry("/test/file.txt", "file.txt", 100, s.TapTimestamp, false)
|
|
}
|
|
s.SortIndex = &DirectoryIndex{
|
|
Entries: entries,
|
|
SortOrders: map[string][]int{
|
|
"name_asc": buildPositionMap(entries, SortModeNameAsc),
|
|
"date_desc": buildPositionMap(entries, SortModeDateDesc),
|
|
},
|
|
}
|
|
for pageIdx := 0; pageIdx <= 10; pageIdx++ {
|
|
p := loadPageFromIndex(s, pageIdx)
|
|
if p != nil {
|
|
s.Pages[pageIdx] = p
|
|
}
|
|
}
|
|
|
|
// Scroll far away using pixel offset
|
|
// Scroll to page 40+ (40 * 100 * 48 = 192000 pixels)
|
|
HandlePixelScroll(s, 192000)
|
|
|
|
// Pages far from current position should be evicted
|
|
// Check that page 0 was evicted (it's far from page 40)
|
|
if page, exists := s.Pages[0]; exists && page.Loaded {
|
|
t.Error("expected page 0 to be evicted after scrolling far away")
|
|
}
|
|
}
|
|
|
|
// --- Test: Pixel Scroll Prefetch Triggered ---
|
|
|
|
func TestPixelScroll_PrefetchTriggered(t *testing.T) {
|
|
s := NewBrowserState()
|
|
s.TotalEntries = 2000
|
|
s.VisibleCount = 20
|
|
s.EntryHeight = 48.0
|
|
s.ScrollOffset = 0
|
|
|
|
// Load some pages around current position
|
|
entries := make([]Entry, 2000)
|
|
for i := 0; i < 2000; i++ {
|
|
entries[i] = NewEntry("/test/file.txt", "file.txt", 100, s.TapTimestamp, false)
|
|
}
|
|
s.SortIndex = &DirectoryIndex{
|
|
Entries: entries,
|
|
SortOrders: map[string][]int{
|
|
"name_asc": buildPositionMap(entries, SortModeNameAsc),
|
|
"date_desc": buildPositionMap(entries, SortModeDateDesc),
|
|
},
|
|
}
|
|
for pageIdx := 3; pageIdx <= 7; pageIdx++ {
|
|
p := loadPageFromIndex(s, pageIdx)
|
|
if p != nil {
|
|
s.Pages[pageIdx] = p
|
|
}
|
|
}
|
|
|
|
// Record current page count
|
|
pagesBefore := len(s.Pages)
|
|
|
|
// Scroll to trigger prefetch using pixel offset
|
|
HandlePixelScroll(s, 200*48) // Scroll to page ~7
|
|
|
|
// After scroll, prefetch should have been triggered
|
|
// Page count may have increased due to prefetch
|
|
if len(s.Pages) < pagesBefore {
|
|
t.Errorf("expected page count to not decrease after scroll, before=%d, after=%d", pagesBefore, len(s.Pages))
|
|
}
|
|
}
|
|
|
|
// --- Test: Pixel Scroll With Small Directory ---
|
|
|
|
func TestPixelScroll_SmallDirectory(t *testing.T) {
|
|
s := NewBrowserState()
|
|
s.TotalEntries = 10 // Smaller than VisibleCount
|
|
s.VisibleCount = 20
|
|
s.EntryHeight = 48.0
|
|
s.ScrollOffset = 0
|
|
|
|
// Try to scroll down (should be clamped since no scrollable area)
|
|
HandlePixelScroll(s, 1000)
|
|
|
|
// ScrollOffset should be 0 (can't scroll beyond available entries)
|
|
if s.ScrollOffset != 0 {
|
|
t.Errorf("expected ScrollOffset=0 for small directory, got %.1f", s.ScrollOffset)
|
|
}
|
|
}
|
|
|
|
// --- Test: Pixel Scroll To Beginning ---
|
|
|
|
func TestPixelScroll_ToBeginning(t *testing.T) {
|
|
s := NewBrowserState()
|
|
s.TotalEntries = 1000
|
|
s.VisibleCount = 20
|
|
s.EntryHeight = 48.0
|
|
s.ScrollOffset = 500 * 48 // Somewhere in the middle
|
|
|
|
// Scroll all the way to the beginning
|
|
HandlePixelScroll(s, -100000)
|
|
|
|
if s.ScrollOffset != 0 {
|
|
t.Errorf("expected ScrollOffset=0 at beginning, got %.1f", s.ScrollOffset)
|
|
}
|
|
}
|
|
|
|
// --- Test: Pixel Scroll To End ---
|
|
|
|
func TestPixelScroll_ToEnd(t *testing.T) {
|
|
s := NewBrowserState()
|
|
s.TotalEntries = 1000
|
|
s.VisibleCount = 20
|
|
s.EntryHeight = 48.0
|
|
s.ScrollOffset = 0
|
|
|
|
// Scroll all the way to the end
|
|
maxScroll := float64(s.TotalEntries-s.VisibleCount) * s.EntryHeight
|
|
HandlePixelScroll(s, int(maxScroll)+1000)
|
|
|
|
if s.ScrollOffset != maxScroll {
|
|
t.Errorf("expected ScrollOffset=%.1f at end, got %.1f", maxScroll, s.ScrollOffset)
|
|
}
|
|
}
|