aboutsummaryrefslogtreecommitdiff
path: root/test/chan
diff options
context:
space:
mode:
authorRuss Cox <rsc@golang.org>2011-01-30 15:46:02 -0500
committerRuss Cox <rsc@golang.org>2011-01-30 15:46:02 -0500
commit7247d6b96f639d266828c33566a71697272a67da (patch)
tree0cd0f47a4d177038854a1dfee414dec9257ca8df /test/chan
parent08a206cc9e5998c195a8d3cc900984025732a79e (diff)
downloadgo-7247d6b96f639d266828c33566a71697272a67da.tar.gz
go-7247d6b96f639d266828c33566a71697272a67da.zip
test: another select test
R=r CC=golang-dev https://golang.org/cl/4004044
Diffstat (limited to 'test/chan')
-rw-r--r--test/chan/select5.go482
1 files changed, 482 insertions, 0 deletions
diff --git a/test/chan/select5.go b/test/chan/select5.go
new file mode 100644
index 0000000000..0678b8dab6
--- /dev/null
+++ b/test/chan/select5.go
@@ -0,0 +1,482 @@
+// $G $D/$F.go && $L $F.$A && ./$A.out >tmp.go &&
+// $G tmp.go && $L tmp.$A && ./$A.out || echo BUG: select5
+// rm -f tmp.go
+
+// 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.
+
+// Generate test of channel operations and simple selects.
+// Only doing one real send or receive at a time, but phrased
+// in various ways that the compiler may or may not rewrite
+// into simpler expressions.
+
+package main
+
+import (
+ "bufio"
+ "fmt"
+ "io"
+ "os"
+ "template"
+)
+
+func main() {
+ out := bufio.NewWriter(os.Stdout)
+ fmt.Fprintln(out, header)
+ a := new(arg)
+
+ // Generate each kind of test as a separate function to avoid
+ // hitting the 6g optimizer with one enormous function.
+ // If we name all the functions init we don't have to
+ // maintain a list of which ones to run.
+ do := func(t *template.Template) {
+ fmt.Fprintln(out, `func init() {`)
+ for ; next(); a.reset() {
+ run(t, a, out)
+ }
+ fmt.Fprintln(out, `}`)
+ }
+
+ do(recv)
+ do(send)
+ do(recvOrder)
+ do(sendOrder)
+ do(nonblock)
+
+ fmt.Fprintln(out, "//", a.nreset, "cases")
+ out.Flush()
+}
+
+func run(t *template.Template, a interface{}, out io.Writer) {
+ if err := t.Execute(a, out); err != nil {
+ panic(err)
+ }
+}
+
+type arg struct{
+ def bool
+ nreset int
+}
+
+func (a *arg) Maybe() bool {
+ return maybe()
+}
+
+func (a *arg) MaybeDefault() bool {
+ if a.def {
+ return false
+ }
+ a.def = maybe()
+ return a.def
+}
+
+func (a *arg) MustDefault() bool {
+ return !a.def
+}
+
+func (a *arg) reset() {
+ a.def = false
+ a.nreset++
+}
+
+const header = `// GENERATED BY select5.go; DO NOT EDIT
+
+package main
+
+// channel is buffered so test is single-goroutine.
+// we are not interested in the concurrency aspects
+// of select, just testing that the right calls happen.
+var c = make(chan int, 1)
+var nilch chan int
+var n = 1
+var x int
+var i interface{}
+var dummy = make(chan int)
+var m = make(map[int]int)
+var order = 0
+
+func f(p *int) *int {
+ return p
+}
+
+// check order of operations by ensuring that
+// successive calls to checkorder have increasing o values.
+func checkorder(o int) {
+ if o <= order {
+ println("invalid order", o, "after", order)
+ panic("order")
+ }
+ order = o
+}
+
+func fc(c chan int, o int) chan int {
+ checkorder(o)
+ return c
+}
+
+func fp(p *int, o int) *int {
+ checkorder(o)
+ return p
+}
+
+func fn(n, o int) int {
+ checkorder(o)
+ return n
+}
+
+func die(x int) {
+ println("have", x, "want", n)
+ panic("chan")
+}
+
+func main() {
+ // everything happens in init funcs
+}
+`
+
+func parse(s string) *template.Template {
+ t := template.New(nil)
+ t.SetDelims("〈", "〉")
+ if err := t.Parse(s); err != nil {
+ panic(s)
+ }
+ return t
+}
+
+var recv = parse(`
+ 〈# Send n, receive it one way or another into x, check that they match.〉
+ c <- n
+ 〈.section Maybe〉
+ x = <-c
+ 〈.or〉
+ select {
+ 〈# Blocking or non-blocking, before the receive.〉
+ 〈# The compiler implements two-case select where one is default with custom code,〉
+ 〈# so test the default branch both before and after the send.〉
+ 〈.section MaybeDefault〉
+ default:
+ panic("nonblock")
+ 〈.end〉
+ 〈# Receive from c. Different cases are direct, indirect, :=, interface, and map assignment.〉
+ 〈.section Maybe〉
+ case x = <-c:
+ 〈.or〉〈.section Maybe〉
+ case *f(&x) = <-c:
+ 〈.or〉〈.section Maybe〉
+ case y := <-c:
+ x = y
+ 〈.or〉〈.section Maybe〉
+ case i = <-c:
+ x = i.(int)
+ 〈.or〉
+ case m[13] = <-c:
+ x = m[13]
+ 〈.end〉〈.end〉〈.end〉〈.end〉
+ 〈# Blocking or non-blocking again, after the receive.〉
+ 〈.section MaybeDefault〉
+ default:
+ panic("nonblock")
+ 〈.end〉
+ 〈# Dummy send, receive to keep compiler from optimizing select.〉
+ 〈.section Maybe〉
+ case dummy <- 1:
+ panic("dummy send")
+ 〈.end〉
+ 〈.section Maybe〉
+ case <-dummy:
+ panic("dummy receive")
+ 〈.end〉
+ 〈# Nil channel send, receive to keep compiler from optimizing select.〉
+ 〈.section Maybe〉
+ case nilch <- 1:
+ panic("nilch send")
+ 〈.end〉
+ 〈.section Maybe〉
+ case <-nilch:
+ panic("nilch recv")
+ 〈.end〉
+ }
+ 〈.end〉
+ if x != n {
+ die(x)
+ }
+ n++
+`)
+
+var recvOrder = parse(`
+ 〈# Send n, receive it one way or another into x, check that they match.〉
+ 〈# Check order of operations along the way by calling functions that check〉
+ 〈# that the argument sequence is strictly increasing.〉
+ order = 0
+ c <- n
+ 〈.section Maybe〉
+ 〈# Outside of select, left-to-right rule applies.〉
+ 〈# (Inside select, assignment waits until case is chosen,〉
+ 〈# so right hand side happens before anything on left hand side.〉
+ *fp(&x, 1) = <-fc(c, 2)
+ 〈.or〉〈.section Maybe〉
+ m[fn(13, 1)] = <-fc(c, 2)
+ x = m[13]
+ 〈.or〉
+ select {
+ 〈# Blocking or non-blocking, before the receive.〉
+ 〈# The compiler implements two-case select where one is default with custom code,〉
+ 〈# so test the default branch both before and after the send.〉
+ 〈.section MaybeDefault〉
+ default:
+ panic("nonblock")
+ 〈.end〉
+ 〈# Receive from c. Different cases are direct, indirect, :=, interface, and map assignment.〉
+ 〈.section Maybe〉
+ case *fp(&x, 100) = <-fc(c, 1):
+ 〈.or〉〈.section Maybe〉
+ case y := <-fc(c, 1):
+ x = y
+ 〈.or〉〈.section Maybe〉
+ case i = <-fc(c, 1):
+ x = i.(int)
+ 〈.or〉
+ case m[fn(13, 100)] = <-fc(c, 1):
+ x = m[13]
+ 〈.end〉〈.end〉〈.end〉
+ 〈# Blocking or non-blocking again, after the receive.〉
+ 〈.section MaybeDefault〉
+ default:
+ panic("nonblock")
+ 〈.end〉
+ 〈# Dummy send, receive to keep compiler from optimizing select.〉
+ 〈.section Maybe〉
+ case fc(dummy, 2) <- fn(1, 3):
+ panic("dummy send")
+ 〈.end〉
+ 〈.section Maybe〉
+ case <-fc(dummy, 4):
+ panic("dummy receive")
+ 〈.end〉
+ 〈# Nil channel send, receive to keep compiler from optimizing select.〉
+ 〈.section Maybe〉
+ case fc(nilch, 5) <- fn(1, 6):
+ panic("nilch send")
+ 〈.end〉
+ 〈.section Maybe〉
+ case <-fc(nilch, 7):
+ panic("nilch recv")
+ 〈.end〉
+ }
+ 〈.end〉〈.end〉
+ if x != n {
+ die(x)
+ }
+ n++
+`)
+
+var send = parse(`
+ 〈# Send n one way or another, receive it into x, check that they match.〉
+ 〈.section Maybe〉
+ c <- n
+ 〈.or〉
+ select {
+ 〈# Blocking or non-blocking, before the receive (same reason as in recv).〉
+ 〈.section MaybeDefault〉
+ default:
+ panic("nonblock")
+ 〈.end〉
+ 〈# Send c <- n. No real special cases here, because no values come back〉
+ 〈# from the send operation.〉
+ case c <- n:
+ 〈# Blocking or non-blocking.〉
+ 〈.section MaybeDefault〉
+ default:
+ panic("nonblock")
+ 〈.end〉
+ 〈# Dummy send, receive to keep compiler from optimizing select.〉
+ 〈.section Maybe〉
+ case dummy <- 1:
+ panic("dummy send")
+ 〈.end〉
+ 〈.section Maybe〉
+ case <-dummy:
+ panic("dummy receive")
+ 〈.end〉
+ 〈# Nil channel send, receive to keep compiler from optimizing select.〉
+ 〈.section Maybe〉
+ case nilch <- 1:
+ panic("nilch send")
+ 〈.end〉
+ 〈.section Maybe〉
+ case <-nilch:
+ panic("nilch recv")
+ 〈.end〉
+ }
+ 〈.end〉
+ x = <-c
+ if x != n {
+ die(x)
+ }
+ n++
+`)
+
+var sendOrder = parse(`
+ 〈# Send n one way or another, receive it into x, check that they match.〉
+ 〈# Check order of operations along the way by calling functions that check〉
+ 〈# that the argument sequence is strictly increasing.〉
+ order = 0
+ 〈.section Maybe〉
+ fc(c, 1) <- fn(n, 2)
+ 〈.or〉
+ select {
+ 〈# Blocking or non-blocking, before the receive (same reason as in recv).〉
+ 〈.section MaybeDefault〉
+ default:
+ panic("nonblock")
+ 〈.end〉
+ 〈# Send c <- n. No real special cases here, because no values come back〉
+ 〈# from the send operation.〉
+ case fc(c, 1) <- fn(n, 2):
+ 〈# Blocking or non-blocking.〉
+ 〈.section MaybeDefault〉
+ default:
+ panic("nonblock")
+ 〈.end〉
+ 〈# Dummy send, receive to keep compiler from optimizing select.〉
+ 〈.section Maybe〉
+ case fc(dummy, 3) <- fn(1, 4):
+ panic("dummy send")
+ 〈.end〉
+ 〈.section Maybe〉
+ case <-fc(dummy, 5):
+ panic("dummy receive")
+ 〈.end〉
+ 〈# Nil channel send, receive to keep compiler from optimizing select.〉
+ 〈.section Maybe〉
+ case fc(nilch, 6) <- fn(1, 7):
+ panic("nilch send")
+ 〈.end〉
+ 〈.section Maybe〉
+ case <-fc(nilch, 8):
+ panic("nilch recv")
+ 〈.end〉
+ }
+ 〈.end〉
+ x = <-c
+ if x != n {
+ die(x)
+ }
+ n++
+`)
+
+var nonblock = parse(`
+ x = n
+ 〈# Test various combinations of non-blocking operations.〉
+ 〈# Receive assignments must not edit or even attempt to compute the address of the lhs.〉
+ select {
+ 〈.section MaybeDefault〉
+ default:
+ 〈.end〉
+ 〈.section Maybe〉
+ case dummy <- 1:
+ panic("dummy <- 1")
+ 〈.end〉
+ 〈.section Maybe〉
+ case nilch <- 1:
+ panic("nilch <- 1")
+ 〈.end〉
+ 〈.section Maybe〉
+ case <-dummy:
+ panic("<-dummy")
+ 〈.end〉
+ 〈.section Maybe〉
+ case x = <-dummy:
+ panic("<-dummy x")
+ 〈.end〉
+ 〈.section Maybe〉
+ case **(**int)(nil) = <-dummy:
+ panic("<-dummy (and didn't crash saving result!)")
+ 〈.end〉
+ 〈.section Maybe〉
+ case <-nilch:
+ panic("<-nilch")
+ 〈.end〉
+ 〈.section Maybe〉
+ case x = <-nilch:
+ panic("<-nilch x")
+ 〈.end〉
+ 〈.section Maybe〉
+ case **(**int)(nil) = <-nilch:
+ panic("<-nilch (and didn't crash saving result!)")
+ 〈.end〉
+ 〈.section MustDefault〉
+ default:
+ 〈.end〉
+ }
+ if x != n {
+ die(x)
+ }
+ n++
+`)
+
+// Code for enumerating all possible paths through
+// some logic. The logic should call choose(n) when
+// it wants to choose between n possibilities.
+// On successive runs through the logic, choose(n)
+// will return 0, 1, ..., n-1. The helper maybe() is
+// similar but returns true and then false.
+//
+// Given a function gen that generates an output
+// using choose and maybe, code can generate all
+// possible outputs using
+//
+// for next() {
+// gen()
+// }
+
+type choice struct {
+ i, n int
+}
+
+var choices []choice
+var cp int = -1
+
+func maybe() bool {
+ return choose(2) == 0
+}
+
+func choose(n int) int {
+ if cp >= len(choices) {
+ // never asked this before: start with 0.
+ choices = append(choices, choice{0, n})
+ cp = len(choices)
+ return 0
+ }
+ // otherwise give recorded answer
+ if n != choices[cp].n {
+ panic("inconsistent choices")
+ }
+ i := choices[cp].i
+ cp++
+ return i
+}
+
+func next() bool {
+ if cp < 0 {
+ // start a new round
+ cp = 0
+ return true
+ }
+
+ // increment last choice sequence
+ cp = len(choices)-1
+ for cp >= 0 && choices[cp].i == choices[cp].n-1 {
+ cp--
+ }
+ if cp < 0 {
+ choices = choices[:0]
+ return false
+ }
+ choices[cp].i++
+ choices = choices[:cp+1]
+ cp = 0
+ return true
+}
+