package winpwn

import "testing"

// TestCyclicMatchesPwntools pins Cyclic(20) against pwntools' own
// cyclic(20) output (b'aaaabaaacaaadaaaeaaa') -- if this ever drifts, every
// offset a player calculates by hand using pwntools docs/muscle memory
// would silently be wrong.
func TestCyclicMatchesPwntools(t *testing.T) {
	got := string(Cyclic(20))
	want := "aaaabaaacaaadaaaeaaa"
	if got != want {
		t.Fatalf("Cyclic(20) = %q, want %q", got, want)
	}
}

func TestCyclicFindRoundTrip(t *testing.T) {
	buf := Cyclic(200)
	for _, off := range []int{0, 4, 17, 100, 196} {
		sub := buf[off : off+4]
		if got := CyclicFind(sub); got != off {
			t.Errorf("CyclicFind(%q) = %d, want %d", sub, got, off)
		}
	}
}

func TestCyclicFindNotFound(t *testing.T) {
	if got := CyclicFind([]byte{0, 1, 2, 3}); got != -1 {
		t.Errorf("CyclicFind(non-alphabet bytes) = %d, want -1", got)
	}
}

// TestCyclicN8StaysBounded guards against eagerly materializing the full
// de Bruijn period (26^8 ~ 2*10^11 bytes) when only a small prefix is
// requested -- a real bug caught in this package's own test run (OOM
// crash) before deBruijnEach was made to stop early via its yield callback.
func TestCyclicN8StaysBounded(t *testing.T) {
	buf := CyclicN(64, 8)
	if len(buf) != 64 {
		t.Fatalf("CyclicN(64, 8) returned %d bytes, want 64", len(buf))
	}
	sub := buf[16:24]
	if off := CyclicFindN(sub, 8); off != 16 {
		t.Errorf("CyclicFindN = %d, want 16", off)
	}
}

func TestCyclicNZeroLength(t *testing.T) {
	if got := CyclicN(0, 4); len(got) != 0 {
		t.Errorf("CyclicN(0, 4) = %v, want empty", got)
	}
}
