// Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package spdy

import (
	"bytes"
	"compress/zlib"
	"http"
	"os"
	"testing"
)

type frameIoTest struct {
	desc      string
	data      []byte
	frame     Frame
	readError os.Error
	readOnly  bool
}

var frameIoTests = []frameIoTest{
	{
		"noop frame",
		[]byte{
			0x80, 0x02, 0x00, 0x05,
			0x00, 0x00, 0x00, 0x00,
		},
		ControlFrame(
			TypeNoop,
			0x00,
			[]byte{},
		),
		nil,
		false,
	},
	{
		"ping frame",
		[]byte{
			0x80, 0x02, 0x00, 0x06,
			0x00, 0x00, 0x00, 0x04,
			0x00, 0x00, 0x00, 0x01,
		},
		ControlFrame(
			TypePing,
			0x00,
			[]byte{0x00, 0x00, 0x00, 0x01},
		),
		nil,
		false,
	},
	{
		"syn_stream frame",
		[]byte{
			0x80, 0x02, 0x00, 0x01,
			0x01, 0x00, 0x00, 0x53,
			0x00, 0x00, 0x00, 0x01,
			0x00, 0x00, 0x00, 0x00,
			0x00, 0x00, 0x78, 0xbb,
			0xdf, 0xa2, 0x51, 0xb2,
			0x62, 0x60, 0x66, 0x60,
			0xcb, 0x4d, 0x2d, 0xc9,
			0xc8, 0x4f, 0x61, 0x60,
			0x4e, 0x4f, 0x2d, 0x61,
			0x60, 0x2e, 0x2d, 0xca,
			0x61, 0x10, 0xcb, 0x28,
			0x29, 0x29, 0xb0, 0xd2,
			0xd7, 0x2f, 0x2f, 0x2f,
			0xd7, 0x4b, 0xcf, 0xcf,
			0x4f, 0xcf, 0x49, 0xd5,
			0x4b, 0xce, 0xcf, 0xd5,
			0x67, 0x60, 0x2f, 0x4b,
			0x2d, 0x2a, 0xce, 0xcc,
			0xcf, 0x63, 0xe0, 0x00,
			0x29, 0xd0, 0x37, 0xd4,
			0x33, 0x04, 0x00, 0x00,
			0x00, 0xff, 0xff,
		},
		ControlFrame(
			TypeSynStream,
			0x01,
			[]byte{
				0x00, 0x00, 0x00, 0x01,
				0x00, 0x00, 0x00, 0x00,
				0x00, 0x00, 0x78, 0xbb,
				0xdf, 0xa2, 0x51, 0xb2,
				0x62, 0x60, 0x66, 0x60,
				0xcb, 0x4d, 0x2d, 0xc9,
				0xc8, 0x4f, 0x61, 0x60,
				0x4e, 0x4f, 0x2d, 0x61,
				0x60, 0x2e, 0x2d, 0xca,
				0x61, 0x10, 0xcb, 0x28,
				0x29, 0x29, 0xb0, 0xd2,
				0xd7, 0x2f, 0x2f, 0x2f,
				0xd7, 0x4b, 0xcf, 0xcf,
				0x4f, 0xcf, 0x49, 0xd5,
				0x4b, 0xce, 0xcf, 0xd5,
				0x67, 0x60, 0x2f, 0x4b,
				0x2d, 0x2a, 0xce, 0xcc,
				0xcf, 0x63, 0xe0, 0x00,
				0x29, 0xd0, 0x37, 0xd4,
				0x33, 0x04, 0x00, 0x00,
				0x00, 0xff, 0xff,
			},
		),
		nil,
		false,
	},
	{
		"data frame",
		[]byte{
			0x00, 0x00, 0x00, 0x05,
			0x01, 0x00, 0x00, 0x04,
			0x01, 0x02, 0x03, 0x04,
		},
		DataFrame(
			5,
			0x01,
			[]byte{0x01, 0x02, 0x03, 0x04},
		),
		nil,
		false,
	},
	{
		"too much data",
		[]byte{
			0x00, 0x00, 0x00, 0x05,
			0x01, 0x00, 0x00, 0x04,
			0x01, 0x02, 0x03, 0x04,
			0x05, 0x06, 0x07, 0x08,
		},
		DataFrame(
			5,
			0x01,
			[]byte{0x01, 0x02, 0x03, 0x04},
		),
		nil,
		true,
	},
	{
		"not enough data",
		[]byte{
			0x00, 0x00, 0x00, 0x05,
		},
		Frame{},
		os.EOF,
		true,
	},
}

