From 82f58c11e1f39e236382153fd2dddc4bacea2176 Mon Sep 17 00:00:00 2001 From: Jeff Johnson Date: Fri, 8 Dec 2017 09:21:57 -0800 Subject: [PATCH] net: enable TestInterfaceHardwareAddrWithGetmac on all windows versions Re-work the test to use wmic instead of PowerShell's getmac that's only avaliable on Server 2008. Maintains duplicate detection added for golang/go#21027. Tested on windows-amd64-{2008, 2012, 2016} buildlets. Enabling for Windows XP because it should work[1]. Fixes golang/go#20073 [1] https://www.microsoft.com/resources/documentation/windows/xp/all/proddocs/en-us/wmic_overview.mspx?mfr=true Change-Id: Ic11d569f7964f61d08ae0dcc1b926efc5336ac5b Reviewed-on: https://go-review.googlesource.com/82975 Reviewed-by: Brad Fitzpatrick Run-TryBot: Brad Fitzpatrick TryBot-Result: Gobot Gobot --- src/net/net_windows_test.go | 143 ++++++++++-------------------------- 1 file changed, 38 insertions(+), 105 deletions(-) diff --git a/src/net/net_windows_test.go b/src/net/net_windows_test.go index db211e9d6e..ab09cdb28a 100644 --- a/src/net/net_windows_test.go +++ b/src/net/net_windows_test.go @@ -503,139 +503,72 @@ func TestInterfaceAddrsWithNetsh(t *testing.T) { } } -// check that getmac exists as a powershell command, and that it -// speaks English. -func checkGetmac(t *testing.T) { - out, err := runCmd("getmac", "/?") - if err != nil { - if strings.Contains(err.Error(), "term 'getmac' is not recognized as the name of a cmdlet") { - t.Skipf("getmac not available") +func contains(needle string, haystack []string) bool { + for _, v := range haystack { + if v == needle { + return true } - t.Fatal(err) - } - if !bytes.Contains(out, []byte("network adapters on a system")) { - t.Skipf("skipping test on non-English system") } + return false } -func TestInterfaceHardwareAddrWithGetmac(t *testing.T) { - if isWindowsXP(t) { - t.Skip("Windows XP does not have powershell command") - } - checkGetmac(t) - +func TestInterfaceHardwareAddrWithWmic(t *testing.T) { ift, err := Interfaces() if err != nil { t.Fatal(err) } - have := make(map[string]string) + goMacToName := make(map[string]string) for _, ifi := range ift { if ifi.Flags&FlagLoopback != 0 { // no MAC address for loopback interfaces continue } - have[ifi.Name] = ifi.HardwareAddr.String() + goMacToName[ifi.HardwareAddr.String()] = ifi.Name } - out, err := runCmd("getmac", "/fo", "list", "/v") + //wmic nic get MACAddress,NetConnectionID /format:csv + // + //Node,MACAddress,NetConnectionID + //SERVER-2008R2-V,, + //SERVER-2008R2-V,42:01:0A:F0:00:18,Local Area Connection + //SERVER-2008R2-V,42:01:0A:F0:00:18,Duplicate Adapter + //SERVER-2008R2-V,20:41:53:59:4E:FF, + out, err := exec.Command("wmic", "nic", "get", "MACAddress,NetConnectionID", "/format:csv").CombinedOutput() if err != nil { t.Fatal(err) } - // getmac output looks like: - // - //Connection Name: Local Area Connection - //Network Adapter: Intel Gigabit Network Connection - //Physical Address: XX-XX-XX-XX-XX-XX - //Transport Name: \Device\Tcpip_{XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX} - // - //Connection Name: Wireless Network Connection - //Network Adapter: Wireles WLAN Card - //Physical Address: XX-XX-XX-XX-XX-XX - //Transport Name: Media disconnected - // - //Connection Name: Bluetooth Network Connection - //Network Adapter: Bluetooth Device (Personal Area Network) - //Physical Address: N/A - //Transport Name: Hardware not present - // - //Connection Name: VMware Network Adapter VMnet8 - //Network Adapter: VMware Virtual Ethernet Adapter for VMnet8 - //Physical Address: Disabled - //Transport Name: Disconnected - // - want := make(map[string]string) - group := make(map[string]string) // name / values for single adapter - getValue := func(name string) string { - value, found := group[name] - if !found { - t.Fatalf("%q has no %q line in it", group, name) - } - if value == "" { - t.Fatalf("%q has empty %q value", group, name) - } - return value - } - processGroup := func() { - if len(group) == 0 { - return - } - tname := strings.ToLower(getValue("Transport Name")) - if tname == "n/a" { - // skip these - return - } - addr := strings.ToLower(getValue("Physical Address")) - if addr == "disabled" || addr == "n/a" { - // skip these - return - } - addr = strings.Replace(addr, "-", ":", -1) - cname := getValue("Connection Name") - want[cname] = addr - group = make(map[string]string) - } + winMacToNames := make(map[string][]string) lines := bytes.Split(out, []byte{'\r', '\n'}) + for _, line := range lines { - if len(line) == 0 { - processGroup() + entry := strings.Split(string(line), ",") + if len(entry) != 3 || entry[1] == "MACAddress" { + // skip empty lines, header continue } - i := bytes.IndexByte(line, ':') - if i == -1 { - t.Fatalf("line %q has no : in it", line) + + mac, name := strings.ToLower(entry[1]), strings.TrimSpace(entry[2]) + if mac == "" || name == "" { + // skip non-physical devices + continue } - group[string(line[:i])] = string(bytes.TrimSpace(line[i+1:])) + + winMacToNames[mac] = append(winMacToNames[mac], name) } - processGroup() - dups := make(map[string][]string) - for name, addr := range want { - if _, ok := dups[addr]; !ok { - dups[addr] = make([]string, 0) - } - dups[addr] = append(dups[addr], name) + if len(goMacToName) != len(winMacToNames) { + t.Errorf("go interface count (%d, %v) differs from wmic count (%d, %v)", len(goMacToName), goMacToName, len(winMacToNames), winMacToNames) } -nextWant: - for name, wantAddr := range want { - if haveAddr, ok := have[name]; ok { - if haveAddr != wantAddr { - t.Errorf("unexpected MAC address for %q - %v, want %v", name, haveAddr, wantAddr) - } - continue - } - // We could not find the interface in getmac output by name. - // But sometimes getmac lists many interface names - // for the same MAC address. If that is the case here, - // and we can match at least one of those names, - // let's ignore the other names. - if dupNames, ok := dups[wantAddr]; ok && len(dupNames) > 1 { - for _, dupName := range dupNames { - if haveAddr, ok := have[dupName]; ok && haveAddr == wantAddr { - continue nextWant - } + for mac, name := range goMacToName { + // Windows appears to associate multiple names to a single MAC + // Consider it a success if one of those names was found + if cmdNames, ok := winMacToNames[mac]; ok { + if contains(name, cmdNames) { + continue } } - t.Errorf("getmac lists %q, but it could not be found among Go interfaces %v", name, have) + + t.Errorf("go found interface (name: %s, mac: %s) not found by wmic (%v)", name, mac, winMacToNames) } } -- 2.50.0