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