// Copyright 2013 Matt T. Proud
//
// 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 pbutil

import (
	"bytes"
	"math/rand"
	"reflect"
	"testing"
	"testing/quick"

	"github.com/matttproud/golang_protobuf_extensions/pbtest"

	. "github.com/coreos/etcd/Godeps/_workspace/src/github.com/golang/protobuf/proto"
	. "github.com/coreos/etcd/Godeps/_workspace/src/github.com/golang/protobuf/proto/testdata"
)

func TestWriteDelimited(t *testing.T) {
	for _, test := range []struct {
		msg Message
		buf []byte
		n   int
		err error
	}{
		{
			msg: &Empty{},
			n:   1,
			buf: []byte{0},
		},
		{
			msg: &GoEnum{Foo: FOO_FOO1.Enum()},
			n:   3,
			buf: []byte{2, 8, 1},
		},
		{
			msg: &Strings{
				StringField: String(`This is my gigantic, unhappy string.  It exceeds
the encoding size of a single byte varint.  We are using it to fuzz test the
correctness of the header decoding mechanisms, which may prove problematic.
I expect it may.  Let's hope you enjoy testing as much as we do.`),
			},
			n: 271,
			buf: []byte{141, 2, 10, 138, 2, 84, 104, 105, 115, 32, 105, 115, 32, 109,
				121, 32, 103, 105, 103, 97, 110, 116, 105, 99, 44, 32, 117, 110, 104,
				97, 112, 112, 121, 32, 115, 116, 114, 105, 110, 103, 46, 32, 32, 73,
				116, 32, 101, 120, 99, 101, 101, 100, 115, 10, 116, 104, 101, 32, 101,
				110, 99, 111, 100, 105, 110, 103, 32, 115, 105, 122, 101, 32, 111, 102,
				32, 97, 32, 115, 105, 110, 103, 108, 101, 32, 98, 121, 116, 101, 32,
				118, 97, 114, 105, 110, 116, 46, 32, 32, 87, 101, 32, 97, 114, 101, 32,
				117, 115, 105, 110, 103, 32, 105, 116, 32, 116, 111, 32, 102, 117, 122,
				122, 32, 116, 101, 115, 116, 32, 116, 104, 101, 10, 99, 111, 114, 114,
				101, 99, 116, 110, 101, 115, 115, 32, 111, 102, 32, 116, 104, 101, 32,
				104, 101, 97, 100, 101, 114, 32, 100, 101, 99, 111, 100, 105, 110, 103,
				32, 109, 101, 99, 104, 97, 110, 105, 115, 109, 115, 44, 32, 119, 104,
				105, 99, 104, 32, 109, 97, 121, 32, 112, 114, 111, 118, 101, 32, 112,
				114, 111, 98, 108, 101, 109, 97, 116, 105, 99, 46, 10, 73, 32, 101, 120,
				112, 101, 99, 116, 32, 105, 116, 32, 109, 97, 121, 46, 32, 32, 76, 101,
				116, 39, 115, 32, 104, 111, 112, 101, 32, 121, 111, 117, 32, 101, 110,
				106, 111, 121, 32, 116, 101, 115, 116, 105, 110, 103, 32, 97, 115, 32,
				109, 117, 99, 104, 32, 97, 115, 32, 119, 101, 32, 100, 111, 46},
		},
	} {
		var buf bytes.Buffer
		if n, err := WriteDelimited(&buf, test.msg); n != test.n || err != test.err {
			t.Fatalf("WriteDelimited(buf, %#v) = %v, %v; want %v, %v", test.msg, n, err, test.n, test.err)
		}
		if out := buf.Bytes(); !bytes.Equal(out, test.buf) {
			t.Fatalf("WriteDelimited(buf, %#v); buf = %v; want %v", test.msg, out, test.buf)
		}
	}
}

