package jitter

import (
	"testing"
	"time"
)

var dur = 30 * time.Millisecond

func TestTimer(t *testing.T) {
	a := time.NewTimer(1 * dur)
	b := newTimer(2 * dur)
	c := time.NewTimer(3 * dur)

	select {
	case <-a.C:
		// correct
	case <-b.C:
		t.Fatalf("timer 2 fired first")
	case <-c.C:
		t.Fatalf("timer 3 fired first")
	}

	select {
	case <-b.C:
		// correct
	case <-c.C:
		t.Fatalf("timer 3 fired second")
	}
}

func TestTicker(t *testing.T) {
	a := NewTicker(1 * dur)
	b := newTimer(4*dur + dur/2)
	c := time.NewTimer(5 * dur)

	var i int
loop:
	for {
		select {
		case <-a.C:
			i++
		case <-b.C:
			break loop
		case <-c.C:
			t.Fatalf("failsafe timer fired")
		}
	}

	if have, want := i, 4; have != want {
		t.Errorf("ticker fired %d times, want %d", have, want)
	}
}

func TestHeap(t *testing.T) {
	a := time.NewTimer(1 * dur)
	c := time.NewTimer(3 * dur)

	keeper := 245

	timers := make([]*Timer, 1000)
	for i := range timers {
		t := newTimer(2 * dur)
		timers[i] = t
	}
	for i, t := range timers {
		if i == keeper {
			continue
		}
		t.Stop()
	}

	select {
	case <-a.C:
		t.Fatalf("pre-timer fired, results will be unreliable")
	default:
	}

	select {
	case <-timers[keeper].C:
	case <-c.C:
		t.Fatalf("post-timer fired")
	}
}

func TestStop(t *testing.T) {
	a := time.NewTimer(1 * dur)
	b := newTimer(2 * dur)
	c := time.NewTimer(3 * dur)

	stopped := b.Stop()

	select {
	case <-a.C:
		t.Fatalf("setup took too long")
	default:
	}

	if have, want := stopped, true; have != want {
		t.Fatalf("Timer.Stop(); %t != %t", have, want)
	}

	select {
	case <-a.C:
	case <-b.C:
		t.Fatalf("timer fired, should have been stopped")
	}

	select {
	case <-b.C:
		t.Fatalf("timer fires, should have been stopped")
	case <-c.C:
	}
}

func TestLateStop(t *testing.T) {
	a := newTimer(1 * dur)
	b := time.NewTimer(2 * dur)

	<-b.C

	stopped := a.Stop()

	if have, want := stopped, false; have != want {
		t.Fatalf("Timer.Stop(); %t != %t", have, want)
	}
}
