Using testing.Benchmark does not produce any output - go

I'm using testing.Benchmark to manually run a couple benchmarks but the result object is always empty.
Am I missing something here?
Here's an example:
package main
import "testing"
func main() {
result := testing.Benchmark(func(parentB *testing.B) {
parentB.Run("example", func(b *testing.B) {
for n := 0; n < b.N; n++ {
println("ok")
}
})
})
println(result.String())
}
This will print ok a couple times and then 0 0 ns/op but the benchmark clearly did run something.

I think you are doing everything right. Doc of testing.Benchmark() says:
Benchmark benchmarks a single function. Useful for creating custom benchmarks that do not use the "go test" command.
If f calls Run, the result will be an estimate of running all its subbenchmarks that don't call Run in sequence in a single benchmark.
Looking into the implementation (Go 1.7.4):
func Benchmark(f func(b *B)) BenchmarkResult {
b := &B{
common: common{
signal: make(chan bool),
w: discard{},
},
benchFunc: f,
benchTime: *benchTime,
}
if !b.run1() {
return BenchmarkResult{}
}
return b.run()
}
This line:
if !b.run1() {
return BenchmarkResult{}
}
b.run1() is supposed to run your passed function once, and detect if it has sub-benchmarks. Yours has. It returns a bool whether more runs are needed. Inside run1():
if b.hasSub || b.finished {
// ...
return true
}
It properly tells it has sub-benchmark, and Benchmark() –with noble simplicity– just returns an empty BenchmarkResult:
if !b.run1() {
return BenchmarkResult{}
}
I do believe that either this is a bug (or rather "incomplete" feature), or doc is incorrect. I suggest to file an issue here: https://github.com/golang/go/issues

Eddited the answer to clarify:
My guess is that you are using go run to run the test. That will not produce any result. In order to run the code exatly as it is written you need to use
go test -bench=. and I think it should work.
The file must be named test_xxx.go where xxx is whatever you want.
If you restucture you code a litle bit it can be run as a single function benchmark:
package main
import "testing"
func main() {
myTest()
}
func myTest() {
fn := func(b *testing.B) {
for n := 0; n < b.N; n++ {
println("ok")
}
}
result := testing.Benchmark(fn)
println(result.String())
}

Related

Golang: Shorthand Variable Declaration inside For loop [duplicate]

I am a new in programming. I have two examples code in Go and its about loop using range. This is the first example:
Program A
type Test struct {
Text string
}
func main() {
tests := []Test{
Test{"Test1"},
Test{"Test2"},
}
var a Test
for _, test := range tests {
a = test
fmt.Println(a)
}
}
This is the second example:
Program B
type Test struct {
Text string
}
func main() {
tests := []Test{
Test{"Test1"},
Test{"Test2"},
}
for _, test := range tests {
a := test
fmt.Println(a)
}
}
In the first example 'a' is declared outside the loop, but in the second example 'a' is declared inside the loop. Like in the other programming language, what is the difference between two example program? Is there any optimization difference? Thank you.
The variables have different scopes. It is usually best practice to use the smallest scope possible as in the second example.
There should be no optimization difference.

go testing outputs wrong case names in json format under parallel mode

