302 lines
7.9 KiB
Go
302 lines
7.9 KiB
Go
package browser
|
|
|
|
import (
|
|
"testing"
|
|
"time"
|
|
|
|
"pad/internal/io/pool"
|
|
"pad/internal/io/pool/mock"
|
|
)
|
|
|
|
// TestNewBrowserManager verifies that a BrowserManager can be constructed
|
|
// with valid parameters and has correct initial state.
|
|
func TestNewBrowserManager(t *testing.T) {
|
|
state := NewBrowserState()
|
|
fs := mock.NewFileSystem()
|
|
wp := pool.NewWorkerPool(1)
|
|
|
|
bm, err := NewBrowserManager(state, wp, fs)
|
|
if err != nil {
|
|
t.Fatalf("NewBrowserManager failed: %v", err)
|
|
}
|
|
|
|
if bm == nil {
|
|
t.Fatal("NewBrowserManager returned nil")
|
|
}
|
|
|
|
// Verify initial state
|
|
if bm.state != state {
|
|
t.Errorf("BrowserManager.state = %p, want %p", bm.state, state)
|
|
}
|
|
}
|
|
|
|
// TestNavigateTo verifies that NavigateTo dispatches a BuildIndexTask
|
|
// and resets browser state appropriately.
|
|
func TestNavigateTo(t *testing.T) {
|
|
state := NewBrowserState()
|
|
fs := mock.NewFileSystem()
|
|
wp := pool.NewWorkerPool(1)
|
|
|
|
// Create some mock entries in the filesystem
|
|
now := time.Now()
|
|
fs.AddDir("/test/docs", now)
|
|
fs.AddFile("/test/docs/file1.txt", []byte("content1"), now)
|
|
fs.AddFile("/test/docs/file2.txt", []byte("content2"), now)
|
|
fs.AddDir("/test/docs/subdir", now)
|
|
|
|
bm, err := NewBrowserManager(state, wp, fs)
|
|
if err != nil {
|
|
t.Fatalf("NewBrowserManager failed: %v", err)
|
|
}
|
|
|
|
// Navigate to a directory
|
|
bm.NavigateTo("/test/docs")
|
|
|
|
// Verify state was reset
|
|
if state.CurrentPath != "/test/docs" {
|
|
t.Errorf("CurrentPath = %q, want %q", state.CurrentPath, "/test/docs")
|
|
}
|
|
|
|
if state.ScrollOffset != 0 {
|
|
t.Errorf("ScrollOffset = %f, want 0", state.ScrollOffset)
|
|
}
|
|
|
|
if state.SelectedIndex != -1 {
|
|
t.Errorf("SelectedIndex = %d, want -1", state.SelectedIndex)
|
|
}
|
|
|
|
// Verify a task was dispatched (should have pending work)
|
|
// The manager should have queued a BuildIndexTask
|
|
if !state.Loading {
|
|
t.Error("Expected Loading to be true after NavigateTo")
|
|
}
|
|
}
|
|
|
|
// TestNavigateTo_NonExistentDirectory verifies behavior when navigating
|
|
// to a directory that doesn't exist in the filesystem.
|
|
func TestNavigateTo_NonExistentDirectory(t *testing.T) {
|
|
state := NewBrowserState()
|
|
fs := mock.NewFileSystem()
|
|
wp := pool.NewWorkerPool(1)
|
|
|
|
bm, err := NewBrowserManager(state, wp, fs)
|
|
if err != nil {
|
|
t.Fatalf("NewBrowserManager failed: %v", err)
|
|
}
|
|
|
|
// Navigate to non-existent directory
|
|
bm.NavigateTo("/non/existent/path")
|
|
|
|
// Should still set the path (error handling happens async)
|
|
if state.CurrentPath != "/non/existent/path" {
|
|
t.Errorf("CurrentPath = %q, want %q", state.CurrentPath, "/non/existent/path")
|
|
}
|
|
}
|
|
|
|
// TestHandleResult_BuildIndexSuccess verifies that a successful BuildIndex
|
|
// result populates the SortIndex and triggers initial page loading.
|
|
func TestHandleResult_BuildIndexSuccess(t *testing.T) {
|
|
state := NewBrowserState()
|
|
fs := mock.NewFileSystem()
|
|
wp := pool.NewWorkerPool(1)
|
|
|
|
bm, err := NewBrowserManager(state, wp, fs)
|
|
if err != nil {
|
|
t.Fatalf("NewBrowserManager failed: %v", err)
|
|
}
|
|
|
|
// Simulate a successful BuildIndex result
|
|
result := pool.Result{
|
|
TaskType: pool.TypeBuildIndex,
|
|
Success: true,
|
|
Data: []mock.DirEntry{},
|
|
}
|
|
|
|
bm.HandleResult(result)
|
|
|
|
// After successful BuildIndex, Loading should be false
|
|
if state.Loading {
|
|
t.Error("Expected Loading to be false after successful BuildIndex result")
|
|
}
|
|
}
|
|
|
|
// TestHandleResult_BuildIndexFailure verifies that a failed BuildIndex
|
|
// result is handled gracefully.
|
|
func TestHandleResult_BuildIndexFailure(t *testing.T) {
|
|
state := NewBrowserState()
|
|
fs := mock.NewFileSystem()
|
|
wp := pool.NewWorkerPool(1)
|
|
|
|
bm, err := NewBrowserManager(state, wp, fs)
|
|
if err != nil {
|
|
t.Fatalf("NewBrowserManager failed: %v", err)
|
|
}
|
|
|
|
// Simulate a failed BuildIndex result
|
|
result := pool.Result{
|
|
TaskType: pool.TypeBuildIndex,
|
|
Success: false,
|
|
Error: ErrDirNotFound,
|
|
}
|
|
|
|
bm.HandleResult(result)
|
|
|
|
// Should not panic, state should be recoverable
|
|
if state.CurrentPath != "" {
|
|
t.Errorf("Expected empty CurrentPath on error, got %q", state.CurrentPath)
|
|
}
|
|
}
|
|
|
|
// TestHandleResult_LoadPagesSuccess verifies that a successful LoadPages
|
|
// result populates the Pages map.
|
|
func TestHandleResult_LoadPagesSuccess(t *testing.T) {
|
|
state := NewBrowserState()
|
|
state.TotalEntries = 500
|
|
fs := mock.NewFileSystem()
|
|
wp := pool.NewWorkerPool(1)
|
|
|
|
bm, err := NewBrowserManager(state, wp, fs)
|
|
if err != nil {
|
|
t.Fatalf("NewBrowserManager failed: %v", err)
|
|
}
|
|
|
|
// Simulate a successful LoadPages result
|
|
pageIndices := []int{0, 1}
|
|
result := pool.Result{
|
|
TaskType: pool.TypeLoadPages,
|
|
Success: true,
|
|
Data: pageIndices,
|
|
}
|
|
|
|
bm.HandleResult(result)
|
|
|
|
// Pages should be populated (or at least not cause a panic)
|
|
// The exact behavior depends on implementation
|
|
}
|
|
|
|
// TestOnScroll_Prefetch verifies that scrolling triggers prefetch for
|
|
// pages beyond the visible region.
|
|
func TestOnScroll_Prefetch(t *testing.T) {
|
|
state := NewBrowserState()
|
|
state.TotalEntries = 1000
|
|
state.EntryHeight = 48.0
|
|
state.VisibleCount = 10
|
|
fs := mock.NewFileSystem()
|
|
wp := pool.NewWorkerPool(1)
|
|
|
|
bm, err := NewBrowserManager(state, wp, fs)
|
|
if err != nil {
|
|
t.Fatalf("NewBrowserManager failed: %v", err)
|
|
}
|
|
|
|
// Set initial scroll position
|
|
state.ScrollOffset = 0
|
|
|
|
// Simulate scrolling down
|
|
state.ScrollOffset = 5000.0 // ~100 entries down
|
|
|
|
bm.OnScroll()
|
|
|
|
// Should trigger prefetch (exact behavior depends on implementation)
|
|
// At minimum, should not panic
|
|
}
|
|
|
|
// TestOnScroll_Eviction verifies that scrolling triggers eviction of
|
|
// pages far from the visible region.
|
|
func TestOnScroll_Eviction(t *testing.T) {
|
|
state := NewBrowserState()
|
|
state.TotalEntries = 1000
|
|
state.EntryHeight = 48.0
|
|
state.VisibleCount = 10
|
|
fs := mock.NewFileSystem()
|
|
wp := pool.NewWorkerPool(1)
|
|
|
|
bm, err := NewBrowserManager(state, wp, fs)
|
|
if err != nil {
|
|
t.Fatalf("NewBrowserManager failed: %v", err)
|
|
}
|
|
|
|
// Pre-populate some pages
|
|
state.Pages[0] = NewPage(0, make([]Entry, 100))
|
|
state.Pages[5] = NewPage(5, make([]Entry, 100))
|
|
state.Pages[10] = NewPage(10, make([]Entry, 100))
|
|
|
|
// Scroll far down
|
|
state.ScrollOffset = 50000.0 // ~1000 entries down
|
|
|
|
bm.OnScroll()
|
|
|
|
// Pages far from visible region should be evicted
|
|
// (exact behavior depends on implementation)
|
|
}
|
|
|
|
// TestBrowserManager_StateOwnership verifies that the BrowserManager
|
|
// does not directly modify state outside of designated methods.
|
|
func TestBrowserManager_StateOwnership(t *testing.T) {
|
|
state := NewBrowserState()
|
|
fs := mock.NewFileSystem()
|
|
wp := pool.NewWorkerPool(1)
|
|
|
|
bm, err := NewBrowserManager(state, wp, fs)
|
|
if err != nil {
|
|
t.Fatalf("NewBrowserManager failed: %v", err)
|
|
}
|
|
|
|
// Verify that the manager has a reference to the state
|
|
// but does not own it (state is owned by the editor)
|
|
if bm.state != state {
|
|
t.Error("BrowserManager should have reference to BrowserState")
|
|
}
|
|
}
|
|
|
|
// TestNavigateTo_ChangesDirectory verifies that navigating to a new
|
|
// directory clears the previous directory's state.
|
|
func TestNavigateTo_ChangesDirectory(t *testing.T) {
|
|
state := NewBrowserState()
|
|
state.CurrentPath = "/old/path"
|
|
state.TotalEntries = 500
|
|
state.Pages[0] = NewPage(0, make([]Entry, 100))
|
|
fs := mock.NewFileSystem()
|
|
wp := pool.NewWorkerPool(1)
|
|
|
|
bm, err := NewBrowserManager(state, wp, fs)
|
|
if err != nil {
|
|
t.Fatalf("NewBrowserManager failed: %v", err)
|
|
}
|
|
|
|
// Navigate to new directory
|
|
bm.NavigateTo("/new/path")
|
|
|
|
if state.CurrentPath != "/new/path" {
|
|
t.Errorf("CurrentPath = %q, want %q", state.CurrentPath, "/new/path")
|
|
}
|
|
|
|
// Pages should be cleared for new directory
|
|
if len(state.Pages) != 0 {
|
|
t.Errorf("Expected Pages to be cleared, got %d pages", len(state.Pages))
|
|
}
|
|
}
|
|
|
|
// TestHandleResult_UnknownType verifies that unknown result types
|
|
// are handled gracefully without panicking.
|
|
func TestHandleResult_UnknownType(t *testing.T) {
|
|
state := NewBrowserState()
|
|
fs := mock.NewFileSystem()
|
|
wp := pool.NewWorkerPool(1)
|
|
|
|
bm, err := NewBrowserManager(state, wp, fs)
|
|
if err != nil {
|
|
t.Fatalf("NewBrowserManager failed: %v", err)
|
|
}
|
|
|
|
// Simulate an unknown result type
|
|
result := pool.Result{
|
|
TaskType: pool.TypeReadFile, // Not a browser task
|
|
Success: true,
|
|
Data: nil,
|
|
}
|
|
|
|
// Should not panic
|
|
bm.HandleResult(result)
|
|
}
|