// $G $D/$F.go && $L $F.$A && ./$A.out

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

// Test close(c), closed(c).
//
// TODO(rsc): Doesn't check behavior of close(c) when there
// are blocked senders/receivers.

package main

type Chan interface {
	Send(int);
	Nbsend(int) bool;
	Recv() int;
	Nbrecv() (int, bool);
	Close();
	Closed() bool;
	Impl() string;
}

// direct channel operations
type XChan chan int
func (c XChan) Send(x int) {
	c <- x
}

func (c XChan) Nbsend(x int) bool {
	return c <- x;
}

func (c XChan) Recv() int {
	return <-c
}

func (c XChan) Nbrecv() (int, bool) {
	x, ok := <-c;
	return x, ok;
}

func (c XChan) Close() {
	close(c)
}

func (c XChan) Closed() bool {
	return closed(c)
}

func (c XChan) Impl() string {
	return "(<- operator)"
}

// indirect operations via select
type SChan chan int
func (c SChan) Send(x int) {
	select {
	case c <- x:
	}
}

func (c SChan) Nbsend(x int) bool {
	select {
	case c <- x:
		return true;
	default:
		return false;
	}
	panic("nbsend");
}

func (c SChan) Recv() int {
	select {
	case x := <-c:
		return x;
	}
	panic("recv");
}

func (c SChan) Nbrecv() (int, bool) {
	select {
	case x := <-c:
		return x, true;
	default:
		return 0, false;
	}
	panic("nbrecv");
}

func (c SChan) Close() {
	close(c)
}

func (c SChan) Closed() bool {
	return closed(c)
}

func (c SChan) Impl() string {
	return "(select)";
}

func test1(c Chan) {
	// not closed until the close signal (a zero value) has been received.
	if c.Closed() {
		println("test1: Closed before Recv zero:", c.Impl());
	}

	for i := 0; i < 3; i++ {
		// recv a close signal (a zero value)
		if x := c.Recv(); x != 0 {
			println("test1: recv on closed got non-zero:", x, c.Impl());
		}

		// should now be closed.
		if !c.Closed() {
			println("test1: not closed after recv zero", c.Impl());
		}

		// should work with ,ok: received a value without blocking, so ok == true.
		x, ok := c.Nbrecv();
		if !ok {
			println("test1: recv on closed got not ok", c.Impl());
		}
		if x != 0 {
			println("test1: recv ,ok on closed got non-zero:", x, c.Impl());
		}
	}

	// send should work with ,ok too: sent a value without blocking, so ok == true.
	ok := c.Nbsend(1);
	if !ok {
		println("test1: send on closed got not ok", c.Impl());
	}

	// but the value should have been discarded.
	if x := c.Recv(); x != 0 {
		println("test1: recv on closed got non-zero after send on closed:", x, c.Impl());
	}

	// similarly Send.
	c.Send(2);
	if x := c.Recv(); x != 0 {
		println("test1: recv on closed got non-zero after send on closed:", x, c.Impl());
	}
}

func testasync1(c Chan) {
	// not closed until the close signal (a zero value) has been received.
	if c.Closed() {
		println("testasync1: Closed before Recv zero:", c.Impl());
	}

	// should be able to get the last value via Recv
	if x := c.Recv(); x != 1 {
		println("testasync1: Recv did not get 1:", x, c.Impl());
	}

	test1(c);
}

func testasync2(c Chan) {
	// not closed until the close signal (a zero value) has been received.
	if c.Closed() {
		println("testasync2: Closed before Recv zero:", c.Impl());
	}

	// should be able to get the last value via Nbrecv
	if x, ok := c.Nbrecv(); !ok || x != 1 {
		println("testasync2: Nbrecv did not get 1, true:", x, ok, c.Impl());
	}

	test1(c);
}

func closedsync() chan int {
	c := make(chan int);
	close(c);
	return c;
}

func closedasync() chan int {
	c := make(chan int, 2);
	c <- 1;
	close(c);
	return c;
}

func main() {
	test1(XChan(closedsync()));
	test1(SChan(closedsync()));

	testasync1(XChan(closedasync()));
	testasync1(SChan(closedasync()));
	testasync2(XChan(closedasync()));
	testasync2(SChan(closedasync()));
}
