blob: e426d9e138154139cff55399c160aeb6c1e58301 [file] [log] [blame]
// Copyright 2015 The Vanadium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package main
import (
"bytes"
"os/exec"
"path/filepath"
"reflect"
"runtime"
"sort"
"strings"
"sync"
"testing"
"v.io/v23"
"v.io/v23/context"
"v.io/v23/rpc"
"v.io/v23/security"
"v.io/x/lib/cmdline"
"v.io/x/ref/services/ben"
"v.io/x/ref/test"
)
func TestGo(t *testing.T) {
// go test -bench . testdata_test.go
goTestOutput := runGoTest(t)
ctx, shutdown := test.V23Init()
defer shutdown()
// Start an archival server
var archiver archiver
_, server, err := v23.WithNewServer(ctx, "", &archiver, security.AllowEveryone())
if err != nil {
t.Fatal(err)
}
// Pipe goTestOutput through the "benup" command
env := cmdline.EnvFromOS()
env.Stdin = bytes.NewBufferString(goTestOutput)
out := bytes.NewBuffer(nil)
env.Stdout = out
flagArchiver = server.Status().Endpoints[0].Name()
if err := runUpload(ctx, env, nil); err != nil {
t.Fatal(err)
}
gotS, gotC, gotR := archiver.lastCall()
// Some sanity checks on the Scenario
if got, want := gotS.Cpu.Architecture, runtime.GOARCH; got != want {
t.Errorf("Got %q, want %q", got, want)
}
if got, want := gotS.Os.Name, runtime.GOOS; got != want {
t.Errorf("Got %q, want %q", got, want)
}
// And the state of the code
if len(gotC) == 0 {
t.Errorf("SourceCode not detected?")
}
// All the runs
// Since goTestOutput was generated by running "go test" directly on a
// file, the "package name" is set to "command-line-arguments" (by the
// "go" tool).
const (
bmGood = "command-line-arguments.BenchmarkGood"
bmThroughput = "command-line-arguments.BenchmarkThroughput"
bmAllocs = "command-line-arguments.BenchmarkAllocs"
bmAll = "command-line-arguments.BenchmarkAllMetrics"
)
var gotNames []string
for _, run := range gotR {
gotNames = append(gotNames, run.Name)
// Some fields should be set for all benchmarks
if run.Iterations == 0 {
t.Errorf("Got 0 iterations for %q", run.Name)
}
if run.NanoSecsPerOp == 0 {
t.Errorf("Got 0 NanoSecsPerOp for %q", run.Name)
}
if run.Parallelism == 0 {
t.Errorf("Got 0 parallelism for %q", run.Name)
}
switch run.Name {
case bmGood:
// No other metrics set
case bmThroughput:
if run.MegaBytesPerSec == 0 {
t.Errorf("Got 0 MegaBytesPerSec for %q", run.Name)
}
case bmAllocs:
if run.AllocsPerOp == 0 {
t.Errorf("Got 0 AllocsPerOp for %q", run.Name)
}
if run.AllocedBytesPerOp == 0 {
t.Errorf("Got 0 AllocedBytesPerOp for %q", run.Name)
}
case bmAll:
if run.MegaBytesPerSec == 0 {
t.Errorf("Got 0 MegaBytesPerSec for %q", run.Name)
}
if run.AllocsPerOp == 0 {
t.Errorf("Got 0 AllocsPerOp for %q", run.Name)
}
if run.AllocedBytesPerOp == 0 {
t.Errorf("Got 0 AllocedBytesPerOp for %q", run.Name)
}
}
}
sort.Strings(gotNames)
if got, want := gotNames, []string{bmAll, bmAllocs, bmGood, bmThroughput}; !reflect.DeepEqual(got, want) {
t.Errorf("Got %v, want %v", got, want)
}
// And benup output should said that it uploaded 4 benchmarks.
if want := "Uploaded 4 benchmark(s)"; !strings.Contains(out.String(), want) {
t.Errorf("Did not find %q in benup output:\n%v", want, out.String())
}
if want := "someprotocol://someurl"; !strings.Contains(out.String(), want) {
t.Errorf("Did not find %q in benup output:\n%v", want, out.String())
}
}
func TestNoBenchmarks(t *testing.T) {
// When the input does not contain any results, no RPCs will be send.
tests := []string{
"",
"There is nothing here",
`
BENDROIDOS_VERSION="Foo bar baz"
Gotcha!`,
}
ctx, shutdown := test.V23Init()
defer shutdown()
env := cmdline.EnvFromOS()
for idx, test := range tests {
env.Stdin = bytes.NewBufferString(test)
out := bytes.NewBuffer(nil)
env.Stdout = out
if err := runUpload(ctx, env, nil); err != nil {
t.Errorf("%v -- with input #%d %q", err, idx, test)
}
if want := "No benchmarks found to upload"; !strings.Contains(out.String(), want) {
t.Errorf("Did not find %q in output for input #%d (output = %q)", want, idx, out.String())
}
}
}
func TestDetectScenario(t *testing.T) {
scn := detectScenario()
testAllFieldsSet(t, scn.Cpu)
testAllFieldsSet(t, scn.Os)
}
func runGoTest(t *testing.T) string {
// Generates the output of "go test" on testdata_test.go
_, file, _, ok := runtime.Caller(0)
if !ok {
t.Fatalf("failed to extract filename of current source file")
}
gobin, err := exec.LookPath("go")
if err != nil {
t.Fatalf("go compiler not found in PATH: %v", err)
}
versionBytes, err := exec.Command(gobin, "version").CombinedOutput()
if err != nil {
t.Fatalf("%v version failed: %v (%s)", gobin, err, versionBytes)
}
t.Logf("Go compiler: %s", versionBytes)
cmd := exec.Command(gobin, "test", "-v", "-bench", ".", filepath.Join(filepath.Dir(file), "testdata_test.go"))
output, err := cmd.CombinedOutput()
if err != nil {
t.Fatalf("%v failed: %v (%s)", cmd.Args, err, output)
}
return string(output)
}
type archiver struct {
mu sync.Mutex
scn ben.Scenario
code string
runs []ben.Run
}
func (a *archiver) Archive(ctx *context.T, call rpc.ServerCall, scenario ben.Scenario, code string, runs []ben.Run) (string, error) {
a.mu.Lock()
a.scn = scenario
a.code = code
a.runs = runs
a.mu.Unlock()
return "someprotocol://someurl", nil
}
func (a *archiver) lastCall() (ben.Scenario, string, []ben.Run) {
a.mu.Lock()
scn, code, runs := a.scn, a.code, a.runs
a.scn = ben.Scenario{}
a.code = ""
a.runs = nil
a.mu.Unlock()
return scn, code, runs
}
func testAllFieldsSet(t *testing.T, value interface{}) {
rv := reflect.ValueOf(value)
for i := 0; i < rv.NumField(); i++ {
f := rv.Field(i)
v := f.Interface()
z := reflect.Zero(f.Type()).Interface()
if reflect.DeepEqual(v, z) {
t.Errorf("%s not set", rv.Type().Field(i).Name)
}
}
}