Ctrl K
ComunidadGuías…

pkg/watcher/watcher_test.go

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
package watcher

import (
	"os"
	"path/filepath"
	"runtime"
	"strings"
	"testing"
	"time"
)

// TestCloseWakesWaitForEvents verifies that Close releases a caller parked
// in WaitForEvents, rather than leaving it asleep on the condition forever.
func TestCloseWakesWaitForEvents(t *testing.T) {
	w, err := New("test-app")
	if err != nil {
		t.Fatal(err)
	}
	if err := w.RecursivelyWatch(t.TempDir()); err != nil {
		t.Fatal(err)
	}
	t.Cleanup(func() { _ = w.Close() })

	done := make(chan bool, 1)
	go func() {
		_, ok := w.WaitForEvents()
		done <- ok
	}()

	// Wait until the caller is genuinely parked on the condition, so the
	// test can't pass merely by observing an already-closed watcher. There
	// is no hook between WaitForEvents' stop-check and its Wait, so stack
	// inspection is the only way to detect this.
	waitUntilParked(t)

	_ = w.Close()

	select {
	case ok := <-done:
		if ok {
			t.Fatal("WaitForEvents reported events after Close, want ok=false")
		}
	case <-time.After(5 * time.Second):
		t.Fatal("WaitForEvents did not return after Close; its caller is leaked")
	}
}

// waitUntilParked blocks until a goroutine is parked on the condition from
// inside WaitForEvents. It matches both frames in the same goroutine's
// stack, so an unrelated goroutine waiting on some other condition doesn't
// count.
func waitUntilParked(t *testing.T) {
	t.Helper()
	deadline := time.Now().Add(5 * time.Second)
	buf := make([]byte, 1<<20)
	for time.Now().Before(deadline) {
		n := runtime.Stack(buf, true)
		for _, g := range strings.Split(string(buf[:n]), "\n\n") {
			if strings.Contains(g, "sync.(*Cond).Wait") &&
				strings.Contains(g, "(*Watcher).WaitForEvents") {
				return
			}
		}
		time.Sleep(5 * time.Millisecond)
	}
	t.Fatal("timed out waiting for a caller to park in WaitForEvents")
}

// TestFilesInNewDirectory verifies that files created together with a new
// directory are reported, even though they exist before the directory is watched.
// The directory is populated elsewhere and moved in, so the file never gets
// an event of its own.
func TestFilesInNewDirectory(t *testing.T) {
	w, err := New("test-app")
	if err != nil {
		t.Fatal(err)
	}
	root := t.TempDir()
	if err := w.RecursivelyWatch(root); err != nil {
		t.Fatal(err)
	}
	t.Cleanup(func() { _ = w.Close() })

	staging := t.TempDir()
	staged := filepath.Join(staging, "a", "migrations", "1_init.up.sql")
	if err := os.MkdirAll(filepath.Dir(staged), 0o755); err != nil {
		t.Fatal(err)
	}
	if err := os.WriteFile(staged, []byte("CREATE TABLE t (id INT);"), 0o644); err != nil {
		t.Fatal(err)
	}
	if err := os.Rename(filepath.Join(staging, "a"), filepath.Join(root, "a")); err != nil {
		t.Fatal(err)
	}
	file := filepath.Join(root, "a", "migrations", "1_init.up.sql")

	found := make(chan struct{})
	go func() {
		for {
			events, ok := w.WaitForEvents()
			if !ok {
				return
			}
			for _, ev := range events {
				if ev.Path == file && ev.EventType == CREATED {
					close(found)
					return
				}
			}
		}
	}()

	select {
	case <-found:
	case <-time.After(5 * time.Second):
		t.Fatal("no event for a file created together with its directory")
	}
}