blob: ca2f07d329793d8022e8aaaed1b7ca8f6700a56c [file] [log] [blame]
package main
import (
"bufio"
"context"
"io"
"os"
"os/exec"
"path/filepath"
"regexp"
"strings"
"testing"
"time"
"fuchsia.googlesource.com/testing/qemu"
)
// The default nodename given to an target with the default QEMU MAC address.
const defaultNodename = "swarm-donut-petri-acre"
type logMatch struct {
pattern string
shouldMatch bool
}
func zbiPath(t *testing.T) string {
ex, err := os.Executable()
if err != nil {
t.Fatal(err)
return ""
}
exPath := filepath.Dir(ex)
return filepath.Join(exPath, "../fuchsia.zbi")
}
func toolPath(t *testing.T, name string) string {
ex, err := os.Executable()
if err != nil {
t.Fatal(err)
return ""
}
exPath := filepath.Dir(ex)
return filepath.Join(exPath, "test_data", "netsvc_tools", name)
}
func matchPattern(pattern string, reader *bufio.Reader) bool {
for {
line, err := reader.ReadString('\n')
if err != nil && err == io.EOF {
break
}
if strings.Contains(line, pattern) {
return true
}
}
return false
}
func cmdWithOutput(t *testing.T, name string, arg ...string) []byte {
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
cmd := exec.CommandContext(ctx, name, arg...)
out, err := cmd.Output()
if err != nil {
t.Errorf("%s failed %s, err=%s", name, out, err)
return nil
}
return out
}
func cmdSearchLog(t *testing.T, logPatterns []logMatch,
name string, arg ...string) {
found := false
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
cmd := exec.CommandContext(ctx, name, arg...)
cmderr, err := cmd.StderrPipe()
if err != nil {
t.Errorf("Failed to stdout %s", err)
}
readerErr := bufio.NewReader(cmderr)
if err := cmd.Start(); err != nil {
t.Errorf("Failed to start %s", err)
}
for _, logPattern := range logPatterns {
match := matchPattern(logPattern.pattern, readerErr)
if match != logPattern.shouldMatch {
found = false
t.Errorf("Log pattern \"%s\" mismatch. Expected - %t, actual - %t",
logPattern.pattern, logPattern.shouldMatch, match)
break
}
found = true
}
if err := cmd.Wait(); err != nil {
t.Logf("Failed to wait on task %s", err)
}
if ctx.Err() == context.DeadlineExceeded {
t.Errorf("%s timed out err=%s", name, ctx.Err())
} else if !found {
t.Errorf("%s failed to match logs", name)
} else {
t.Logf("%s worked as expected", name)
}
}
func attemptPaveNoBind(t *testing.T, i *qemu.Instance, shouldWork bool) {
// Get the node ipv6 address
out := cmdWithOutput(t, toolPath(t, "netls"))
// Extract the ipv6 from the netls output
regexString := defaultNodename + ` \((?P<ipv6>.*)\)`
match := regexp.MustCompile(regexString).FindStringSubmatch(string(out))
if len(match) != 2 {
t.Errorf("Node %s not found in netls output - %s", defaultNodename, out)
return
}
var logPattern []logMatch
if shouldWork {
paveWorksPattern := []logMatch{{"Sending request to ", true},
{"Received request from ", true},
{"Proceeding with nodename ", true},
{"Transfer starts", true}}
logPattern = paveWorksPattern
} else {
paveFailsPattern := []logMatch{{"Sending request to ", true},
{"Received request from ", false},
{"Proceeding with nodename ", false},
{"Transfer starts", false}}
logPattern = paveFailsPattern
}
cmdSearchLog(
t, logPattern,
toolPath(t, "bootserver"), "--fvm", "\"dummy.blk\"",
"--no-bind", "-a", match[1], "-1", "--fail-fast")
}
func setupQemu(t *testing.T, appendCmdline string, modeString string) (*qemu.Instance, func()) {
distro, err := qemu.Unpack()
if err != nil {
t.Fatal(err)
}
arch, err := distro.TargetCPU()
if err != nil {
t.Fatal(err)
}
i := distro.Create(qemu.Params{
Arch: arch,
ZBI: zbiPath(t),
AppendCmdline: appendCmdline,
Networking: true,
})
i.Start()
if err != nil {
t.Fatal(err)
}
// Make sure netsvc in expected mode.
i.WaitForLogMessage("netsvc: running in " + modeString + " mode")
// Make sure netsvc is booted.
i.WaitForLogMessage("netsvc: start")
return i, func() {
i.Kill()
distro.Delete()
}
}
func TestPaveNoBind(t *testing.T) {
i, cleanup := setupQemu(t, "netsvc.all-features=true, netsvc.netboot=true", "full")
defer cleanup()
// Test that advertise request is serviced and paving starts as netsvc.netboot=true
attemptPaveNoBind(t, i, true)
}
func TestPaveNoBindFailure(t *testing.T) {
i, cleanup := setupQemu(t, "netsvc.all-features=true, netsvc.netboot=false", "full")
defer cleanup()
// Test that advertise request is NOT serviced and paving does NOT start
// as netsvc.netboot=false
attemptPaveNoBind(t, i, false)
}