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