You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

482 lines
10 KiB

// runoutput
// 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.
// The output of this program is compiled and run to do the
// actual test.
// Each test does only 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"
"text/template"
)
func main() {
out := bufio.NewWriter(os.Stdout)
fmt.Fprintln(out, header)
a := new(arg)
// Generate each test as a separate function to avoid
// hitting the gc 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) {
for ; next(); a.reset() {
fmt.Fprintln(out, `func init() {`)
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(name, s string) *template.Template {
t, err := template.New(name).Parse(s)
if err != nil {
panic(fmt.Sprintf("%q: %s", name, err))
}
return t
}
var recv = parse("recv", `
{{/* Send n, receive it one way or another into x, check that they match. */}}
c <- n
{{if .Maybe}}
x = <-c
{{else}}
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. */}}
{{if .MaybeDefault}}
default:
panic("nonblock")
{{end}}
{{/* Receive from c. Different cases are direct, indirect, :=, interface, and map assignment. */}}
{{if .Maybe}}
case x = <-c:
{{else}}{{if .Maybe}}
case *f(&x) = <-c:
{{else}}{{if .Maybe}}
case y := <-c:
x = y
{{else}}{{if .Maybe}}
case i = <-c:
x = i.(int)
{{else}}
case m[13] = <-c:
x = m[13]
{{end}}{{end}}{{end}}{{end}}
{{/* Blocking or non-blocking again, after the receive. */}}
{{if .MaybeDefault}}
default:
panic("nonblock")
{{end}}
{{/* Dummy send, receive to keep compiler from optimizing select. */}}
{{if .Maybe}}
case dummy <- 1:
panic("dummy send")
{{end}}
{{if .Maybe}}
case <-dummy:
panic("dummy receive")
{{end}}
{{/* Nil channel send, receive to keep compiler from optimizing select. */}}
{{if .Maybe}}
case nilch <- 1:
panic("nilch send")
{{end}}
{{if .Maybe}}
case <-nilch:
panic("nilch recv")
{{end}}
}
{{end}}
if x != n {
die(x)
}
n++
`)
var recvOrder = parse("recvOrder", `
{{/* 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
{{if .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)
{{else}}{{if .Maybe}}
m[fn(13, 1)] = <-fc(c, 2)
x = m[13]
{{else}}
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. */}}
{{if .MaybeDefault}}
default:
panic("nonblock")
{{end}}
{{/* Receive from c. Different cases are direct, indirect, :=, interface, and map assignment. */}}
{{if .Maybe}}
case *fp(&x, 100) = <-fc(c, 1):
{{else}}{{if .Maybe}}
case y := <-fc(c, 1):
x = y
{{else}}{{if .Maybe}}
case i = <-fc(c, 1):
x = i.(int)
{{else}}
case m[fn(13, 100)] = <-fc(c, 1):
x = m[13]
{{end}}{{end}}{{end}}
{{/* Blocking or non-blocking again, after the receive. */}}
{{if .MaybeDefault}}
default:
panic("nonblock")
{{end}}
{{/* Dummy send, receive to keep compiler from optimizing select. */}}
{{if .Maybe}}
case fc(dummy, 2) <- fn(1, 3):
panic("dummy send")
{{end}}
{{if .Maybe}}
case <-fc(dummy, 4):
panic("dummy receive")
{{end}}
{{/* Nil channel send, receive to keep compiler from optimizing select. */}}
{{if .Maybe}}
case fc(nilch, 5) <- fn(1, 6):
panic("nilch send")
{{end}}
{{if .Maybe}}
case <-fc(nilch, 7):
panic("nilch recv")
{{end}}
}
{{end}}{{end}}
if x != n {
die(x)
}
n++
`)
var send = parse("send", `
{{/* Send n one way or another, receive it into x, check that they match. */}}
{{if .Maybe}}
c <- n
{{else}}
select {
{{/* Blocking or non-blocking, before the receive (same reason as in recv). */}}
{{if .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. */}}
{{if .MaybeDefault}}
default:
panic("nonblock")
{{end}}
{{/* Dummy send, receive to keep compiler from optimizing select. */}}
{{if .Maybe}}
case dummy <- 1:
panic("dummy send")
{{end}}
{{if .Maybe}}
case <-dummy:
panic("dummy receive")
{{end}}
{{/* Nil channel send, receive to keep compiler from optimizing select. */}}
{{if .Maybe}}
case nilch <- 1:
panic("nilch send")
{{end}}
{{if .Maybe}}
case <-nilch:
panic("nilch recv")
{{end}}
}
{{end}}
x = <-c
if x != n {
die(x)
}
n++
`)
var sendOrder = parse("sendOrder", `
{{/* 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
{{if .Maybe}}
fc(c, 1) <- fn(n, 2)
{{else}}
select {
{{/* Blocking or non-blocking, before the receive (same reason as in recv). */}}
{{if .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. */}}
{{if .MaybeDefault}}
default:
panic("nonblock")
{{end}}
{{/* Dummy send, receive to keep compiler from optimizing select. */}}
{{if .Maybe}}
case fc(dummy, 3) <- fn(1, 4):
panic("dummy send")
{{end}}
{{if .Maybe}}
case <-fc(dummy, 5):
panic("dummy receive")
{{end}}
{{/* Nil channel send, receive to keep compiler from optimizing select. */}}
{{if .Maybe}}
case fc(nilch, 6) <- fn(1, 7):
panic("nilch send")
{{end}}
{{if .Maybe}}
case <-fc(nilch, 8):
panic("nilch recv")
{{end}}
}
{{end}}
x = <-c
if x != n {
die(x)
}
n++
`)
var nonblock = parse("nonblock", `
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 {
{{if .MaybeDefault}}
default:
{{end}}
{{if .Maybe}}
case dummy <- 1:
panic("dummy <- 1")
{{end}}
{{if .Maybe}}
case nilch <- 1:
panic("nilch <- 1")
{{end}}
{{if .Maybe}}
case <-dummy:
panic("<-dummy")
{{end}}
{{if .Maybe}}
case x = <-dummy:
panic("<-dummy x")
{{end}}
{{if .Maybe}}
case **(**int)(nil) = <-dummy:
panic("<-dummy (and didn't crash saving result!)")
{{end}}
{{if .Maybe}}
case <-nilch:
panic("<-nilch")
{{end}}
{{if .Maybe}}
case x = <-nilch:
panic("<-nilch x")
{{end}}
{{if .Maybe}}
case **(**int)(nil) = <-nilch:
panic("<-nilch (and didn't crash saving result!)")
{{end}}
{{if .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
}