diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-16 19:23:18 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-16 19:23:18 +0000 |
commit | 43a123c1ae6613b3efeed291fa552ecd909d3acf (patch) | |
tree | fd92518b7024bc74031f78a1cf9e454b65e73665 /test/chan/select5.go | |
parent | Initial commit. (diff) | |
download | golang-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.go | 481 |
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 +} |