func TestReadFrame(t *testing.T) {
	for _, tt := range frameIoTests {
		f, err := ReadFrame(bytes.NewBuffer(tt.data))
		if err != tt.readError {
			t.Errorf("%s: ReadFrame: %s", tt.desc, err)
			continue
		}
		if err == nil {
			if !bytes.Equal(f.Header[:], tt.frame.Header[:]) {
				t.Errorf("%s: header %q != %q", tt.desc, string(f.Header[:]), string(tt.frame.Header[:]))
			}
			if f.Flags != tt.frame.Flags {
				t.Errorf("%s: flags %#02x != %#02x", tt.desc, f.Flags, tt.frame.Flags)
			}
			if !bytes.Equal(f.Data, tt.frame.Data) {
				t.Errorf("%s: data %q != %q", tt.desc, string(f.Data), string(tt.frame.Data))
			}
		}
	}
}

func TestWriteTo(t *testing.T) {
	for _, tt := range frameIoTests {
		if tt.readOnly {
			continue
		}
		b := new(bytes.Buffer)
		_, err := tt.frame.WriteTo(b)
		if err != nil {
			t.Errorf("%s: WriteTo: %s", tt.desc, err)
		}
		if !bytes.Equal(b.Bytes(), tt.data) {
			t.Errorf("%s: data %q != %q", tt.desc, string(b.Bytes()), string(tt.data))
		}
	}
}

var headerDataTest = []byte{
	0x78, 0xbb, 0xdf, 0xa2,
	0x51, 0xb2, 0x62, 0x60,
	0x66, 0x60, 0xcb, 0x4d,
	0x2d, 0xc9, 0xc8, 0x4f,
	0x61, 0x60, 0x4e, 0x4f,
	0x2d, 0x61, 0x60, 0x2e,
	0x2d, 0xca, 0x61, 0x10,
	0xcb, 0x28, 0x29, 0x29,
	0xb0, 0xd2, 0xd7, 0x2f,
	0x2f, 0x2f, 0xd7, 0x4b,
	0xcf, 0xcf, 0x4f, 0xcf,
	0x49, 0xd5, 0x4b, 0xce,
	0xcf, 0xd5, 0x67, 0x60,
	0x2f, 0x4b, 0x2d, 0x2a,
	0xce, 0xcc, 0xcf, 0x63,
	0xe0, 0x00, 0x29, 0xd0,
	0x37, 0xd4, 0x33, 0x04,
	0x00, 0x00, 0x00, 0xff,
	0xff,
}

func TestReadHeader(t *testing.T) {
	r := NewHeaderReader()
	h, err := r.Decode(headerDataTest)
	if err != nil {
		t.Fatalf("Error: %v", err)
		return
	}
	if len(h) != 3 {
		t.Errorf("Header count = %d (expected 3)", len(h))
	}
	if h.Get("Url") != "http://www.google.com/" {
		t.Errorf("Url: %q != %q", h.Get("Url"), "http://www.google.com/")
	}
	if h.Get("Method") != "get" {
		t.Errorf("Method: %q != %q", h.Get("Method"), "get")
	}
	if h.Get("Version") != "http/1.1" {
		t.Errorf("Version: %q != %q", h.Get("Version"), "http/1.1")
	}
}

func TestWriteHeader(t *testing.T) {
	for level := zlib.NoCompression; level <= zlib.BestCompression; level++ {
		r := NewHeaderReader()
		w := NewHeaderWriter(level)
		for i := 0; i < 100; i++ {
			b := new(bytes.Buffer)
			gold := http.Header{
				"Url":     []string{"http://www.google.com/"},
				"Method":  []string{"get"},
				"Version": []string{"http/1.1"},
			}
			w.WriteHeader(b, gold)
			h, err := r.Decode(b.Bytes())
			if err != nil {
				t.Errorf("(level=%d i=%d) Error: %v", level, i, err)
				return
			}
			if len(h) != len(gold) {
				t.Errorf("(level=%d i=%d) Header count = %d (expected %d)", level, i, len(h), len(gold))
			}
			for k, _ := range h {
				if h.Get(k) != gold.Get(k) {
					t.Errorf("(level=%d i=%d) %s: %q != %q", level, i, k, h.Get(k), gold.Get(k))
				}
			}
		}
	}
}
