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