package join

import (
	"fmt"
	"io/ioutil"
	"testing"

	"code.justin.tv/rhys/nursery/cmd/multicp/join/internal"
)

func TestBuffer(t *testing.T) {
	b := NewBuffer()

	writeAt := func(p string, off int64) {
		n, err := b.WriteAt([]byte(p), off)
		if err != nil || n != len(p) {
			t.Errorf("WriteAt(%q, %d); n, err = %d, %v", p, off, n, err)
		}
	}
	close := func() {
		err := b.Close()
		if err != nil {
			t.Errorf("Close; err = %v", err)
		}
	}

	dump := func() {
		b.cond.L.Lock()
		defer b.cond.L.Unlock()
		t.Logf("base=%d closed=%t data=%d", b.base, b.closed, len(b.data))
		for _, buf := range b.data {
			t.Logf("offset=%d data=%q", buf.offset, buf.payload)
		}
	}

	dump()
	writeAt("23456", 2)
	dump()
	writeAt("45", 4)
	dump()
	writeAt("78", 7)
	dump()
	writeAt("0123", 0)
	dump()
	close()
	dump()

	body, err := ioutil.ReadAll(b)
	dump()
	if err != nil {
		t.Errorf("ReadAll; err = %v", err)
	}
	if have, want := string(body), "012345678"; have != want {
		t.Errorf("Read; %q != %v", have, want)
	}
}

func TestFuzz(t *testing.T) {
	testcase := func(input string) func(t *testing.T) {
		return func(t *testing.T) {
			defer func() {
				p := recover()
				if p != nil {
					t.Fatalf("panic: %v", p)
				}
			}()
			internal.Fuzz([]byte(input), NewBuffer(), t.Logf)
		}
	}

	for _, tt := range []string{
		// Incorrect reassembly with overlapping data segments
		"000000000\r\x00H",
		// Fuzzer enters infinite loop (??)
		"\x00\x00\x00\xe3(\"t9\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +
			"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xe3" +
			"(\"t9\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xfc\x00" +
			"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +
			"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +
			"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +
			"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x03\x00\x00\x00\x00\x00\x00" +
			"\x00\x00\x00\x00\x00\x00",
	} {
		t.Run(fmt.Sprintf("%q", tt), testcase(tt))
	}
}
