github.com/insionng/yougam@v0.0.0-20170714101924-2bc18d833463/libraries/golang/groupcache/http_test.go (about) 1 /* 2 Copyright 2013 Google Inc. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package groupcache 18 19 import ( 20 "errors" 21 "flag" 22 "log" 23 "net" 24 "net/http" 25 "os" 26 "os/exec" 27 "strconv" 28 "strings" 29 "sync" 30 "testing" 31 "time" 32 ) 33 34 var ( 35 peerAddrs = flag.String("test_peer_addrs", "", "Comma-separated list of peer addresses; used by TestHTTPPool") 36 peerIndex = flag.Int("test_peer_index", -1, "Index of which peer this child is; used by TestHTTPPool") 37 peerChild = flag.Bool("test_peer_child", false, "True if running as a child process; used by TestHTTPPool") 38 ) 39 40 func TestHTTPPool(t *testing.T) { 41 if *peerChild { 42 beChildForTestHTTPPool() 43 os.Exit(0) 44 } 45 46 const ( 47 nChild = 4 48 nGets = 100 49 ) 50 51 var childAddr []string 52 for i := 0; i < nChild; i++ { 53 childAddr = append(childAddr, pickFreeAddr(t)) 54 } 55 56 var cmds []*exec.Cmd 57 var wg sync.WaitGroup 58 for i := 0; i < nChild; i++ { 59 cmd := exec.Command(os.Args[0], 60 "--test.run=TestHTTPPool", 61 "--test_peer_child", 62 "--test_peer_addrs="+strings.Join(childAddr, ","), 63 "--test_peer_index="+strconv.Itoa(i), 64 ) 65 cmds = append(cmds, cmd) 66 wg.Add(1) 67 if err := cmd.Start(); err != nil { 68 t.Fatal("failed to start child process: ", err) 69 } 70 go awaitAddrReady(t, childAddr[i], &wg) 71 } 72 defer func() { 73 for i := 0; i < nChild; i++ { 74 if cmds[i].Process != nil { 75 cmds[i].Process.Kill() 76 } 77 } 78 }() 79 wg.Wait() 80 81 // Use a dummy self address so that we don't handle gets in-process. 82 p := NewHTTPPool("should-be-ignored") 83 p.Set(addrToURL(childAddr)...) 84 85 // Dummy getter function. Gets should go to children only. 86 // The only time this process will handle a get is when the 87 // children can't be contacted for some reason. 88 getter := GetterFunc(func(ctx Context, key string, dest Sink) error { 89 return errors.New("parent getter called; something's wrong") 90 }) 91 g := NewGroup("httpPoolTest", 1<<20, getter) 92 93 for _, key := range testKeys(nGets) { 94 var value string 95 if err := g.Get(nil, key, StringSink(&value)); err != nil { 96 t.Fatal(err) 97 } 98 if suffix := ":" + key; !strings.HasSuffix(value, suffix) { 99 t.Errorf("Get(%q) = %q, want value ending in %q", key, value, suffix) 100 } 101 t.Logf("Get key=%q, value=%q (peer:key)", key, value) 102 } 103 } 104 105 func testKeys(n int) (keys []string) { 106 keys = make([]string, n) 107 for i := range keys { 108 keys[i] = strconv.Itoa(i) 109 } 110 return 111 } 112 113 func beChildForTestHTTPPool() { 114 addrs := strings.Split(*peerAddrs, ",") 115 116 p := NewHTTPPool("http://" + addrs[*peerIndex]) 117 p.Set(addrToURL(addrs)...) 118 119 getter := GetterFunc(func(ctx Context, key string, dest Sink) error { 120 dest.SetString(strconv.Itoa(*peerIndex) + ":" + key) 121 return nil 122 }) 123 NewGroup("httpPoolTest", 1<<20, getter) 124 125 log.Fatal(http.ListenAndServe(addrs[*peerIndex], p)) 126 } 127 128 // This is racy. Another process could swoop in and steal the port between the 129 // call to this function and the next listen call. Should be okay though. 130 // The proper way would be to pass the l.File() as ExtraFiles to the child 131 // process, and then close your copy once the child starts. 132 func pickFreeAddr(t *testing.T) string { 133 l, err := net.Listen("tcp", "127.0.0.1:0") 134 if err != nil { 135 t.Fatal(err) 136 } 137 defer l.Close() 138 return l.Addr().String() 139 } 140 141 func addrToURL(addr []string) []string { 142 url := make([]string, len(addr)) 143 for i := range addr { 144 url[i] = "http://" + addr[i] 145 } 146 return url 147 } 148 149 func awaitAddrReady(t *testing.T, addr string, wg *sync.WaitGroup) { 150 defer wg.Done() 151 const max = 1 * time.Second 152 tries := 0 153 for { 154 tries++ 155 c, err := net.Dial("tcp", addr) 156 if err == nil { 157 c.Close() 158 return 159 } 160 delay := time.Duration(tries) * 25 * time.Millisecond 161 if delay > max { 162 delay = max 163 } 164 time.Sleep(delay) 165 } 166 }