go version: 1.18.1
suppose i wrote this test file parallel_test.go
package parallel_json_output
import (
"fmt"
"testing"
"time"
)
func TestP(t *testing.T) {
t.Run("a", func(t *testing.T) {
t.Parallel()
for i := 0; i < 5; i++ {
time.Sleep(time.Second)
fmt.Println("a", i)
}
})
t.Run("b", func(t *testing.T) {
t.Parallel()
for i := 0; i < 5; i++ {
time.Sleep(time.Second)
fmt.Println("b", i)
}
})
}
after running go test parallel_test.go -v -json, i got
{"Time":"2022-06-11T02:48:10.3262833+08:00","Action":"run","Package":"command-line-arguments","Test":"TestP"}
{"Time":"2022-06-11T02:48:10.3672856+08:00","Action":"output","Package":"command-line-arguments","Test":"TestP","Output":"=== RUN TestP\n"}
{"Time":"2022-06-11T02:48:10.3682857+08:00","Action":"run","Package":"command-line-arguments","Test":"TestP/a"}
{"Time":"2022-06-11T02:48:10.3682857+08:00","Action":"output","Package":"command-line-arguments","Test":"TestP/a","Output":"=== RUN TestP/a\n"}
{"Time":"2022-06-11T02:48:10.3692857+08:00","Action":"output","Package":"command-line-arguments","Test":"TestP/a","Output":"=== PAUSE TestP/a\n"}
{"Time":"2022-06-11T02:48:10.3702858+08:00","Action":"pause","Package":"command-line-arguments","Test":"TestP/a"}
{"Time":"2022-06-11T02:48:10.3702858+08:00","Action":"run","Package":"command-line-arguments","Test":"TestP/b"}
{"Time":"2022-06-11T02:48:10.3712858+08:00","Action":"output","Package":"command-line-arguments","Test":"TestP/b","Output":"=== RUN TestP/b\n"}
{"Time":"2022-06-11T02:48:10.3712858+08:00","Action":"output","Package":"command-line-arguments","Test":"TestP/b","Output":"=== PAUSE TestP/b\n"}
{"Time":"2022-06-11T02:48:10.3722859+08:00","Action":"pause","Package":"command-line-arguments","Test":"TestP/b"}
{"Time":"2022-06-11T02:48:10.373286+08:00","Action":"cont","Package":"command-line-arguments","Test":"TestP/a"}
{"Time":"2022-06-11T02:48:10.373286+08:00","Action":"output","Package":"command-line-arguments","Test":"TestP/a","Output":"=== CONT TestP/a\n"}
{"Time":"2022-06-11T02:48:10.374286+08:00","Action":"cont","Package":"command-line-arguments","Test":"TestP/b"}
{"Time":"2022-06-11T02:48:10.374286+08:00","Action":"output","Package":"command-line-arguments","Test":"TestP/b","Output":"=== CONT TestP/b\n"}
{"Time":"2022-06-11T02:48:11.3352891+08:00","Action":"output","Package":"command-line-arguments","Test":"TestP/b","Output":"b 0\n"}
{"Time":"2022-06-11T02:48:11.3352891+08:00","Action":"output","Package":"command-line-arguments","Test":"TestP/b","Output":"a 0\n"}
...
look at this line {"Time":"2022-06-11T02:48:11.3352891+08:00","Action":"output","Package":"command-line-arguments","Test":"TestP/b","Output":"a 0\n"}. this output should be printed by case TestP/a instead of b, but the output messed up the case name in parallel tests.
this problem made reporting tool generate wrong HTML report, IDEs (like GoLand) are effected too and cannot sort parallel output correctly.
i found an issue of it in Github here, but this issue seems had been fixed already in go 1.14.6, however, it still appears in go 1.18.
i wonder what happend and how to deal with it, many thanks.
It makes sense that generic fmt package has little knowledge about currently executed tests in concurrent environment.
Testing package has its own Log method that correctly renders current test:
t.Log("a", i)

Having some issues recursively printing a complete list of a tree-like structure

