1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 package redis
16
17 import (
18 "bufio"
19 "errors"
20 "flag"
21 "fmt"
22 "io"
23 "io/ioutil"
24 "os"
25 "os/exec"
26 "strconv"
27 "strings"
28 "sync"
29 "testing"
30 "time"
31 )
32
33 func SetNowFunc(f func() time.Time) {
34 nowFunc = f
35 }
36
37 var (
38 ErrNegativeInt = errNegativeInt
39
40 serverPath = flag.String("redis-server", "redis-server", "Path to redis server binary")
41 serverAddress = flag.String("redis-address", "127.0.0.1", "The address of the server")
42 serverBasePort = flag.Int("redis-port", 16379, "Beginning of port range for test servers")
43 serverLogName = flag.String("redis-log", "", "Write Redis server logs to `filename`")
44 serverLog = ioutil.Discard
45
46 defaultServerMu sync.Mutex
47 defaultServer *Server
48 defaultServerErr error
49 )
50
51 type Server struct {
52 name string
53 cmd *exec.Cmd
54 done chan struct{}
55 }
56
57 func NewServer(name string, args ...string) (*Server, error) {
58 s := &Server{
59 name: name,
60 cmd: exec.Command(*serverPath, args...),
61 done: make(chan struct{}),
62 }
63
64 r, err := s.cmd.StdoutPipe()
65 if err != nil {
66 return nil, err
67 }
68
69 err = s.cmd.Start()
70 if err != nil {
71 return nil, err
72 }
73
74 ready := make(chan error, 1)
75 go s.watch(r, ready)
76
77 select {
78 case err = <-ready:
79 case <-time.After(time.Second * 10):
80 err = errors.New("timeout waiting for server to start")
81 }
82
83 if err != nil {
84 s.Stop()
85 return nil, err
86 }
87
88 return s, nil
89 }
90
91 func (s *Server) watch(r io.Reader, ready chan error) {
92 fmt.Fprintf(serverLog, "%d START %s \n", s.cmd.Process.Pid, s.name)
93 var listening bool
94 var text string
95 scn := bufio.NewScanner(r)
96 for scn.Scan() {
97 text = scn.Text()
98 fmt.Fprintf(serverLog, "%s\n", text)
99 if !listening {
100 if strings.Contains(text, " * Ready to accept connections") ||
101 strings.Contains(text, " * The server is now ready to accept connections on port") {
102 listening = true
103 ready <- nil
104 }
105 }
106 }
107 if !listening {
108 ready <- fmt.Errorf("server exited: %s", text)
109 }
110 s.cmd.Wait()
111 fmt.Fprintf(serverLog, "%d STOP %s \n", s.cmd.Process.Pid, s.name)
112 close(s.done)
113 }
114
115 func (s *Server) Stop() {
116 s.cmd.Process.Signal(os.Interrupt)
117 <-s.done
118 }
119
120
121 func stopDefaultServer() {
122 defaultServerMu.Lock()
123 defer defaultServerMu.Unlock()
124 if defaultServer != nil {
125 defaultServer.Stop()
126 defaultServer = nil
127 }
128 }
129
130
131
132 func DefaultServerAddr() (string, error) {
133 defaultServerMu.Lock()
134 defer defaultServerMu.Unlock()
135 addr := fmt.Sprintf("%v:%d", *serverAddress, *serverBasePort)
136 if defaultServer != nil || defaultServerErr != nil {
137 return addr, defaultServerErr
138 }
139 defaultServer, defaultServerErr = NewServer(
140 "default",
141 "--port", strconv.Itoa(*serverBasePort),
142 "--bind", *serverAddress,
143 "--save", "",
144 "--appendonly", "no")
145 return addr, defaultServerErr
146 }
147
148
149
150 func DialDefaultServer() (Conn, error) {
151 addr, err := DefaultServerAddr()
152 if err != nil {
153 return nil, err
154 }
155 c, err := Dial("tcp", addr, DialReadTimeout(1*time.Second), DialWriteTimeout(1*time.Second))
156 if err != nil {
157 return nil, err
158 }
159 c.Do("FLUSHDB")
160 return c, nil
161 }
162
163 func TestMain(m *testing.M) {
164 os.Exit(func() int {
165 flag.Parse()
166
167 var f *os.File
168 if *serverLogName != "" {
169 var err error
170 f, err = os.OpenFile(*serverLogName, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0600)
171 if err != nil {
172 fmt.Fprintf(os.Stderr, "Error opening redis-log: %v\n", err)
173 return 1
174 }
175 defer f.Close()
176 serverLog = f
177 }
178
179 defer stopDefaultServer()
180
181 return m.Run()
182 }())
183 }
184
View as plain text