/*
 *
 * Copyright 2020 gRPC authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */

package testutils

import (
	"testing"

	"google.golang.org/grpc/balancer"
	"google.golang.org/grpc/internal/testutils"
)

func TestIsRoundRobin(t *testing.T) {
	var (
		sc1 = testutils.TestSubConns[0]
		sc2 = testutils.TestSubConns[1]
		sc3 = testutils.TestSubConns[2]
	)

	testCases := []struct {
		desc string
		want []balancer.SubConn
		got  []balancer.SubConn
		pass bool
	}{
		{
			desc: "0 element",
			want: []balancer.SubConn{},
			got:  []balancer.SubConn{},
			pass: true,
		},
		{
			desc: "1 element RR",
			want: []balancer.SubConn{sc1},
			got:  []balancer.SubConn{sc1, sc1, sc1, sc1},
			pass: true,
		},
		{
			desc: "1 element not RR",
			want: []balancer.SubConn{sc1},
			got:  []balancer.SubConn{sc1, sc2, sc1},
			pass: false,
		},
		{
			desc: "2 elements RR",
			want: []balancer.SubConn{sc1, sc2},
			got:  []balancer.SubConn{sc1, sc2, sc1, sc2, sc1, sc2},
			pass: true,
		},
		{
			desc: "2 elements RR different order from want",
			want: []balancer.SubConn{sc2, sc1},
			got:  []balancer.SubConn{sc1, sc2, sc1, sc2, sc1, sc2},
			pass: true,
		},
		{
			desc: "2 elements RR not RR, mistake in first iter",
			want: []balancer.SubConn{sc1, sc2},
			got:  []balancer.SubConn{sc1, sc1, sc1, sc2, sc1, sc2},
			pass: false,
		},
		{
			desc: "2 elements RR not RR, mistake in second iter",
			want: []balancer.SubConn{sc1, sc2},
			got:  []balancer.SubConn{sc1, sc2, sc1, sc1, sc1, sc2},
			pass: false,
		},
		{
			desc: "2 elements weighted RR",
			want: []balancer.SubConn{sc1, sc1, sc2},
			got:  []balancer.SubConn{sc1, sc1, sc2, sc1, sc1, sc2},
			pass: true,
		},
		{
			desc: "2 elements weighted RR different order",
			want: []balancer.SubConn{sc1, sc1, sc2},
			got:  []balancer.SubConn{sc1, sc2, sc1, sc1, sc2, sc1},
			pass: true,
		},

		{
			desc: "3 elements RR",
			want: []balancer.SubConn{sc1, sc2, sc3},
			got:  []balancer.SubConn{sc1, sc2, sc3, sc1, sc2, sc3, sc1, sc2, sc3},
			pass: true,
		},
		{
			desc: "3 elements RR different order",
			want: []balancer.SubConn{sc1, sc2, sc3},
			got:  []balancer.SubConn{sc3, sc2, sc1, sc3, sc2, sc1},
			pass: true,
		},
		{
			desc: "3 elements weighted RR",
			want: []balancer.SubConn{sc1, sc1, sc1, sc2, sc2, sc3},
			got:  []balancer.SubConn{sc1, sc2, sc3, sc1, sc2, sc1, sc1, sc2, sc3, sc1, sc2, sc1},
			pass: true,
		},
		{
			desc: "3 elements weighted RR not RR, mistake in first iter",
			want: []balancer.SubConn{sc1, sc1, sc1, sc2, sc2, sc3},
			got:  []balancer.SubConn{sc1, sc2, sc1, sc1, sc2, sc1, sc1, sc2, sc3, sc1, sc2, sc1},
			pass: false,
		},
		{
			desc: "3 elements weighted RR not RR, mistake in second iter",
			want: []balancer.SubConn{sc1, sc1, sc1, sc2, sc2, sc3},
			got:  []balancer.SubConn{sc1, sc2, sc3, sc1, sc2, sc1, sc1, sc1, sc3, sc1, sc2, sc1},
			pass: false,
		},
	}
	for _, tC := range testCases {
		t.Run(tC.desc, func(t *testing.T) {
			err := testutils.IsRoundRobin(tC.want, (&testClosure{r: tC.got}).next)
			if err == nil != tC.pass {
				t.Errorf("want pass %v, want %v, got err %v", tC.pass, tC.want, err)
			}
		})
	}
}

// testClosure is a test util for TestIsRoundRobin.
type testClosure struct {
	r []balancer.SubConn
	i int
}

func (tc *testClosure) next() balancer.SubConn {
	ret := tc.r[tc.i]
	tc.i = (tc.i + 1) % len(tc.r)
	return ret
}
