...

Source file src/github.com/golang/groupcache/http_test.go

Documentation: github.com/golang/groupcache

     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  	"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  	// Use a dummy self address so that we don't handle gets in-process.
    83  	p := NewHTTPPool("should-be-ignored")
    84  	p.Set(addrToURL(childAddr)...)
    85  
    86  	// Dummy getter function. Gets should go to children only.
    87  	// The only time this process will handle a get is when the
    88  	// children can't be contacted for some reason.
    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  // This is racy. Another process could swoop in and steal the port between the
   130  // call to this function and the next listen call. Should be okay though.
   131  // The proper way would be to pass the l.File() as ExtraFiles to the child
   132  // process, and then close your copy once the child starts.
   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