func TestReadDelimited(t *testing.T) {
	for _, test := range []struct {
		buf []byte
		msg Message
		n   int
		err error
	}{
		{
			buf: []byte{0},
			msg: &Empty{},
			n:   1,
		},
		{
			n:   3,
			buf: []byte{2, 8, 1},
			msg: &GoEnum{Foo: FOO_FOO1.Enum()},
		},
		{
			buf: []byte{141, 2, 10, 138, 2, 84, 104, 105, 115, 32, 105, 115, 32, 109,
				121, 32, 103, 105, 103, 97, 110, 116, 105, 99, 44, 32, 117, 110, 104,
				97, 112, 112, 121, 32, 115, 116, 114, 105, 110, 103, 46, 32, 32, 73,
				116, 32, 101, 120, 99, 101, 101, 100, 115, 10, 116, 104, 101, 32, 101,
				110, 99, 111, 100, 105, 110, 103, 32, 115, 105, 122, 101, 32, 111, 102,
				32, 97, 32, 115, 105, 110, 103, 108, 101, 32, 98, 121, 116, 101, 32,
				118, 97, 114, 105, 110, 116, 46, 32, 32, 87, 101, 32, 97, 114, 101, 32,
				117, 115, 105, 110, 103, 32, 105, 116, 32, 116, 111, 32, 102, 117, 122,
				122, 32, 116, 101, 115, 116, 32, 116, 104, 101, 10, 99, 111, 114, 114,
				101, 99, 116, 110, 101, 115, 115, 32, 111, 102, 32, 116, 104, 101, 32,
				104, 101, 97, 100, 101, 114, 32, 100, 101, 99, 111, 100, 105, 110, 103,
				32, 109, 101, 99, 104, 97, 110, 105, 115, 109, 115, 44, 32, 119, 104,
				105, 99, 104, 32, 109, 97, 121, 32, 112, 114, 111, 118, 101, 32, 112,
				114, 111, 98, 108, 101, 109, 97, 116, 105, 99, 46, 10, 73, 32, 101, 120,
				112, 101, 99, 116, 32, 105, 116, 32, 109, 97, 121, 46, 32, 32, 76, 101,
				116, 39, 115, 32, 104, 111, 112, 101, 32, 121, 111, 117, 32, 101, 110,
				106, 111, 121, 32, 116, 101, 115, 116, 105, 110, 103, 32, 97, 115, 32,
				109, 117, 99, 104, 32, 97, 115, 32, 119, 101, 32, 100, 111, 46},
			msg: &Strings{
				StringField: String(`This is my gigantic, unhappy string.  It exceeds
the encoding size of a single byte varint.  We are using it to fuzz test the
correctness of the header decoding mechanisms, which may prove problematic.
I expect it may.  Let's hope you enjoy testing as much as we do.`),
			},
			n: 271,
		},
	} {
		msg := Clone(test.msg)
		msg.Reset()
		if n, err := ReadDelimited(bytes.NewBuffer(test.buf), msg); n != test.n || err != test.err {
			t.Fatalf("ReadDelimited(%v, msg) = %v, %v; want %v, %v", test.buf, n, err, test.n, test.err)
		}
		if !Equal(msg, test.msg) {
			t.Fatalf("ReadDelimited(%v, msg); msg = %v; want %v", test.buf, msg, test.msg)
		}
	}
}

func TestEndToEndValid(t *testing.T) {
	for _, test := range [][]Message{
		{&Empty{}},
		{&GoEnum{Foo: FOO_FOO1.Enum()}, &Empty{}, &GoEnum{Foo: FOO_FOO1.Enum()}},
		{&GoEnum{Foo: FOO_FOO1.Enum()}},
		{&Strings{
			StringField: String(`This is my gigantic, unhappy string.  It exceeds
the encoding size of a single byte varint.  We are using it to fuzz test the
correctness of the header decoding mechanisms, which may prove problematic.
I expect it may.  Let's hope you enjoy testing as much as we do.`),
		}},
	} {
		var buf bytes.Buffer
		var written int
		for i, msg := range test {
			n, err := WriteDelimited(&buf, msg)
			if err != nil {
				// Assumption: TestReadDelimited and TestWriteDelimited are sufficient
				//             and inputs for this test are explicitly exercised there.
				t.Fatalf("WriteDelimited(buf, %v[%d]) = ?, %v; wanted ?, nil", test, i, err)
			}
			written += n
		}
		var read int
		for i, msg := range test {
			out := Clone(msg)
			out.Reset()
			n, _ := ReadDelimited(&buf, out)
			// Decide to do EOF checking?
			read += n
			if !Equal(out, msg) {
				t.Fatalf("out = %v; want %v[%d] = %#v", out, test, i, msg)
			}
		}
		if read != written {
			t.Fatalf("%v read = %d; want %d", test, read, written)
		}
	}
}

