// Copyright 2020 Pilosa Corp.
//
// 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 pilosa

import (
	"bytes"
	"context"
	"io"
	"io/ioutil"
	"os"
	"strings"
	"testing"

	cryrand "crypto/rand"
)

var _ = context.Background
var _ = os.Open
var _ = strings.Split

func TestMultiReaderB(t *testing.T) {
	// MultiReaderB should read identical chunks of bytes from both its "a" and "b"
	// member io.Readers, else it should panic. This should hold for
	// varying sizes of inputs.

	for n := 1 << 5; n < (1 << 18); n = n*2 - 13 {
		src := io.LimitReader(cryrand.Reader, int64(n))

		a := make([]byte, n)
		nr := 0
		for nr < n {
			na, err := src.Read(a)
			panicOn(err)
			nr += na
		}
		if nr != n {
			panic("short read")
		}

		b := make([]byte, n)
		copy(b, a)
		if !bytes.Equal(a, b) {
			panic("test prep failed")
		}

		m := &MultiReaderB{
			a: ioutil.NopCloser(bytes.NewBuffer(a)),
			b: ioutil.NopCloser(bytes.NewBuffer(b)),
		}

		// should not trigger the internal panic of MultiReadB
		ncp, err := io.Copy(ioutil.Discard, m)
		panicOn(err)
		if ncp != int64(n) {
			panic("short copy")
		}

		for victim := 0; victim < n; victim += 7 {

			copy(b, a)
			if victim%2 == 0 {
				// corrupt b
				b[victim] = (b[victim] + 1) % 255
			} else {
				// corrupt a
				a[victim] = (a[victim] + 1) % 255
			}
			m = &MultiReaderB{
				a: ioutil.NopCloser(bytes.NewBuffer(a)),
				b: ioutil.NopCloser(bytes.NewBuffer(b)),
			}
			helperShouldPanicOnCopy(m)
		}
	}
}

func helperShouldPanicOnCopy(m *MultiReaderB) {
	// differences in bytes read should be noticed
	defer func() {
		r := recover()
		if r == nil {
			panic("expected panic on byte difference but didn't see it")
		}
	}()
	_, _ = io.Copy(ioutil.Discard, m)
}
