Przeglądaj źródła

Start Redis server used for tests

Gary Burd 10 lat temu
rodzic
commit
c13eb4c3ec
7 zmienionych plików z 263 dodań i 101 usunięć
  1. 66 54
      redis/conn_test.go
  2. 5 6
      redis/pool_test.go
  3. 9 11
      redis/pubsub_test.go
  4. 10 7
      redis/reply_test.go
  5. 24 12
      redis/scan_test.go
  6. 1 2
      redis/script_test.go
  7. 148 9
      redis/test_test.go

+ 66 - 54
redis/conn_test.go

@@ -15,8 +15,8 @@
 package redis_test
 
 import (
-	"bufio"
 	"bytes"
+	"io"
 	"math"
 	"net"
 	"os"
@@ -25,10 +25,27 @@ import (
 	"testing"
 	"time"
 
-	"github.com/garyburd/redigo/internal/redistest"
 	"github.com/garyburd/redigo/redis"
 )
 
+type testConn struct {
+	io.Reader
+	io.Writer
+}
+
+func (*testConn) Close() error                       { return nil }
+func (*testConn) LocalAddr() net.Addr                { return nil }
+func (*testConn) RemoteAddr() net.Addr               { return nil }
+func (*testConn) SetDeadline(t time.Time) error      { return nil }
+func (*testConn) SetReadDeadline(t time.Time) error  { return nil }
+func (*testConn) SetWriteDeadline(t time.Time) error { return nil }
+
+func dialTestConn(r io.Reader, w io.Writer) redis.DialOption {
+	return redis.DialNetDial(func(net, addr string) (net.Conn, error) {
+		return &testConn{Reader: r, Writer: w}, nil
+	})
+}
+
 var writeTests = []struct {
 	args     []interface{}
 	expected string
@@ -74,14 +91,13 @@ var writeTests = []struct {
 func TestWrite(t *testing.T) {
 	for _, tt := range writeTests {
 		var buf bytes.Buffer
-		rw := bufio.ReadWriter{Writer: bufio.NewWriter(&buf)}
-		c := redis.NewConnBufio(rw)
+		c, _ := redis.Dial("", "", dialTestConn(nil, &buf))
 		err := c.Send(tt.args[0].(string), tt.args[1:]...)
 		if err != nil {
 			t.Errorf("Send(%v) returned error %v", tt.args, err)
 			continue
 		}
-		rw.Flush()
+		c.Flush()
 		actual := buf.String()
 		if actual != tt.expected {
 			t.Errorf("Send(%v) = %q, want %q", tt.args, actual, tt.expected)
@@ -174,11 +190,7 @@ var readTests = []struct {
 
 func TestRead(t *testing.T) {
 	for _, tt := range readTests {
-		rw := bufio.ReadWriter{
-			Reader: bufio.NewReader(strings.NewReader(tt.reply)),
-			Writer: bufio.NewWriter(nil), // writer need to support Flush
-		}
-		c := redis.NewConnBufio(rw)
+		c, _ := redis.Dial("", "", dialTestConn(strings.NewReader(tt.reply), nil))
 		actual, err := c.Receive()
 		if tt.expected == errorSentinel {
 			if err == nil {
@@ -258,7 +270,7 @@ var testCommands = []struct {
 }
 
 func TestDoCommands(t *testing.T) {
-	c, err := redistest.Dial()
+	c, err := redis.DialDefaultServer()
 	if err != nil {
 		t.Fatalf("error connection to database, %v", err)
 	}
@@ -277,7 +289,7 @@ func TestDoCommands(t *testing.T) {
 }
 
 func TestPipelineCommands(t *testing.T) {
-	c, err := redistest.Dial()
+	c, err := redis.DialDefaultServer()
 	if err != nil {
 		t.Fatalf("error connection to database, %v", err)
 	}
@@ -303,7 +315,7 @@ func TestPipelineCommands(t *testing.T) {
 }
 
 func TestBlankCommmand(t *testing.T) {
-	c, err := redistest.Dial()
+	c, err := redis.DialDefaultServer()
 	if err != nil {
 		t.Fatalf("error connection to database, %v", err)
 	}
@@ -330,7 +342,7 @@ func TestBlankCommmand(t *testing.T) {
 }
 
 func TestRecvBeforeSend(t *testing.T) {
-	c, err := redistest.Dial()
+	c, err := redis.DialDefaultServer()
 	if err != nil {
 		t.Fatalf("error connection to database, %v", err)
 	}
@@ -351,7 +363,7 @@ func TestRecvBeforeSend(t *testing.T) {
 }
 
 func TestError(t *testing.T) {
-	c, err := redistest.Dial()
+	c, err := redis.DialDefaultServer()
 	if err != nil {
 		t.Fatalf("error connection to database, %v", err)
 	}
@@ -371,7 +383,7 @@ func TestError(t *testing.T) {
 	}
 }
 
-func TestReadDeadline(t *testing.T) {
+func TestReadTimeout(t *testing.T) {
 	l, err := net.Listen("tcp", "127.0.0.1:0")
 	if err != nil {
 		t.Fatalf("net.Listen returned %v", err)
@@ -392,7 +404,9 @@ func TestReadDeadline(t *testing.T) {
 		}
 	}()
 
-	c1, err := redis.DialTimeout(l.Addr().Network(), l.Addr().String(), 0, time.Millisecond, 0)
+	// Do
+
+	c1, err := redis.Dial(l.Addr().Network(), l.Addr().String(), redis.DialReadTimeout(time.Millisecond))
 	if err != nil {
 		t.Fatalf("redis.Dial returned %v", err)
 	}
@@ -406,7 +420,9 @@ func TestReadDeadline(t *testing.T) {
 		t.Fatalf("c1.Err() = nil, expect error")
 	}
 
-	c2, err := redis.DialTimeout(l.Addr().Network(), l.Addr().String(), 0, time.Millisecond, 0)
+	// Send/Flush/Receive
+
+	c2, err := redis.Dial(l.Addr().Network(), l.Addr().String(), redis.DialReadTimeout(time.Millisecond))
 	if err != nil {
 		t.Fatalf("redis.Dial returned %v", err)
 	}
@@ -445,77 +461,71 @@ var dialErrors = []struct {
 		"http://www.google.com",
 		"invalid redis URL scheme: http",
 	},
-	{
-		"redis://x:abc123@localhost",
-		"no password is set",
-	},
 	{
 		"redis://localhost:6379/abc123",
 		"invalid database: abc123",
 	},
 }
 
-func TestDialURL(t *testing.T) {
+func TestDialURLErrors(t *testing.T) {
 	for _, d := range dialErrors {
 		_, err := redis.DialURL(d.rawurl)
 		if err == nil || !strings.Contains(err.Error(), d.expectedError) {
 			t.Errorf("DialURL did not return expected error (expected %v to contain %s)", err, d.expectedError)
 		}
 	}
+}
 
+func TestDialURLPort(t *testing.T) {
 	checkPort := func(network, address string) (net.Conn, error) {
 		if address != "localhost:6379" {
 			t.Errorf("DialURL did not set port to 6379 by default (got %v)", address)
 		}
-		return net.Dial(network, address)
+		return nil, nil
 	}
-	c, err := redis.DialURL("redis://localhost", redis.DialNetDial(checkPort))
+	_, err := redis.DialURL("redis://localhost", redis.DialNetDial(checkPort))
 	if err != nil {
 		t.Error("dial error:", err)
 	}
-	c.Close()
+}
 
+func TestDialURLHost(t *testing.T) {
 	checkHost := func(network, address string) (net.Conn, error) {
 		if address != "localhost:6379" {
 			t.Errorf("DialURL did not set host to localhost by default (got %v)", address)
 		}
-		return net.Dial(network, address)
+		return nil, nil
 	}
-	c, err = redis.DialURL("redis://:6379", redis.DialNetDial(checkHost))
+	_, err := redis.DialURL("redis://:6379", redis.DialNetDial(checkHost))
 	if err != nil {
 		t.Error("dial error:", err)
 	}
-	c.Close()
+}
 
-	// Check that the database is set correctly
-	c1, err := redis.DialURL("redis://:6379/8")
-	defer c1.Close()
+func TestDialURLPassword(t *testing.T) {
+	var buf bytes.Buffer
+	_, err := redis.DialURL("redis://x:abc123@localhost", dialTestConn(strings.NewReader("+OK\r\n"), &buf))
 	if err != nil {
-		t.Error("Dial error:", err)
+		t.Error("dial error:", err)
 	}
-	dbSize, _ := redis.Int(c1.Do("DBSIZE"))
-	if dbSize > 0 {
-		t.Fatal("DB 8 has existing keys; aborting test to avoid overwriting data")
+	expected := "*2\r\n$4\r\nAUTH\r\n$6\r\nabc123\r\n"
+	actual := buf.String()
+	if actual != expected {
+		t.Errorf("commands = %q, want %q", actual, expected)
 	}
-	c1.Do("SET", "var", "val")
+}
 
-	c2, err := redis.Dial("tcp", ":6379")
-	defer c2.Close()
+func TestDialURLDatabase(t *testing.T) {
+	var buf bytes.Buffer
+	_, err := redis.DialURL("redis://localhost/3", dialTestConn(strings.NewReader("+OK\r\n"), &buf))
 	if err != nil {
 		t.Error("dial error:", err)
 	}
-	_, err = c2.Do("SELECT", "8")
-	if err != nil {
-		t.Error(err)
-	}
-	got, err := redis.String(c2.Do("GET", "var"))
-	if err != nil {
-		t.Error(err)
-	}
-	if got != "val" {
-		t.Error("DialURL did not correctly set the db.")
+	expected := "*2\r\n$6\r\nSELECT\r\n$1\r\n3\r\n"
+	actual := buf.String()
+	if actual != expected {
+		t.Errorf("commands = %q, want %q", actual, expected)
 	}
-	_, err = c2.Do("DEL", "var")
 }
 
 // Connect to local instance of Redis running on the default port.
@@ -540,7 +550,7 @@ func ExampleDialURL() {
 // http://redis.io/topics/transactions for information on how Redis handles
 // errors in a transaction.
 func TestExecError(t *testing.T) {
-	c, err := redistest.Dial()
+	c, err := redis.DialDefaultServer()
 	if err != nil {
 		t.Fatalf("error connection to database, %v", err)
 	}
@@ -548,6 +558,7 @@ func TestExecError(t *testing.T) {
 
 	// Execute commands that fail before EXEC is called.
 
+	c.Do("DEL", "k0")
 	c.Do("ZADD", "k0", 0, 0)
 	c.Send("MULTI")
 	c.Send("NOTACOMMAND", "k0", 0, 0)
@@ -560,6 +571,7 @@ func TestExecError(t *testing.T) {
 	// Execute commands that fail after EXEC is called. The first command
 	// returns an error.
 
+	c.Do("DEL", "k1")
 	c.Do("ZADD", "k1", 0, 0)
 	c.Send("MULTI")
 	c.Send("HSET", "k1", 0, 0)
@@ -583,7 +595,7 @@ func TestExecError(t *testing.T) {
 	}
 
 	if _, ok := vs[1].([]byte); !ok {
-		t.Fatalf("second result is type %T, expected []byte", vs[2])
+		t.Fatalf("second result is type %T, expected []byte", vs[1])
 	}
 
 	// Execute commands that fail after EXEC is called. The second command
@@ -618,7 +630,7 @@ func TestExecError(t *testing.T) {
 
 func BenchmarkDoEmpty(b *testing.B) {
 	b.StopTimer()
-	c, err := redistest.Dial()
+	c, err := redis.DialDefaultServer()
 	if err != nil {
 		b.Fatal(err)
 	}
@@ -633,7 +645,7 @@ func BenchmarkDoEmpty(b *testing.B) {
 
 func BenchmarkDoPing(b *testing.B) {
 	b.StopTimer()
-	c, err := redistest.Dial()
+	c, err := redis.DialDefaultServer()
 	if err != nil {
 		b.Fatal(err)
 	}

+ 5 - 6
redis/pool_test.go

@@ -22,7 +22,6 @@ import (
 	"testing"
 	"time"
 
-	"github.com/garyburd/redigo/internal/redistest"
 	"github.com/garyburd/redigo/redis"
 )
 
@@ -74,7 +73,7 @@ func (d *poolDialer) dial() (redis.Conn, error) {
 	if dialErr != nil {
 		return nil, d.dialErr
 	}
-	c, err := redistest.Dial()
+	c, err := redis.DialDefaultServer()
 	if err != nil {
 		return nil, err
 	}
@@ -236,7 +235,7 @@ func TestPoolTimeout(t *testing.T) {
 
 func TestPoolConcurrenSendReceive(t *testing.T) {
 	p := &redis.Pool{
-		Dial: redistest.Dial,
+		Dial: redis.DialDefaultServer,
 	}
 	defer p.Close()
 
@@ -632,7 +631,7 @@ func TestLocking_TestOnBorrowFails_PoolDoesntCrash(t *testing.T) {
 
 func BenchmarkPoolGet(b *testing.B) {
 	b.StopTimer()
-	p := redis.Pool{Dial: redistest.Dial, MaxIdle: 2}
+	p := redis.Pool{Dial: redis.DialDefaultServer, MaxIdle: 2}
 	c := p.Get()
 	if err := c.Err(); err != nil {
 		b.Fatal(err)
@@ -648,7 +647,7 @@ func BenchmarkPoolGet(b *testing.B) {
 
 func BenchmarkPoolGetErr(b *testing.B) {
 	b.StopTimer()
-	p := redis.Pool{Dial: redistest.Dial, MaxIdle: 2}
+	p := redis.Pool{Dial: redis.DialDefaultServer, MaxIdle: 2}
 	c := p.Get()
 	if err := c.Err(); err != nil {
 		b.Fatal(err)
@@ -667,7 +666,7 @@ func BenchmarkPoolGetErr(b *testing.B) {
 
 func BenchmarkPoolGetPing(b *testing.B) {
 	b.StopTimer()
-	p := redis.Pool{Dial: redistest.Dial, MaxIdle: 2}
+	p := redis.Pool{Dial: redis.DialDefaultServer, MaxIdle: 2}
 	c := p.Get()
 	if err := c.Err(); err != nil {
 		b.Fatal(err)

+ 9 - 11
redis/pubsub_test.go

@@ -16,20 +16,18 @@ package redis_test
 
 import (
 	"fmt"
-	"net"
 	"reflect"
 	"sync"
 	"testing"
-	"time"
 
-	"github.com/garyburd/redigo/internal/redistest"
 	"github.com/garyburd/redigo/redis"
 )
 
 func publish(channel, value interface{}) {
 	c, err := dial()
 	if err != nil {
-		panic(err)
+		fmt.Println(err)
+		return
 	}
 	defer c.Close()
 	c.Do("PUBLISH", channel, value)
@@ -39,7 +37,8 @@ func publish(channel, value interface{}) {
 func ExamplePubSubConn() {
 	c, err := dial()
 	if err != nil {
-		panic(err)
+		fmt.Println(err)
+		return
 	}
 	defer c.Close()
 	var wg sync.WaitGroup
@@ -111,20 +110,19 @@ func expectPushed(t *testing.T, c redis.PubSubConn, message string, expected int
 }
 
 func TestPushed(t *testing.T) {
-	pc, err := redistest.Dial()
+	pc, err := redis.DialDefaultServer()
 	if err != nil {
 		t.Fatalf("error connection to database, %v", err)
 	}
 	defer pc.Close()
 
-	nc, err := net.Dial("tcp", ":6379")
+	sc, err := redis.DialDefaultServer()
 	if err != nil {
-		t.Fatal(err)
+		t.Fatalf("error connection to database, %v", err)
 	}
-	defer nc.Close()
-	nc.SetReadDeadline(time.Now().Add(4 * time.Second))
+	defer sc.Close()
 
-	c := redis.PubSubConn{Conn: redis.NewConn(nc, 0, 0)}
+	c := redis.PubSubConn{Conn: sc}
 
 	c.Subscribe("c1")
 	expectPushed(t, c, "Subscribe(c1)", redis.Subscription{Kind: "subscribe", Channel: "c1", Count: 1})

+ 10 - 7
redis/reply_test.go

@@ -19,7 +19,6 @@ import (
 	"reflect"
 	"testing"
 
-	"github.com/garyburd/redigo/internal/redistest"
 	"github.com/garyburd/redigo/redis"
 )
 
@@ -101,15 +100,16 @@ func TestReply(t *testing.T) {
 	}
 }
 
-// dial wraps DialTestDB() with a more suitable function name for examples.
+// dial wraps DialDefaultServer() with a more suitable function name for examples.
 func dial() (redis.Conn, error) {
-	return redistest.Dial()
+	return redis.DialDefaultServer()
 }
 
 func ExampleBool() {
 	c, err := dial()
 	if err != nil {
-		panic(err)
+		fmt.Println(err)
+		return
 	}
 	defer c.Close()
 
@@ -123,7 +123,8 @@ func ExampleBool() {
 func ExampleInt() {
 	c, err := dial()
 	if err != nil {
-		panic(err)
+		fmt.Println(err)
+		return
 	}
 	defer c.Close()
 
@@ -140,7 +141,8 @@ func ExampleInt() {
 func ExampleInts() {
 	c, err := dial()
 	if err != nil {
-		panic(err)
+		fmt.Println(err)
+		return
 	}
 	defer c.Close()
 
@@ -154,7 +156,8 @@ func ExampleInts() {
 func ExampleString() {
 	c, err := dial()
 	if err != nil {
-		panic(err)
+		fmt.Println(err)
+		return
 	}
 	defer c.Close()
 

+ 24 - 12
redis/scan_test.go

@@ -16,10 +16,11 @@ package redis_test
 
 import (
 	"fmt"
-	"github.com/garyburd/redigo/redis"
 	"math"
 	"reflect"
 	"testing"
+
+	"github.com/garyburd/redigo/redis"
 )
 
 var scanConversionTests = []struct {
@@ -100,7 +101,8 @@ func TestScanConversionError(t *testing.T) {
 func ExampleScan() {
 	c, err := dial()
 	if err != nil {
-		panic(err)
+		fmt.Println(err)
+		return
 	}
 	defer c.Close()
 
@@ -115,7 +117,8 @@ func ExampleScan() {
 		"GET", "album:*->title",
 		"GET", "album:*->rating"))
 	if err != nil {
-		panic(err)
+		fmt.Println(err)
+		return
 	}
 
 	for len(values) > 0 {
@@ -123,7 +126,8 @@ func ExampleScan() {
 		rating := -1 // initialize to illegal value to detect nil.
 		values, err = redis.Scan(values, &title, &rating)
 		if err != nil {
-			panic(err)
+			fmt.Println(err)
+			return
 		}
 		if rating == -1 {
 			fmt.Println(title, "not-rated")
@@ -295,7 +299,8 @@ func TestScanSlice(t *testing.T) {
 func ExampleScanSlice() {
 	c, err := dial()
 	if err != nil {
-		panic(err)
+		fmt.Println(err)
+		return
 	}
 	defer c.Close()
 
@@ -310,7 +315,8 @@ func ExampleScanSlice() {
 		"GET", "album:*->title",
 		"GET", "album:*->rating"))
 	if err != nil {
-		panic(err)
+		fmt.Println(err)
+		return
 	}
 
 	var albums []struct {
@@ -318,7 +324,8 @@ func ExampleScanSlice() {
 		Rating int
 	}
 	if err := redis.ScanSlice(values, &albums); err != nil {
-		panic(err)
+		fmt.Println(err)
+		return
 	}
 	fmt.Printf("%v\n", albums)
 	// Output:
@@ -364,7 +371,8 @@ func TestArgs(t *testing.T) {
 func ExampleArgs() {
 	c, err := dial()
 	if err != nil {
-		panic(err)
+		fmt.Println(err)
+		return
 	}
 	defer c.Close()
 
@@ -379,7 +387,8 @@ func ExampleArgs() {
 	p1.Body = "Hello"
 
 	if _, err := c.Do("HMSET", redis.Args{}.Add("id1").AddFlat(&p1)...); err != nil {
-		panic(err)
+		fmt.Println(err)
+		return
 	}
 
 	m := map[string]string{
@@ -389,18 +398,21 @@ func ExampleArgs() {
 	}
 
 	if _, err := c.Do("HMSET", redis.Args{}.Add("id2").AddFlat(m)...); err != nil {
-		panic(err)
+		fmt.Println(err)
+		return
 	}
 
 	for _, id := range []string{"id1", "id2"} {
 
 		v, err := redis.Values(c.Do("HGETALL", id))
 		if err != nil {
-			panic(err)
+			fmt.Println(err)
+			return
 		}
 
 		if err := redis.ScanStruct(v, &p2); err != nil {
-			panic(err)
+			fmt.Println(err)
+			return
 		}
 
 		fmt.Printf("%+v\n", p2)

+ 1 - 2
redis/script_test.go

@@ -20,7 +20,6 @@ import (
 	"testing"
 	"time"
 
-	"github.com/garyburd/redigo/internal/redistest"
 	"github.com/garyburd/redigo/redis"
 )
 
@@ -35,7 +34,7 @@ func ExampleScript(c redis.Conn, reply interface{}, err error) {
 }
 
 func TestScript(t *testing.T) {
-	c, err := redistest.Dial()
+	c, err := redis.DialDefaultServer()
 	if err != nil {
 		t.Fatalf("error connection to database, %v", err)
 	}

+ 148 - 9
redis/test_test.go

@@ -16,7 +16,17 @@ package redis
 
 import (
 	"bufio"
-	"net"
+	"errors"
+	"flag"
+	"fmt"
+	"io"
+	"io/ioutil"
+	"os"
+	"os/exec"
+	"strconv"
+	"strings"
+	"sync"
+	"testing"
 	"time"
 )
 
@@ -24,15 +34,144 @@ func SetNowFunc(f func() time.Time) {
 	nowFunc = f
 }
 
-type nopCloser struct{ net.Conn }
+var (
+	ErrNegativeInt = errNegativeInt
 
-func (nopCloser) Close() error { return nil }
+	serverPath     = flag.String("redis-server", "redis-server", "Path to redis server binary")
+	serverBasePort = flag.Int("redis-port", 16379, "Beginning of port range for test servers")
+	serverLogName  = flag.String("redis-log", "", "Write Redis server logs to `filename`")
+	serverLog      = ioutil.Discard
+
+	defaultServerMu  sync.Mutex
+	defaultServer    *Server
+	defaultServerErr error
+)
 
-// NewConnBufio is a hook for tests.
-func NewConnBufio(rw bufio.ReadWriter) Conn {
-	return &conn{br: rw.Reader, bw: rw.Writer, conn: nopCloser{}}
+type Server struct {
+	name string
+	cmd  *exec.Cmd
+	done chan struct{}
 }
 
-var (
-	ErrNegativeInt = errNegativeInt
-)
+func NewServer(name string, args ...string) (*Server, error) {
+	s := &Server{
+		name: name,
+		cmd:  exec.Command(*serverPath, args...),
+		done: make(chan struct{}),
+	}
+
+	r, err := s.cmd.StdoutPipe()
+	if err != nil {
+		return nil, err
+	}
+
+	err = s.cmd.Start()
+	if err != nil {
+		return nil, err
+	}
+
+	ready := make(chan error, 1)
+	go s.watch(r, ready)
+
+	select {
+	case err = <-ready:
+	case <-time.After(time.Second * 10):
+		err = errors.New("timeout waiting for server to start")
+	}
+
+	if err != nil {
+		s.Stop()
+		return nil, err
+	}
+
+	return s, nil
+}
+
+func (s *Server) watch(r io.Reader, ready chan error) {
+	fmt.Fprintf(serverLog, "%d START %s \n", s.cmd.Process.Pid, s.name)
+	var listening bool
+	var text string
+	scn := bufio.NewScanner(r)
+	for scn.Scan() {
+		text = scn.Text()
+		fmt.Fprintf(serverLog, "%s\n", text)
+		if !listening {
+			if strings.Contains(text, "The server is now ready to accept connections on port") {
+				listening = true
+				ready <- nil
+			}
+		}
+	}
+	if !listening {
+		ready <- fmt.Errorf("server exited: %s", text)
+	}
+	s.cmd.Wait()
+	fmt.Fprintf(serverLog, "%d STOP %s \n", s.cmd.Process.Pid, s.name)
+	close(s.done)
+}
+
+func (s *Server) Stop() {
+	s.cmd.Process.Signal(os.Interrupt)
+	<-s.done
+}
+
+// stopDefaultServer stops the server created by DialDefaultServer.
+func stopDefaultServer() {
+	defaultServerMu.Lock()
+	defer defaultServerMu.Unlock()
+	if defaultServer != nil {
+		defaultServer.Stop()
+		defaultServer = nil
+	}
+}
+
+// startDefaultServer starts the default server if not already running.
+func startDefaultServer() error {
+	defaultServerMu.Lock()
+	defer defaultServerMu.Unlock()
+	if defaultServer != nil || defaultServerErr != nil {
+		return defaultServerErr
+	}
+	defaultServer, defaultServerErr = NewServer(
+		"default",
+		"--port", strconv.Itoa(*serverBasePort),
+		"--save", "",
+		"--appendonly", "no")
+	return defaultServerErr
+}
+
+// DialDefaultServer starts the test server if not already started and dials a
+// connection to the server.
+func DialDefaultServer() (Conn, error) {
+	if err := startDefaultServer(); err != nil {
+		return nil, err
+	}
+	c, err := Dial("tcp", fmt.Sprintf(":%d", *serverBasePort), DialReadTimeout(1*time.Second), DialWriteTimeout(1*time.Second))
+	if err != nil {
+		return nil, err
+	}
+	c.Do("FLUSHDB")
+	return c, nil
+}
+
+func TestMain(m *testing.M) {
+	os.Exit(func() int {
+		flag.Parse()
+
+		var f *os.File
+		if *serverLogName != "" {
+			var err error
+			f, err = os.OpenFile(*serverLogName, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0600)
+			if err != nil {
+				fmt.Fprintf(os.Stderr, "Error opening redis-log: %v\n", err)
+				return 1
+			}
+			defer f.Close()
+			serverLog = f
+		}
+
+		defer stopDefaultServer()
+
+		return m.Run()
+	}())
+}