1
16
17 package groupcache
18
19 import (
20 "context"
21 "errors"
22 "flag"
23 "log"
24 "net"
25 "net/http"
26 "os"
27 "os/exec"
28 "strconv"
29 "strings"
30 "sync"
31 "testing"
32 "time"
33 )
34
35 var (
36 peerAddrs = flag.String("test_peer_addrs", "", "Comma-separated list of peer addresses; used by TestHTTPPool")
37 peerIndex = flag.Int("test_peer_index", -1, "Index of which peer this child is; used by TestHTTPPool")
38 peerChild = flag.Bool("test_peer_child", false, "True if running as a child process; used by TestHTTPPool")
39 )
40
41 func TestHTTPPool(t *testing.T) {
42 if *peerChild {
43 beChildForTestHTTPPool()
44 os.Exit(0)
45 }
46
47 const (
48 nChild = 4
49 nGets = 100
50 )
51
52 var childAddr []string
53 for i := 0; i < nChild; i++ {
54 childAddr = append(childAddr, pickFreeAddr(t))
55 }
56
57 var cmds []*exec.Cmd
58 var wg sync.WaitGroup
59 for i := 0; i < nChild; i++ {
60 cmd := exec.Command(os.Args[0],
61 "--test.run=TestHTTPPool",
62 "--test_peer_child",
63 "--test_peer_addrs="+strings.Join(childAddr, ","),
64 "--test_peer_index="+strconv.Itoa(i),
65 )
66 cmds = append(cmds, cmd)
67 wg.Add(1)
68 if err := cmd.Start(); err != nil {
69 t.Fatal("failed to start child process: ", err)
70 }
71 go awaitAddrReady(t, childAddr[i], &wg)
72 }
73 defer func() {
74 for i := 0; i < nChild; i++ {
75 if cmds[i].Process != nil {
76 cmds[i].Process.Kill()
77 }
78 }
79 }()
80 wg.Wait()
81
82
83 p := NewHTTPPool("should-be-ignored")
84 p.Set(addrToURL(childAddr)...)
85
86
87
88
89 getter := GetterFunc(func(ctx context.Context, key string, dest Sink) error {
90 return errors.New("parent getter called; something's wrong")
91 })
92 g := NewGroup("httpPoolTest", 1<<20, getter)
93
94 for _, key := range testKeys(nGets) {
95 var value string
96 if err := g.Get(context.TODO(), key, StringSink(&value)); err != nil {
97 t.Fatal(err)
98 }
99 if suffix := ":" + key; !strings.HasSuffix(value, suffix) {
100 t.Errorf("Get(%q) = %q, want value ending in %q", key, value, suffix)
101 }
102 t.Logf("Get key=%q, value=%q (peer:key)", key, value)
103 }
104 }
105
106 func testKeys(n int) (keys []string) {
107 keys = make([]string, n)
108 for i := range keys {
109 keys[i] = strconv.Itoa(i)
110 }
111 return
112 }
113
114 func beChildForTestHTTPPool() {
115 addrs := strings.Split(*peerAddrs, ",")
116
117 p := NewHTTPPool("http://" + addrs[*peerIndex])
118 p.Set(addrToURL(addrs)...)
119
120 getter := GetterFunc(func(ctx context.Context, key string, dest Sink) error {
121 dest.SetString(strconv.Itoa(*peerIndex) + ":" + key)
122 return nil
123 })
124 NewGroup("httpPoolTest", 1<<20, getter)
125
126 log.Fatal(http.ListenAndServe(addrs[*peerIndex], p))
127 }
128
129
130
131
132
133 func pickFreeAddr(t *testing.T) string {
134 l, err := net.Listen("tcp", "127.0.0.1:0")
135 if err != nil {
136 t.Fatal(err)
137 }
138 defer l.Close()
139 return l.Addr().String()
140 }
141
142 func addrToURL(addr []string) []string {
143 url := make([]string, len(addr))
144 for i := range addr {
145 url[i] = "http://" + addr[i]
146 }
147 return url
148 }
149
150 func awaitAddrReady(t *testing.T, addr string, wg *sync.WaitGroup) {
151 defer wg.Done()
152 const max = 1 * time.Second
153 tries := 0
154 for {
155 tries++
156 c, err := net.Dial("tcp", addr)
157 if err == nil {
158 c.Close()
159 return
160 }
161 delay := time.Duration(tries) * 25 * time.Millisecond
162 if delay > max {
163 delay = max
164 }
165 time.Sleep(delay)
166 }
167 }
168
View as plain text