blob: e25e8186d6bd25ff0c54181673af4fb56aa19da4 [file] [log] [blame]
// Copyright 2018 The Fuchsia 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 ifconfig
import (
"os/exec"
"strings"
"testing"
)
// The Go standard library sets stdin on `exec.Command`s to /dev/null if it's not specified.
// /dev/null doesn't exist in most component sandboxes, so we make our own null byte reader here.
// See http://fxb.dev/39907 for more context.
type nullReader struct{}
func (*nullReader) Read(p []byte) (int, error) {
for i := range p {
p[i] = 0
}
return len(p), nil
}
func run(t *testing.T, argv0 string, argv ...string) string {
t.Helper()
cmd := exec.Command(argv0, argv...)
cmd.Stdin = &nullReader{}
out, err := cmd.CombinedOutput()
if err != nil {
t.Fatalf("%s %s: %s", argv0, argv, err)
}
return string(out)
}
func runExpectingError(t *testing.T, cmd0 string, cmd ...string) {
_, err := exec.Command(cmd0, cmd...).CombinedOutput()
if err == nil {
t.Errorf("expected `%s %s` to exit nonzero", cmd0, cmd)
}
}
func runQuietly(t *testing.T, cmd0 string, cmd ...string) {
t.Helper()
out := run(t, cmd0, cmd...)
if len(out) > 0 {
t.Errorf("Expected '%s %s' to have no output but output was '%s'", cmd0, cmd, out)
}
}
func TestOutput(t *testing.T) {
t.Run("ifconfig route add", func(t *testing.T) {
before := run(t, "/bin/ifconfig", "route", "show")
runExpectingError(t, "/bin/ifconfig", "route", "add", "1.2.3.4/14", "gateway", "9.8.7.6", "iface", "NON_EXISTENT_INTERFACE_FORCES_FAILURE")
after := run(t, "/bin/ifconfig", "route", "show")
if before != after {
t.Errorf("Expected ifconfig route add failure but the routes changed: '%s' vs '%s'", before, after)
}
})
t.Run("ifconfig route del", func(t *testing.T) {
// Add a route.
runQuietly(t, "/bin/ifconfig", "route", "add", "1.2.3.4/14", "gateway", "9.8.7.6", "iface", "lo")
out := run(t, "/bin/ifconfig", "route", "show")
expected := "1.0.0.0/14 via 9.8.7.6 lo"
if !strings.Contains(out, expected) {
t.Errorf("ifconfig route add failed, couldn't find '%s' in '%s'", expected, out)
}
// Try to delete it but the gateway mismatches so it should fail.
runExpectingError(t, "/bin/ifconfig", "route", "del", "1.2.3.4/14", "gateway", "9.9.9.9")
out = run(t, "/bin/ifconfig", "route", "show")
if !strings.Contains(out, expected) {
t.Errorf("ifconfig route del removed '%s' from '%s' but it should not have", expected, out)
}
// Try to delete it but the mask length mismatches so it should fail.
runExpectingError(t, "/bin/ifconfig", "route", "del", "1.2.3.4/15")
out = run(t, "/bin/ifconfig", "route", "show")
if !strings.Contains(out, expected) {
t.Errorf("ifconfig route del removed '%s' from '%s' but it should not have", expected, out)
}
// Now delete it.
runQuietly(t, "/bin/ifconfig", "route", "del", "1.2.3.4/14", "gateway", "9.8.7.6")
out = run(t, "/bin/ifconfig", "route", "show")
if strings.Contains(out, expected) {
t.Errorf("ifconfig route del failed, did not expect to find '%s' in '%s'", expected, out)
}
// Try to delete it again, it should fail.
runExpectingError(t, "/bin/ifconfig", "route", "del", "1.2.3.4/14")
out = run(t, "/bin/ifconfig", "route", "show")
if strings.Contains(out, expected) {
t.Errorf("ifconfig route del failed, did not expect to find '%s' in '%s'", expected, out)
}
})
t.Run("ifconfig route parse error formatting", func(t *testing.T) {
cmd := exec.Command("/bin/ifconfig", "route", "add")
cmd.Stdin = &nullReader{}
out, err := cmd.CombinedOutput()
if err == nil {
t.Errorf("ifconfig route add returned success without enough arguments. output: \n%s", out)
}
expected := "Not enough arguments"
if !strings.Contains(string(out), expected) {
t.Errorf("want `ifconfig route add` to print \"%s\", got \"%s\"", expected, out)
}
cmd = exec.Command("/bin/ifconfig", "route", "add", "1.2.3.4/14")
cmd.Stdin = &nullReader{}
out, err = cmd.CombinedOutput()
if err == nil {
t.Errorf("ifconfig route add returned success with neither gateway or iface specified. output: \n%s", out)
}
expected = "Error adding route to route table"
if !strings.Contains(string(out), expected) {
t.Errorf("want `ifconfig route add` to print \"%s\", got \"%s\"", expected, out)
}
})
t.Run("Interface name exact match: `ifconfig lo` should return 1 interface", func(t *testing.T) {
cmd := exec.Command("/bin/ifconfig", "lo")
cmd.Stdin = &nullReader{}
out, err := cmd.CombinedOutput()
if err != nil {
t.Errorf("want no error but got error:\n%s", out)
}
headlines := findLinesContainingHWaddr(string(out))
if len(headlines) != 1 {
t.Errorf("want exactly 1 interface from `ifconfig lo`, got: %d", len(headlines))
} else {
expected := "lo\tHWaddr Id:1"
if !strings.HasPrefix(headlines[0], expected) {
t.Errorf("want interface `lo` from `ifconfig lo`, got:\n%s", out)
}
}
})
t.Run("Interface name no match: `ifconfig o` should return 0 interface", func(t *testing.T) {
cmd := exec.Command("/bin/ifconfig", "o")
cmd.Stdin = &nullReader{}
out, err := cmd.CombinedOutput()
if err != nil {
t.Errorf("want no error but got error:\n%s", out)
}
expected := "ifconfig: no such interface"
if !(strings.HasPrefix(string(out), expected)) {
t.Errorf("want no interface from `ifconfig o`, got:\n%s", out)
}
})
t.Run("Interface name partial match: `ifconfig l` should return 1 interface", func(t *testing.T) {
cmd := exec.Command("/bin/ifconfig", "l")
cmd.Stdin = &nullReader{}
out, err := cmd.CombinedOutput()
if err != nil {
t.Errorf("want no error from `ifconfig l` but got error:\n%s", out)
}
headlines := findLinesContainingHWaddr(string(out))
if len(headlines) != 1 {
t.Errorf("want exactly 1 interface from `ifconfig l`, got: %d", len(headlines))
} else {
expected := "lo\tHWaddr Id:1"
if !strings.HasPrefix(headlines[0], expected) {
t.Errorf("want interface `lo` from `ifconfig l`, got:\n%s", out)
}
}
})
t.Run("ambiguous match: `ifconfig \"\"` should return 0 interfaces", func(t *testing.T) {
// TODO(fxbug.dev/20973): add an interface to ensure there are at least 2 interfaces.
})
}
func findLinesContainingHWaddr(in string) []string {
var out []string
for _, line := range strings.Split(in, "\n") {
if strings.Contains(line, "HWaddr") {
out = append(out, line)
}
}
return out
}