blob: e7ca9e015c1c228d334151c4b0188a625ed7ff68 [file] [log] [blame]
// $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(out, a); 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
}