blob: d9d897a49a1abcd8ebc6dca8edc84588fde4b56b [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_test
import (
"bufio"
"encoding/json"
"fmt"
"os"
"path/filepath"
"regexp"
"strconv"
"strings"
"testing"
"time"
"v.io/x/ref/test/v23test"
)
func TestV23DebugGlob(t *testing.T) {
v23test.SkipUnlessRunningIntegrationTests(t)
sh := v23test.NewShell(t, nil)
defer sh.Cleanup()
sh.StartRootMountTable()
binary := v23test.BuildGoPkg(sh, "v.io/x/ref/services/debug/debug")
stdout := sh.Cmd(binary, "glob", "__debug/*").Stdout()
var want string
for _, entry := range []string{"logs", "pprof", "stats", "vtrace"} {
want += "__debug/" + entry + "\n"
}
if got := stdout; got != want {
t.Fatalf("unexpected output, want %s, got %s", want, got)
}
}
func TestV23DebugGlobLogs(t *testing.T) {
v23test.SkipUnlessRunningIntegrationTests(t)
sh := v23test.NewShell(t, nil)
defer sh.Cleanup()
sh.StartRootMountTable()
// Create a temp dir before we list the logs.
dirName := sh.MakeTempDir()
binary := v23test.BuildGoPkg(sh, "v.io/x/ref/services/debug/debug")
output := sh.Cmd(binary, "--timeout=1m", "glob", "__debug/logs/*").CombinedOutput()
// The output should contain the dir name.
want := "/logs/" + filepath.Base(dirName)
if !strings.Contains(output, want) {
fi, err := os.Stat(dirName)
t.Logf("Stat(%q): (%v, %v)", dirName, fi, err)
t.Logf("TempDir: %q", os.TempDir())
t.Fatalf("output should contain %s but did not\n%s", want, output)
}
}
func TestV23ReadHostname(t *testing.T) {
v23test.SkipUnlessRunningIntegrationTests(t)
sh := v23test.NewShell(t, nil)
defer sh.Cleanup()
sh.StartRootMountTable()
path := "__debug/stats/system/hostname"
binary := v23test.BuildGoPkg(sh, "v.io/x/ref/services/debug/debug")
stdout := sh.Cmd(binary, "stats", "read", path).Stdout()
hostname, err := os.Hostname()
if err != nil {
t.Fatalf("Hostname() failed: %v", err)
}
if got, want := stdout, path+": "+hostname+"\n"; got != want {
t.Fatalf("unexpected output, want %q, got %q", want, got)
}
}
func createTestLogFile(t *testing.T, sh *v23test.Shell, content string) *os.File {
file := sh.MakeTempFile()
_, err := file.Write([]byte(content))
if err != nil {
t.Fatalf("Write failed: %v", err)
}
return file
}
func TestV23LogSize(t *testing.T) {
v23test.SkipUnlessRunningIntegrationTests(t)
sh := v23test.NewShell(t, nil)
defer sh.Cleanup()
sh.StartRootMountTable()
binary := v23test.BuildGoPkg(sh, "v.io/x/ref/services/debug/debug")
testLogData := "This is a test log file"
file := createTestLogFile(t, sh, testLogData)
// Check to ensure the file size is accurate
str := strings.TrimSpace(sh.Cmd(binary, "logs", "size", "__debug/logs/"+filepath.Base(file.Name())).Stdout())
got, err := strconv.Atoi(str)
if err != nil {
t.Fatalf("Atoi(\"%q\") failed", str)
}
want := len(testLogData)
if got != want {
t.Fatalf("unexpected output, want %d, got %d", got, want)
}
}
func TestV23StatsRead(t *testing.T) {
v23test.SkipUnlessRunningIntegrationTests(t)
sh := v23test.NewShell(t, nil)
defer sh.Cleanup()
sh.StartRootMountTable()
binary := v23test.BuildGoPkg(sh, "v.io/x/ref/services/debug/debug")
testLogData := "This is a test log file\n"
file := createTestLogFile(t, sh, testLogData)
logName := filepath.Base(file.Name())
runCount := 12
for c := 0; c < runCount; c++ {
sh.Cmd(binary, "logs", "read", "__debug/logs/"+logName).Run()
}
readlogStatsEndpoint := "__debug/stats/rpc/server/routing-id/*/methods/ReadLog/latency-ms"
got := sh.Cmd(binary, "stats", "read", readlogStatsEndpoint).Stdout()
want := fmt.Sprintf("Count: %d", runCount)
if !strings.Contains(got, want) {
t.Fatalf("expected output %q to contain %q, but did not\n", got, want)
}
// Test "-json" format.
jsonOutput := sh.Cmd(binary, "stats", "read", "-json", readlogStatsEndpoint).Stdout()
var stats []struct {
Name string
Value struct {
Count int
}
}
if err := json.Unmarshal([]byte(jsonOutput), &stats); err != nil {
t.Fatalf("invalid json output:\n%s", jsonOutput)
}
if want, got := 1, len(stats); want != got {
t.Fatalf("unexpected stats length, want %d, got %d", want, got)
}
if !strings.HasPrefix(stats[0].Name, "__debug/stats/rpc/server/routing-id") {
t.Fatalf("unexpected Name field, want %q, got %q", want, got)
}
if want, got := runCount, stats[0].Value.Count; want != got {
t.Fatalf("unexpected Value.Count field, want %d, got %d", want, got)
}
}
func TestV23StatsWatch(t *testing.T) {
v23test.SkipUnlessRunningIntegrationTests(t)
sh := v23test.NewShell(t, nil)
defer sh.Cleanup()
sh.StartRootMountTable()
binary := v23test.BuildGoPkg(sh, "v.io/x/ref/services/debug/debug")
testLogData := "This is a test log file\n"
file := createTestLogFile(t, sh, testLogData)
logName := filepath.Base(file.Name())
sh.Cmd(binary, "logs", "read", "__debug/logs/"+logName).Run()
cmd := sh.Cmd(binary, "stats", "watch", "-raw", "__debug/stats/rpc/server/routing-id/*/methods/ReadLog/latency-ms")
stdoutPipe := cmd.StdoutPipe()
cmd.Start()
lineChan := make(chan string)
// Go off and read the invocation's stdout.
go func() {
line, err := bufio.NewReader(stdoutPipe).ReadString('\n')
if err != nil {
t.Fatalf("Could not read line from invocation")
}
lineChan <- line
}()
// Wait up to 10 seconds for some stats output. Either some output
// occurs or the timeout expires without any output.
select {
case <-time.After(10 * time.Second):
t.Errorf("Timed out waiting for output")
case got := <-lineChan:
// Expect one ReadLog call to have occurred.
want := "}}{Count: 1"
if !strings.Contains(got, want) {
t.Errorf("wanted but could not find %q in output\n%s", want, got)
}
}
}
func performTracedRead(sh *v23test.Shell, debugBinary, path string) string {
return sh.Cmd(debugBinary, "--v23.vtrace.sample-rate=1", "logs", "read", path).Stdout()
}
func TestV23VTrace(t *testing.T) {
v23test.SkipUnlessRunningIntegrationTests(t)
sh := v23test.NewShell(t, nil)
defer sh.Cleanup()
sh.StartRootMountTable()
binary := v23test.BuildGoPkg(sh, "v.io/x/ref/services/debug/debug")
logContent := "Hello, world!\n"
logPath := "__debug/logs/" + filepath.Base(createTestLogFile(t, sh, logContent).Name())
// Create a log file with tracing, read it and check that the resulting trace exists.
got := performTracedRead(sh, binary, logPath)
if logContent != got {
t.Fatalf("unexpected output: want %s, got %s", logContent, got)
}
// Grab the ID of the first and only trace.
traceContent, want := sh.Cmd(binary, "vtrace", "__debug/vtrace").Stdout(), 1
if count := strings.Count(traceContent, "Trace -"); count != want {
t.Fatalf("unexpected trace count, want %d, got %d: %s", want, count, traceContent)
}
fields := strings.Split(traceContent, " ")
if len(fields) < 3 {
t.Fatalf("expected at least 3 space-delimited fields, got %d: %v", len(fields), traceContent)
}
traceId := fields[2]
// Do a sanity check on the trace ID: it should be a 32-character hex ID prefixed with 0x
if match, _ := regexp.MatchString("0x[0-9a-f]{32}", traceId); !match {
t.Fatalf("wanted a 32-character hex ID prefixed with 0x, got %s", traceId)
}
// Do another traced read, this will generate a new trace entry.
performTracedRead(sh, binary, logPath)
// Read vtrace, we should have 2 traces now.
output, want := sh.Cmd(binary, "vtrace", "__debug/vtrace").Stdout(), 2
if count := strings.Count(output, "Trace -"); count != want {
t.Fatalf("unexpected trace count, want %d, got %d\n%s", want, count, output)
}
// Now ask for a particular trace. The output should contain exactly
// one trace whose ID is equal to the one we asked for.
got, want = sh.Cmd(binary, "vtrace", "__debug/vtrace", traceId).Stdout(), 1
if count := strings.Count(got, "Trace -"); count != want {
t.Fatalf("unexpected trace count, want %d, got %d\n%s", want, count, got)
}
fields = strings.Split(got, " ")
if len(fields) < 3 {
t.Fatalf("expected at least 3 space-delimited fields, got %d: %v", len(fields), got)
}
got = fields[2]
if traceId != got {
t.Fatalf("unexpected traceId, want %s, got %s", traceId, got)
}
}
func TestV23Pprof(t *testing.T) {
t.Skip("https://github.com/vanadium/build/issues/49")
v23test.SkipUnlessRunningIntegrationTests(t)
sh := v23test.NewShell(t, nil)
defer sh.Cleanup()
sh.StartRootMountTable()
binary := v23test.BuildGoPkg(sh, "v.io/x/ref/services/debug/debug")
stdout := sh.Cmd(binary, "pprof", "run", "__debug/pprof", "heap", "--text").Stdout()
// Assert that a profile indicating the heap size was written out.
want, got := "(.*) of (.*) total", stdout
var groups []string
if groups = regexp.MustCompile(want).FindStringSubmatch(got); len(groups) < 3 {
t.Fatalf("could not find regexp %q in output\n%s", want, got)
}
t.Logf("got a heap profile showing a heap size of %s", groups[2])
}
func TestMain(m *testing.M) {
v23test.TestMain(m)
}