I'm having trouble creating a structure for a CLI tool that will have n commands. Each command can have n subcommands, and each subcommand can have n more subcommands.
My issue is that, in Go, I'm struggling to figure out a way to create a recursive function to output the name of each command, along with each n subcommands + every n subcommands for that subcommand, in a complete list.
For example, I'm looking to get the following output:
1. command1
2. command2
3. command3
4. command3 subcommand1
5. command3 subcommand1 subcommand1
6. command3 subcommand2
Here is my code:
package main
import (
"fmt"
)
type command struct {
name string
parent *command
subcommands []*command
}
func getLastCommand(c command) command {
for _, s := range c.subcommands {
if len(s.subcommands) == 0 {
return *s
}
return getLastCommand(*s)
}
return c
}
func main() {
cmdBase1 := command{
name: "base1",
}
cmdBase2 := command{
name: "base2",
}
var (
cmdBase3,
cmdBase3Sub1,
cmdBase3Sub1Sub1,
cmdBase3Sub2 command
)
cmdBase3 = command{
name: "base3",
subcommands: []*command{&cmdBase3Sub1, &cmdBase3Sub2},
}
cmdBase3Sub1 = command{
name: "base3:sub1",
parent: &cmdBase3,
subcommands: []*command{&cmdBase3Sub1Sub1},
}
cmdBase3Sub1Sub1 = command{
name: "base3:sub1:sub1",
parent: &cmdBase3Sub1,
}
cmdBase3Sub2 = command{
name: "base3:sub2",
parent: &cmdBase3,
}
// root commands
commands := []command{
cmdBase1,
cmdBase2,
cmdBase3,
}
for _, c := range commands {
last := getLastCommand(c)
fmt.Println(last.name)
}
}
https://play.golang.org/p/HZPRlSghfAY
Here is the current output:
base1
base2
base3:sub1:sub1
My desired output is with the above code is:
base1
base2
base3
base3:sub1
base3:sub1:sub1
base3:sub2
What do I need to change in my code so I can get my above desired output? Is there an algorithm or data structure that I could follow to solve this? I've tried depth-first and binary searching, but I can't seem to mold it to my structure.
A simple and elegant solution would be to "arm" command with a print() method. This could print its name, and range over its subcommands, calling their print() (which do the same):
func (c *command) print() {
fmt.Println(c.name)
for _, sc := range c.subcommands {
sc.print()
}
}
Then printing the commands in main() is just calling their print() method (getLastCommand() is not even needed / used):
for _, c := range commands {
c.print()
}
This will result in your desired output (try it on the Go Playground):
base1
base2
base3
base3:sub1
base3:sub1:sub1
base3:sub2
Note that of course print() doesn't have to be a method, it may be a regular function too, in which case it could look like this:
func print(c *command) {
fmt.Println(c.name)
for _, sc := range c.subcommands {
print(sc)
}
}
And the loop in main():
for _, c := range commands {
print(&c)
}
Result is the same, try this one on the Go Playground.
I would also suggest to be consistent. If you decide to use pointer to command, do it everywhere (e.g. the commands slice in your main() stores non-pointers, that's why its element's address had to be taken to pass to print()).

Go Declare Variable outside vs Declare Variable Inside Loop

I am a new in programming. I have two examples code in Go and its about loop using range. This is the first example:
Program A
type Test struct {
Text string
}
func main() {
tests := []Test{
Test{"Test1"},
Test{"Test2"},
}
var a Test
for _, test := range tests {
a = test
fmt.Println(a)
}
}
This is the second example:
Program B
type Test struct {
Text string
}
func main() {
tests := []Test{
Test{"Test1"},
Test{"Test2"},
}
for _, test := range tests {
a := test
fmt.Println(a)
}
}
In the first example 'a' is declared outside the loop, but in the second example 'a' is declared inside the loop. Like in the other programming language, what is the difference between two example program? Is there any optimization difference? Thank you.
The variables have different scopes. It is usually best practice to use the smallest scope possible as in the second example.
There should be no optimization difference.

Go MinGW compiler complains about if-else statement

Consider the following (useless) Go function:
func domagic(n int) int {
if n > 10 {
return n;
} else {
return 0;
}
}
This gives me the following compiler error:
main.go:15: function ends without a return statement
However, if i return a value outside the if-else block (before the end of the function), it compiles without errors.
Is this behavior by design, or is it something simply not yet implemented in the Go MinGW compiler?
Simple googling for the exact compiler error message yields this bugtracker issue. So I'd not say it's "by design" as it looks more like "it'd just happened to be implemented this way". See also this thread.
It's by design. Write:
package main
import "fmt"
func domagic(n int) int {
if n > 10 {
return n
}
return 0
}
func main() {
fmt.Println(domagic(7), domagic(42))
}
Output:
0 42

Resources