test: another select test

R=r
CC=golang-dev
https://golang.org/cl/4004044
This commit is contained in:
Russ Cox 2011-01-30 15:46:02 -05:00
parent 08a206cc9e
commit 7247d6b96f

482
test/chan/select5.go Normal file
View file

@ -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
}