]> Cypherpunks repositories - gostls13.git/commitdiff
test: another select test
authorRuss Cox <rsc@golang.org>
Sun, 30 Jan 2011 20:46:02 +0000 (15:46 -0500)
committerRuss Cox <rsc@golang.org>
Sun, 30 Jan 2011 20:46:02 +0000 (15:46 -0500)
R=r
CC=golang-dev
https://golang.org/cl/4004044

test/chan/select5.go [new file with mode: 0644]

diff --git a/test/chan/select5.go b/test/chan/select5.go
new file mode 100644 (file)
index 0000000..0678b8d
--- /dev/null
@@ -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
+}
+