// rndMessage generates a random valid Protocol Buffer message.
func rndMessage(r *rand.Rand) Message {
	var t reflect.Type
	switch v := rand.Intn(23); v {
	// TODO(br): Uncomment the elements below once fix is incorporated, except
	//           for the elements marked as patently incompatible.
	// case 0:
	// 	t = reflect.TypeOf(&GoEnum{})
	// 	break
	// case 1:
	// 	t = reflect.TypeOf(&GoTestField{})
	// 	break
	case 2:
		t = reflect.TypeOf(&GoTest{})
		break
		// case 3:
		// 	t = reflect.TypeOf(&GoSkipTest{})
		// 	break
		// case 4:
		// 	t = reflect.TypeOf(&NonPackedTest{})
		// 	break
		// case 5:
		// t = reflect.TypeOf(&PackedTest{})
		//	break
	case 6:
		t = reflect.TypeOf(&MaxTag{})
		break
	case 7:
		t = reflect.TypeOf(&OldMessage{})
		break
	case 8:
		t = reflect.TypeOf(&NewMessage{})
		break
	case 9:
		t = reflect.TypeOf(&InnerMessage{})
		break
	case 10:
		t = reflect.TypeOf(&OtherMessage{})
		break
	case 11:
		// PATENTLY INVALID FOR FUZZ GENERATION
		// t = reflect.TypeOf(&MyMessage{})
		break
		// case 12:
		// 	t = reflect.TypeOf(&Ext{})
		// 	break
	case 13:
		// PATENTLY INVALID FOR FUZZ GENERATION
		// t = reflect.TypeOf(&MyMessageSet{})
		break
		// case 14:
		//   t = reflect.TypeOf(&Empty{})
		//   break
		// case 15:
		// t = reflect.TypeOf(&MessageList{})
		// break
		// case 16:
		// 	t = reflect.TypeOf(&Strings{})
		// 	break
		// case 17:
		// 	t = reflect.TypeOf(&Defaults{})
		// 	break
		// case 17:
		// 	t = reflect.TypeOf(&SubDefaults{})
		// 	break
		// case 18:
		// 	t = reflect.TypeOf(&RepeatedEnum{})
		// 	break
	case 19:
		t = reflect.TypeOf(&MoreRepeated{})
		break
		// case 20:
		// 	t = reflect.TypeOf(&GroupOld{})
		// 	break
		// case 21:
		// 	t = reflect.TypeOf(&GroupNew{})
		// 	break
	case 22:
		t = reflect.TypeOf(&FloatingPoint{})
		break
	default:
		// TODO(br): Replace with an unreachable once fixed.
		t = reflect.TypeOf(&GoTest{})
		break
	}
	if t == nil {
		t = reflect.TypeOf(&GoTest{})
	}
	v, ok := quick.Value(t, r)
	if !ok {
		panic("attempt to generate illegal item; consult item 11")
	}
	if err := pbtest.SanitizeGenerated(v.Interface().(Message)); err != nil {
		panic(err)
	}
	return v.Interface().(Message)
}

// rndMessages generates several random Protocol Buffer messages.
func rndMessages(r *rand.Rand) []Message {
	n := r.Intn(128)
	out := make([]Message, 0, n)
	for i := 0; i < n; i++ {
		out = append(out, rndMessage(r))
	}
	return out
}

func TestFuzz(t *testing.T) {
	rnd := rand.New(rand.NewSource(42))
	check := func() bool {
		messages := rndMessages(rnd)
		var buf bytes.Buffer
		var written int
		for i, msg := range messages {
			n, err := WriteDelimited(&buf, msg)
			if err != nil {
				t.Fatalf("WriteDelimited(buf, %v[%d]) = ?, %v; wanted ?, nil", messages, i, err)
			}
			written += n
		}
		var read int
		for i, msg := range messages {
			out := Clone(msg)
			out.Reset()
			n, _ := ReadDelimited(&buf, out)
			read += n
			if !Equal(out, msg) {
				t.Fatalf("out = %v; want %v[%d] = %#v", out, messages, i, msg)
			}
		}
		if read != written {
			t.Fatalf("%v read = %d; want %d", messages, read, written)
		}
		return true
	}
	if err := quick.Check(check, nil); err != nil {
		t.Fatal(err)
	}
}
