From 21ed40c8cb4795433bda0cfb7754db5603a916f0 Mon Sep 17 00:00:00 2001 From: Mikio Hara Date: Wed, 1 Apr 2015 22:18:33 +0900 Subject: [PATCH] net: deflake TestDialGoogle, TestResolveDialGoogle This change makes use of the socktest package instead of the non-thread safe variable syscall.SocketDisableIPv6 for simulating unreachable external networks. Also adds -ipv4 flag, -ipv6 flag already exists, as a control knob for testing on each of IPv4-only, IPv6-only and dual IP stack kernels. Fixes #7687. Change-Id: I82002007fd526e8cf4de207f935e721df049a22f Reviewed-on: https://go-review.googlesource.com/8390 Reviewed-by: Ian Lance Taylor --- src/net/dialgoogle_test.go | 289 ++++++++++++++++++------------------- src/net/main_plan9_test.go | 4 + src/net/main_posix_test.go | 34 +++++ src/net/main_test.go | 8 +- 4 files changed, 184 insertions(+), 151 deletions(-) create mode 100644 src/net/main_posix_test.go diff --git a/src/net/dialgoogle_test.go b/src/net/dialgoogle_test.go index 084de9f444..20611ff420 100644 --- a/src/net/dialgoogle_test.go +++ b/src/net/dialgoogle_test.go @@ -8,198 +8,189 @@ import ( "fmt" "io" "strings" - "syscall" "testing" ) func TestResolveGoogle(t *testing.T) { if testing.Short() || !*testExternal { - t.Skip("skipping test to avoid external network") + t.Skip("avoid external network") + } + if !supportsIPv4 && !supportsIPv6 { + t.Skip("ipv4 and ipv6 are not supported") } for _, network := range []string{"tcp", "tcp4", "tcp6"} { addr, err := ResolveTCPAddr(network, "www.google.com:http") if err != nil { - if (network == "tcp" || network == "tcp4") && !supportsIPv4 { - t.Logf("ipv4 is not supported: %v", err) - } else if network == "tcp6" && !supportsIPv6 { - t.Logf("ipv6 is not supported: %v", err) - } else { - t.Errorf("ResolveTCPAddr failed: %v", err) + switch { + case network == "tcp" && !supportsIPv4: + fallthrough + case network == "tcp4" && !supportsIPv4: + t.Logf("skipping test; ipv4 is not supported: %v", err) + case network == "tcp6" && !supportsIPv6: + t.Logf("skipping test; ipv6 is not supported: %v", err) + default: + t.Error(err) } continue } - if (network == "tcp" || network == "tcp4") && addr.IP.To4() == nil { - t.Errorf("got %v; expected an IPv4 address on %v", addr, network) - } else if network == "tcp6" && (addr.IP.To16() == nil || addr.IP.To4() != nil) { - t.Errorf("got %v; expected an IPv6 address on %v", addr, network) + + switch { + case network == "tcp" && addr.IP.To4() == nil: + fallthrough + case network == "tcp4" && addr.IP.To4() == nil: + t.Errorf("got %v; want an ipv4 address on %s", addr, network) + case network == "tcp6" && (addr.IP.To16() == nil || addr.IP.To4() != nil): + t.Errorf("got %v; want an ipv6 address on %s", addr, network) } } } +var dialGoogleTests = []struct { + dial func(string, string) (Conn, error) + unreachableNetwork string + networks []string + addrs []string +}{ + { + dial: (&Dialer{DualStack: true}).Dial, + networks: []string{"tcp", "tcp4", "tcp6"}, + addrs: []string{"www.google.com:http"}, + }, + { + dial: Dial, + unreachableNetwork: "tcp6", + networks: []string{"tcp", "tcp4"}, + }, + { + dial: Dial, + unreachableNetwork: "tcp4", + networks: []string{"tcp", "tcp6"}, + }, +} + func TestDialGoogle(t *testing.T) { if testing.Short() || !*testExternal { - t.Skip("skipping test to avoid external network") - } - - d := &Dialer{DualStack: true} - for _, network := range []string{"tcp", "tcp4", "tcp6"} { - if network == "tcp" && !supportsIPv4 && !supportsIPv6 { - t.Logf("skipping test; both ipv4 and ipv6 are not supported") - continue - } else if network == "tcp4" && !supportsIPv4 { - t.Logf("skipping test; ipv4 is not supported") - continue - } else if network == "tcp6" && !supportsIPv6 { - t.Logf("skipping test; ipv6 is not supported") - continue - } else if network == "tcp6" && !*testIPv6 { - t.Logf("test disabled; use -ipv6 to enable") - continue - } - if c, err := d.Dial(network, "www.google.com:http"); err != nil { - t.Errorf("Dial failed: %v", err) - } else { - c.Close() - } + t.Skip("avoid external network") } -} - -// fd is already connected to the destination, port 80. -// Run an HTTP request to fetch the appropriate page. -func fetchGoogle(t *testing.T, fd Conn, network, addr string) { - req := []byte("GET /robots.txt HTTP/1.0\r\nHost: www.google.com\r\n\r\n") - n, err := fd.Write(req) - - buf := make([]byte, 1000) - n, err = io.ReadFull(fd, buf) - - if n < 1000 { - t.Errorf("fetchGoogle: short HTTP read from %s %s - %v", network, addr, err) - return + if !supportsIPv4 && !supportsIPv6 { + t.Skip("ipv4 and ipv6 are not supported") } -} -func doDial(t *testing.T, network, addr string) { - fd, err := Dial(network, addr) + var err error + dialGoogleTests[1].addrs, dialGoogleTests[2].addrs, err = googleLiteralAddrs() if err != nil { - t.Errorf("Dial(%q, %q, %q) = _, %v", network, "", addr, err) - return - } - fetchGoogle(t, fd, network, addr) - fd.Close() -} + t.Error(err) + } + for _, tt := range dialGoogleTests { + for _, network := range tt.networks { + switch { + case network == "tcp4" && !supportsIPv4: + t.Log("skipping test; ipv4 is not supported") + continue + case network == "tcp4" && !*testIPv4: + fallthrough + case tt.unreachableNetwork == "tcp6" && !*testIPv4: + t.Log("disabled; use -ipv4 to enable") + continue + case network == "tcp6" && !supportsIPv6: + t.Log("skipping test; ipv6 is not supported") + continue + case network == "tcp6" && !*testIPv6: + fallthrough + case tt.unreachableNetwork == "tcp4" && !*testIPv6: + t.Log("disabled; use -ipv6 to enable") + continue + } -var googleaddrsipv4 = []string{ - "%d.%d.%d.%d:80", - "www.google.com:80", - "%d.%d.%d.%d:http", - "www.google.com:http", - "%03d.%03d.%03d.%03d:0080", - "[::ffff:%d.%d.%d.%d]:80", - "[::ffff:%02x%02x:%02x%02x]:80", - "[0:0:0:0:0000:ffff:%d.%d.%d.%d]:80", - "[0:0:0:0:000000:ffff:%d.%d.%d.%d]:80", - "[0:0:0:0::ffff:%d.%d.%d.%d]:80", + disableSocketConnect(tt.unreachableNetwork) + for _, addr := range tt.addrs { + if err := fetchGoogle(tt.dial, network, addr); err != nil { + t.Error(err) + } + } + enableSocketConnect() + } + } } -func TestDialGoogleIPv4(t *testing.T) { - if testing.Short() || !*testExternal { - t.Skip("skipping test to avoid external network") +var ( + literalAddrs4 = [...]string{ + "%d.%d.%d.%d:80", + "www.google.com:80", + "%d.%d.%d.%d:http", + "www.google.com:http", + "%03d.%03d.%03d.%03d:0080", + "[::ffff:%d.%d.%d.%d]:80", + "[::ffff:%02x%02x:%02x%02x]:80", + "[0:0:0:0:0000:ffff:%d.%d.%d.%d]:80", + "[0:0:0:0:000000:ffff:%d.%d.%d.%d]:80", + "[0:0:0:0::ffff:%d.%d.%d.%d]:80", + } + literalAddrs6 = [...]string{ + "[%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x]:80", + "ipv6.google.com:80", + "[%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x]:http", + "ipv6.google.com:http", } +) - // Insert an actual IPv4 address for google.com - // into the table. - addrs, err := LookupIP("www.google.com") +func googleLiteralAddrs() (lits4, lits6 []string, err error) { + ips, err := LookupIP("www.google.com") if err != nil { - t.Fatalf("lookup www.google.com: %v", err) + return nil, nil, err } - var ip IP - for _, addr := range addrs { - if x := addr.To4(); x != nil { - ip = x + if len(ips) == 0 { + return nil, nil, nil + } + var ip4, ip6 IP + for _, ip := range ips { + if ip4 == nil && ip.To4() != nil { + ip4 = ip.To4() + } + if ip6 == nil && ip.To16() != nil && ip.To4() == nil { + ip6 = ip.To16() + } + if ip4 != nil && ip6 != nil { break } } - if ip == nil { - t.Fatalf("no IPv4 addresses for www.google.com") - } - - for i, s := range googleaddrsipv4 { - if strings.Contains(s, "%") { - googleaddrsipv4[i] = fmt.Sprintf(s, ip[0], ip[1], ip[2], ip[3]) + if ip4 != nil { + for i, lit4 := range literalAddrs4 { + if strings.Contains(lit4, "%") { + literalAddrs4[i] = fmt.Sprintf(lit4, ip4[0], ip4[1], ip4[2], ip4[3]) + } } + lits4 = literalAddrs4[:] } - - for i := 0; i < len(googleaddrsipv4); i++ { - addr := googleaddrsipv4[i] - if addr == "" { - continue - } - t.Logf("-- %s --", addr) - doDial(t, "tcp", addr) - if addr[0] != '[' { - doDial(t, "tcp4", addr) - if supportsIPv6 { - // make sure syscall.SocketDisableIPv6 flag works. - syscall.SocketDisableIPv6 = true - doDial(t, "tcp", addr) - doDial(t, "tcp4", addr) - syscall.SocketDisableIPv6 = false + if ip6 != nil { + for i, lit6 := range literalAddrs6 { + if strings.Contains(lit6, "%") { + literalAddrs6[i] = fmt.Sprintf(lit6, ip6[0], ip6[1], ip6[2], ip6[3], ip6[4], ip6[5], ip6[6], ip6[7], ip6[8], ip6[9], ip6[10], ip6[11], ip6[12], ip6[13], ip6[14], ip6[15]) } } + lits6 = literalAddrs6[:] } + return } -var googleaddrsipv6 = []string{ - "[%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x]:80", - "ipv6.google.com:80", - "[%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x]:http", - "ipv6.google.com:http", -} - -func TestDialGoogleIPv6(t *testing.T) { - if testing.Short() || !*testExternal { - t.Skip("skipping test to avoid external network") - } - // Only run tcp6 if the kernel will take it. - if !supportsIPv6 { - t.Skip("skipping test; ipv6 is not supported") - } - if !*testIPv6 { - t.Skip("test disabled; use -ipv6 to enable") - } - - // Insert an actual IPv6 address for ipv6.google.com - // into the table. - addrs, err := LookupIP("ipv6.google.com") +func fetchGoogle(dial func(string, string) (Conn, error), network, address string) error { + c, err := dial(network, address) if err != nil { - t.Fatalf("lookup ipv6.google.com: %v", err) + return err } - var ip IP - for _, addr := range addrs { - if x := addr.To16(); x != nil { - ip = x - break - } - } - if ip == nil { - t.Fatalf("no IPv6 addresses for ipv6.google.com") + defer c.Close() + req := []byte("GET /robots.txt HTTP/1.0\r\nHost: www.google.com\r\n\r\n") + if _, err := c.Write(req); err != nil { + return err } - - for i, s := range googleaddrsipv6 { - if strings.Contains(s, "%") { - googleaddrsipv6[i] = fmt.Sprintf(s, ip[0], ip[1], ip[2], ip[3], ip[4], ip[5], ip[6], ip[7], ip[8], ip[9], ip[10], ip[11], ip[12], ip[13], ip[14], ip[15]) - } + b := make([]byte, 1000) + n, err := io.ReadFull(c, b) + if err != nil { + return err } - - for i := 0; i < len(googleaddrsipv6); i++ { - addr := googleaddrsipv6[i] - if addr == "" { - continue - } - t.Logf("-- %s --", addr) - doDial(t, "tcp", addr) - doDial(t, "tcp6", addr) + if n < 1000 { + return fmt.Errorf("short read from %s:%s->%s", network, c.RemoteAddr(), c.LocalAddr()) } + return nil } diff --git a/src/net/main_plan9_test.go b/src/net/main_plan9_test.go index bbd47aaaf6..94501cada9 100644 --- a/src/net/main_plan9_test.go +++ b/src/net/main_plan9_test.go @@ -9,3 +9,7 @@ func installTestHooks() {} func uninstallTestHooks() {} func forceCloseSockets() {} + +func enableSocketConnect() {} + +func disableSocketConnect(network string) {} diff --git a/src/net/main_posix_test.go b/src/net/main_posix_test.go new file mode 100644 index 0000000000..da80ff03c8 --- /dev/null +++ b/src/net/main_posix_test.go @@ -0,0 +1,34 @@ +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build !plan9 + +package net + +import ( + "net/internal/socktest" + "strings" + "syscall" +) + +func enableSocketConnect() { + sw.Set(socktest.FilterConnect, nil) +} + +func disableSocketConnect(network string) { + ss := strings.Split(network, ":") + sw.Set(socktest.FilterConnect, func(so *socktest.Status) (socktest.AfterFilter, error) { + switch ss[0] { + case "tcp4", "udp4", "ip4": + if so.Cookie.Family() == syscall.AF_INET { + return nil, syscall.EHOSTUNREACH + } + case "tcp6", "udp6", "ip6": + if so.Cookie.Family() == syscall.AF_INET6 { + return nil, syscall.EHOSTUNREACH + } + } + return nil, nil + }) +} diff --git a/src/net/main_test.go b/src/net/main_test.go index 2948c4ecd8..ac56d31a25 100644 --- a/src/net/main_test.go +++ b/src/net/main_test.go @@ -34,6 +34,10 @@ var ( testExternal = flag.Bool("external", true, "allow use of external networks during long test") + // If external IPv4 connectivity exists, we can try dialing + // non-node/interface local scope IPv4 addresses. + testIPv4 = flag.Bool("ipv4", true, "assume external IPv4 connectivity exists") + // If external IPv6 connectivity exists, we can try dialing // non-node/interface local scope IPv6 addresses. testIPv6 = flag.Bool("ipv6", false, "assume external IPv6 connectivity exists") @@ -70,8 +74,8 @@ func printLeakedGoroutines() { fmt.Fprintf(os.Stderr, "\n") } -// leakedGoroutines returns a list of remaining goroutins used in test -// cases. +// leakedGoroutines returns a list of remaining goroutines used in +// test cases. func leakedGoroutines() []string { var gss []string b := make([]byte, 2<<20) -- 2.48.1