summaryrefslogtreecommitdiffstats
path: root/test/chan/select5.go
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-16 19:23:18 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-16 19:23:18 +0000
commit43a123c1ae6613b3efeed291fa552ecd909d3acf (patch)
treefd92518b7024bc74031f78a1cf9e454b65e73665 /test/chan/select5.go
parentInitial commit. (diff)
downloadgolang-1.20-upstream.tar.xz
golang-1.20-upstream.zip
Adding upstream version 1.20.14.upstream/1.20.14upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'test/chan/select5.go')
-rw-r--r--test/chan/select5.go481
1 files changed, 481 insertions, 0 deletions
diff --git a/test/chan/select5.go b/test/chan/select5.go
new file mode 100644
index 0000000..8b98c3a
--- /dev/null
+++ b/test/chan/select5.go
@@ -0,0 +1,481 @@
+// 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
+}