// Copyright 2012 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.

// This file implements API tests across platforms and will never have a build
// tag.

package net

import (
	"os"
	"runtime"
	"testing"
	"time"
)

var connTests = []struct {
	net  string
	addr string
}{
	{"tcp", "127.0.0.1:0"},
	{"unix", testUnixAddr()},
	{"unixpacket", testUnixAddr()},
}

// someTimeout is used just to test that net.Conn implementations
// don't explode when their SetFooDeadline methods are called.
// It isn't actually used for testing timeouts.
const someTimeout = 10 * time.Second

func TestConnAndListener(t *testing.T) {
	for _, tt := range connTests {
		switch tt.net {
		case "unix":
			switch runtime.GOOS {
			case "nacl", "plan9", "windows":
				continue
			}
		case "unixpacket":
			switch runtime.GOOS {
			case "android", "darwin", "nacl", "openbsd", "plan9", "windows":
				continue
			case "freebsd": // FreeBSD 8 doesn't support unixpacket
				continue
			}
		}

		ln, err := Listen(tt.net, tt.addr)
		if err != nil {
			t.Fatalf("Listen failed: %v", err)
		}
		defer func(ln Listener, net, addr string) {
			ln.Close()
			switch net {
			case "unix", "unixpacket":
				os.Remove(addr)
			}
		}(ln, tt.net, tt.addr)
		if ln.Addr().Network() != tt.net {
			t.Fatalf("got %v; expected %v", ln.Addr().Network(), tt.net)
		}

		done := make(chan int)
		go transponder(t, ln, done)

		c, err := Dial(tt.net, ln.Addr().String())
		if err != nil {
			t.Fatalf("Dial failed: %v", err)
		}
		defer c.Close()
		if c.LocalAddr().Network() != tt.net || c.LocalAddr().Network() != tt.net {
			t.Fatalf("got %v->%v; expected %v->%v", c.LocalAddr().Network(), c.RemoteAddr().Network(), tt.net, tt.net)
		}
		c.SetDeadline(time.Now().Add(someTimeout))
		c.SetReadDeadline(time.Now().Add(someTimeout))
		c.SetWriteDeadline(time.Now().Add(someTimeout))

		if _, err := c.Write([]byte("CONN TEST")); err != nil {
			t.Fatalf("Conn.Write failed: %v", err)
		}
		rb := make([]byte, 128)
		if _, err := c.Read(rb); err != nil {
			t.Fatalf("Conn.Read failed: %v", err)
		}

		<-done
	}
}

func transponder(t *testing.T, ln Listener, done chan<- int) {
	defer func() { done <- 1 }()

	switch ln := ln.(type) {
	case *TCPListener:
		ln.SetDeadline(time.Now().Add(someTimeout))
	case *UnixListener:
		ln.SetDeadline(time.Now().Add(someTimeout))
	}
	c, err := ln.Accept()
	if err != nil {
		t.Errorf("Listener.Accept failed: %v", err)
		return
	}
	defer c.Close()
	network := ln.Addr().Network()
	if c.LocalAddr().Network() != network || c.LocalAddr().Network() != network {
		t.Errorf("got %v->%v; expected %v->%v", c.LocalAddr().Network(), c.RemoteAddr().Network(), network, network)
		return
	}
	c.SetDeadline(time.Now().Add(someTimeout))
	c.SetReadDeadline(time.Now().Add(someTimeout))
	c.SetWriteDeadline(time.Now().Add(someTimeout))

	b := make([]byte, 128)
	n, err := c.Read(b)
	if err != nil {
		t.Errorf("Conn.Read failed: %v", err)
		return
	}
	if _, err := c.Write(b[:n]); err != nil {
		t.Errorf("Conn.Write failed: %v", err)
		return
	}
}
