// Copyright 2017 The OPA Authors.  All rights reserved.
// Use of this source code is governed by an Apache2
// license that can be found in the LICENSE file.

package server

import (
	"fmt"
	"math/rand"
	"testing"
	"time"
)

func TestBufferAutoErase(t *testing.T) {
	buf := NewBoundedBuffer(30)
	for i := 0; i < 100; i++ {
		buf.Push(&Info{Query: fmt.Sprint(i)})
	}

	var i uint64 = 70
	buf.Iter(func(in *Info) {
		if int(i-70) > 30 {
			t.Fatal("Ran off the end of the buffer")
		}

		if in.Query != fmt.Sprint(i) {
			t.Fatalf("Expected query to be %d, got %s", i, in.Query)
		}
		i++
	})
}

func TestBufferRandom(t *testing.T) {
	rand.Seed(time.Now().Unix())
	for i := 0; i < 13; i++ {
		testBufferRandomSize(t, int((1 << uint(i))))
	}
}

var (
	threshold int = 1e3
	ops       int = 1e5
)

func testBufferRandomSize(t *testing.T, cap int) {
	var head, cur uint64
	var size int

	var pushes, iters uint

	buf := NewBoundedBuffer(cap)
	for i := 0; i < ops; i++ {
		switch r := rand.Intn(threshold + 1); {
		case r < threshold:
			pushes++
			buf.Push(&Info{Query: fmt.Sprint(cur)})

			cur++
			if size == cap {
				head++
			} else {
				size++
			}
		default:
			// Rarely, test that the iteration is working correctly.
			// Testing often leads to N^2 runtime for the tests, which is too
			// slow, so we only iter with very low probability.
			//
			// Occurs with probability 1/(threshold+1).
			iters++
			j := head
			buf.Iter(func(i *Info) {
				if int(j-head) > size {
					t.Fatal("Ran off the end of the buffer")
				}

				if i.Query != fmt.Sprint(j) {
					t.Fatalf("Expected query to be %d, got %s", j, i.Query)
				}
				j++
			})
		}
	}

	t.Logf("\nPushes: %d (%f%%)\nIters:  %d (%f%%)",
		pushes, 100*float64(pushes)/float64(ops),
		iters, 100*float64(iters)/float64(ops),
	)
}
