...
Run Format

Source file test/chan/select5.go

Documentation: test/chan

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

View as plain text