diff --git a/.travis.yml b/.travis.yml index 2e2c4ae..4051cc7 100644 --- a/.travis.yml +++ b/.travis.yml @@ -8,8 +8,5 @@ before_install: - sudo apt-get install redis-server; redis-server & - go get github.com/mattn/goveralls script: - - go test -coverprofile=coverage.txt -covermode=atomic ./... + - go test -covermode=atomic ./... - $GOPATH/bin/goveralls -service=travis-ci - -after_success: - - bash <(curl -s https://codecov.io/bash) diff --git a/cluster/cluster.go b/cluster/cluster.go index 0db846a..31fee2a 100644 --- a/cluster/cluster.go +++ b/cluster/cluster.go @@ -70,8 +70,8 @@ func MakeCluster() *Cluster { return cluster } -// args contains all -type CmdFunc func(cluster *Cluster, c redis.Connection, args [][]byte) redis.Reply +// CmdFunc represents the handler of a redis command +type CmdFunc func(cluster *Cluster, c redis.Connection, cmdAndArgs [][]byte) redis.Reply func (cluster *Cluster) Close() { cluster.db.Close() diff --git a/cluster/pubsub.go b/cluster/pubsub.go index 1f6ddc5..f0e6265 100644 --- a/cluster/pubsub.go +++ b/cluster/pubsub.go @@ -19,20 +19,11 @@ var ( // broadcast msg to all peers in cluster when receive publish command from client func Publish(cluster *Cluster, c redis.Connection, args [][]byte) redis.Reply { var count int64 = 0 - for _, peer := range cluster.nodes { - var re redis.Reply - if peer == cluster.self { - args0 := make([][]byte, len(args)) - copy(args0, args) - args0[0] = publishCmd - re = cluster.db.Exec(c, args0) // let local db.hub handle publish - } else { - args[0] = publishRelayCmd - re = cluster.Relay(peer, c, args) - } - if errReply, ok := re.(reply.ErrorReply); ok { + results := cluster.Broadcast(c, args) + for _, val := range results { + if errReply, ok := val.(reply.ErrorReply); ok { logger.Error("publish occurs error: " + errReply.Error()) - } else if intReply, ok := re.(*reply.IntReply); ok { + } else if intReply, ok := val.(*reply.IntReply); ok { count += intReply.Code } } diff --git a/cluster/pubsub_test.go b/cluster/pubsub_test.go new file mode 100644 index 0000000..5bafba3 --- /dev/null +++ b/cluster/pubsub_test.go @@ -0,0 +1,48 @@ +package cluster + +import ( + "github.com/hdt3213/godis/lib/utils" + "github.com/hdt3213/godis/redis/connection" + "github.com/hdt3213/godis/redis/parser" + "github.com/hdt3213/godis/redis/reply/asserts" + "testing" +) + +func TestPublish(t *testing.T) { + channel := utils.RandString(5) + msg := utils.RandString(5) + conn := &connection.FakeConn{} + Subscribe(testCluster, conn, utils.ToBytesList("SUBSCRIBE", channel)) + conn.Clean() // clean subscribe success + Publish(testCluster, conn, utils.ToBytesList("PUBLISH", channel, msg)) + data := conn.Bytes() + ret, err := parser.ParseOne(data) + if err != nil { + t.Error(err) + return + } + asserts.AssertMultiBulkReply(t, ret, []string{ + "message", + channel, + msg, + }) + + // unsubscribe + UnSubscribe(testCluster, conn, utils.ToBytesList("UNSUBSCRIBE", channel)) + conn.Clean() + Publish(testCluster, conn, utils.ToBytesList("PUBLISH", channel, msg)) + data = conn.Bytes() + if len(data) > 0 { + t.Error("expect no msg") + } + + // unsubscribe all + Subscribe(testCluster, conn, utils.ToBytesList("SUBSCRIBE", channel)) + UnSubscribe(testCluster, conn, utils.ToBytesList("UNSUBSCRIBE")) + conn.Clean() + Publish(testCluster, conn, utils.ToBytesList("PUBLISH", channel, msg)) + data = conn.Bytes() + if len(data) > 0 { + t.Error("expect no msg") + } +} diff --git a/db/aof.go b/db/aof.go index 578250d..1bbf698 100644 --- a/db/aof.go +++ b/db/aof.go @@ -82,7 +82,7 @@ func (db *DB) loadAof(maxBytes int) { defer file.Close() reader := utils.NewLimitedReader(file, maxBytes) - ch := parser.Parse(reader) + ch := parser.ParseStream(reader) for p := range ch { if p.Err != nil { if p.Err == io.EOF { diff --git a/db/aof_test.go b/db/aof_test.go index a9e4911..124ad78 100644 --- a/db/aof_test.go +++ b/db/aof_test.go @@ -3,6 +3,8 @@ package db import ( "github.com/hdt3213/godis/config" "github.com/hdt3213/godis/datastruct/utils" + utils2 "github.com/hdt3213/godis/lib/utils" + "github.com/hdt3213/godis/redis/reply" "io/ioutil" "os" "path" @@ -32,31 +34,31 @@ func TestAof(t *testing.T) { for i := 0; i < size; i++ { key := strconv.Itoa(cursor) cursor++ - Set(aofWriteDB, toArgs(key, RandString(8), "EX", "10000")) + Set(aofWriteDB, utils2.ToBytesList(key, utils2.RandString(8), "EX", "10000")) keys = append(keys, key) } for i := 0; i < size; i++ { key := strconv.Itoa(cursor) cursor++ - RPush(aofWriteDB, toArgs(key, RandString(8))) + RPush(aofWriteDB, utils2.ToBytesList(key, utils2.RandString(8))) keys = append(keys, key) } for i := 0; i < size; i++ { key := strconv.Itoa(cursor) cursor++ - HSet(aofWriteDB, toArgs(key, RandString(8), RandString(8))) + HSet(aofWriteDB, utils2.ToBytesList(key, utils2.RandString(8), utils2.RandString(8))) keys = append(keys, key) } for i := 0; i < size; i++ { key := strconv.Itoa(cursor) cursor++ - SAdd(aofWriteDB, toArgs(key, RandString(8))) + SAdd(aofWriteDB, utils2.ToBytesList(key, utils2.RandString(8))) keys = append(keys, key) } for i := 0; i < size; i++ { key := strconv.Itoa(cursor) cursor++ - ZAdd(aofWriteDB, toArgs(key, "10", RandString(8))) + ZAdd(aofWriteDB, utils2.ToBytesList(key, "10", utils2.RandString(8))) keys = append(keys, key) } aofWriteDB.Close() // wait for aof finished @@ -98,41 +100,49 @@ func TestRewriteAOF(t *testing.T) { aofWriteDB := MakeDB() size := 1 keys := make([]string, 0) + ttlKeys := make([]string, 0) cursor := 0 for i := 0; i < size; i++ { key := "str" + strconv.Itoa(cursor) cursor++ - Set(aofWriteDB, toArgs(key, RandString(8))) - Set(aofWriteDB, toArgs(key, RandString(8))) + Set(aofWriteDB, utils2.ToBytesList(key, utils2.RandString(8))) + Set(aofWriteDB, utils2.ToBytesList(key, utils2.RandString(8))) keys = append(keys, key) } + // test ttl + for i := 0; i < size; i++ { + key := "str" + strconv.Itoa(cursor) + cursor++ + Set(aofWriteDB, utils2.ToBytesList(key, utils2.RandString(8), "EX", "1000")) + ttlKeys = append(ttlKeys, key) + } for i := 0; i < size; i++ { key := "list" + strconv.Itoa(cursor) cursor++ - RPush(aofWriteDB, toArgs(key, RandString(8))) - RPush(aofWriteDB, toArgs(key, RandString(8))) + RPush(aofWriteDB, utils2.ToBytesList(key, utils2.RandString(8))) + RPush(aofWriteDB, utils2.ToBytesList(key, utils2.RandString(8))) keys = append(keys, key) } for i := 0; i < size; i++ { key := "hash" + strconv.Itoa(cursor) cursor++ - field := RandString(8) - HSet(aofWriteDB, toArgs(key, field, RandString(8))) - HSet(aofWriteDB, toArgs(key, field, RandString(8))) + field := utils2.RandString(8) + HSet(aofWriteDB, utils2.ToBytesList(key, field, utils2.RandString(8))) + HSet(aofWriteDB, utils2.ToBytesList(key, field, utils2.RandString(8))) keys = append(keys, key) } for i := 0; i < size; i++ { key := "set" + strconv.Itoa(cursor) cursor++ - member := RandString(8) - SAdd(aofWriteDB, toArgs(key, member)) - SAdd(aofWriteDB, toArgs(key, member)) + member := utils2.RandString(8) + SAdd(aofWriteDB, utils2.ToBytesList(key, member)) + SAdd(aofWriteDB, utils2.ToBytesList(key, member)) keys = append(keys, key) } for i := 0; i < size; i++ { key := "zset" + strconv.Itoa(cursor) cursor++ - ZAdd(aofWriteDB, toArgs(key, "10", RandString(8))) + ZAdd(aofWriteDB, utils2.ToBytesList(key, "10", utils2.RandString(8))) keys = append(keys, key) } time.Sleep(time.Second) // wait for async goroutine finish its job @@ -156,5 +166,16 @@ func TestRewriteAOF(t *testing.T) { t.Errorf("wrong value of key: %s", key) } } + for _, key := range ttlKeys { + ret := TTL(aofReadDB, utils2.ToBytesList(key)) + intResult, ok := ret.(*reply.IntReply) + if !ok { + t.Errorf("expected int reply, actually %s", ret.ToBytes()) + return + } + if intResult.Code <= 0 { + t.Errorf("expect a positive integer, actual: %d", intResult.Code) + } + } aofReadDB.Close() } diff --git a/db/geo_test.go b/db/geo_test.go index 1c542b9..3725960 100644 --- a/db/geo_test.go +++ b/db/geo_test.go @@ -2,6 +2,7 @@ package db import ( "fmt" + "github.com/hdt3213/godis/lib/utils" "github.com/hdt3213/godis/redis/reply" "github.com/hdt3213/godis/redis/reply/asserts" "strconv" @@ -9,52 +10,52 @@ import ( ) func TestGeoHash(t *testing.T) { - FlushDB(testDB, toArgs()) - key := RandString(10) - pos := RandString(10) - result := GeoAdd(testDB, toArgs(key, "13.361389", "38.115556", pos)) + FlushDB(testDB, utils.ToBytesList()) + key := utils.RandString(10) + pos := utils.RandString(10) + result := GeoAdd(testDB, utils.ToBytesList(key, "13.361389", "38.115556", pos)) asserts.AssertIntReply(t, result, 1) - result = GeoHash(testDB, toArgs(key, pos)) + result = GeoHash(testDB, utils.ToBytesList(key, pos)) asserts.AssertMultiBulkReply(t, result, []string{"sqc8b49rnys00"}) } func TestGeoRadius(t *testing.T) { - FlushDB(testDB, toArgs()) - key := RandString(10) - pos1 := RandString(10) - pos2 := RandString(10) - GeoAdd(testDB, toArgs(key, + FlushDB(testDB, utils.ToBytesList()) + key := utils.RandString(10) + pos1 := utils.RandString(10) + pos2 := utils.RandString(10) + GeoAdd(testDB, utils.ToBytesList(key, "13.361389", "38.115556", pos1, "15.087269", "37.502669", pos2, )) - result := GeoRadius(testDB, toArgs(key, "15", "37", "200", "km")) + result := GeoRadius(testDB, utils.ToBytesList(key, "15", "37", "200", "km")) asserts.AssertMultiBulkReplySize(t, result, 2) } func TestGeoRadiusByMember(t *testing.T) { - FlushDB(testDB, toArgs()) - key := RandString(10) - pos1 := RandString(10) - pos2 := RandString(10) - pivot := RandString(10) - GeoAdd(testDB, toArgs(key, + FlushDB(testDB, utils.ToBytesList()) + key := utils.RandString(10) + pos1 := utils.RandString(10) + pos2 := utils.RandString(10) + pivot := utils.RandString(10) + GeoAdd(testDB, utils.ToBytesList(key, "13.361389", "38.115556", pos1, "17.087269", "38.502669", pos2, "13.583333", "37.316667", pivot, )) - result := GeoRadiusByMember(testDB, toArgs(key, pivot, "100", "km")) + result := GeoRadiusByMember(testDB, utils.ToBytesList(key, pivot, "100", "km")) asserts.AssertMultiBulkReplySize(t, result, 2) } func TestGeoPos(t *testing.T) { - FlushDB(testDB, toArgs()) - key := RandString(10) - pos1 := RandString(10) - pos2 := RandString(10) - GeoAdd(testDB, toArgs(key, + FlushDB(testDB, utils.ToBytesList()) + key := utils.RandString(10) + pos1 := utils.RandString(10) + pos2 := utils.RandString(10) + GeoAdd(testDB, utils.ToBytesList(key, "13.361389", "38.115556", pos1, )) - result := GeoPos(testDB, toArgs(key, pos1, pos2)) + result := GeoPos(testDB, utils.ToBytesList(key, pos1, pos2)) expected := "*2\r\n*2\r\n$18\r\n13.361386698670685\r\n$17\r\n38.11555536696687\r\n*0\r\n" if string(result.ToBytes()) != expected { t.Error("test failed") @@ -62,15 +63,15 @@ func TestGeoPos(t *testing.T) { } func TestGeoDist(t *testing.T) { - FlushDB(testDB, toArgs()) - key := RandString(10) - pos1 := RandString(10) - pos2 := RandString(10) - GeoAdd(testDB, toArgs(key, + FlushDB(testDB, utils.ToBytesList()) + key := utils.RandString(10) + pos1 := utils.RandString(10) + pos2 := utils.RandString(10) + GeoAdd(testDB, utils.ToBytesList(key, "13.361389", "38.115556", pos1, "15.087269", "37.502669", pos2, )) - result := GeoDist(testDB, toArgs(key, pos1, pos2, "km")) + result := GeoDist(testDB, utils.ToBytesList(key, pos1, pos2, "km")) bulkReply, ok := result.(*reply.BulkReply) if !ok { t.Error(fmt.Sprintf("expected bulk reply, actually %s", result.ToBytes())) diff --git a/db/hash_test.go b/db/hash_test.go index aef2dfd..a1c6085 100644 --- a/db/hash_test.go +++ b/db/hash_test.go @@ -3,6 +3,7 @@ package db import ( "fmt" "github.com/hdt3213/godis/datastruct/utils" + utils2 "github.com/hdt3213/godis/lib/utils" "github.com/hdt3213/godis/redis/reply" "github.com/hdt3213/godis/redis/reply/asserts" "strconv" @@ -14,13 +15,13 @@ func TestHSet(t *testing.T) { size := 100 // test hset - key := RandString(10) + key := utils2.RandString(10) values := make(map[string][]byte, size) for i := 0; i < size; i++ { - value := RandString(10) + value := utils2.RandString(10) field := strconv.Itoa(i) values[field] = []byte(value) - result := HSet(testDB, toArgs(key, field, value)) + result := HSet(testDB, utils2.ToBytesList(key, field, value)) if intResult, _ := result.(*reply.IntReply); intResult.Code != int64(1) { t.Error(fmt.Sprintf("expected %d, actually %d", 1, intResult.Code)) } @@ -28,19 +29,19 @@ func TestHSet(t *testing.T) { // test hget and hexists for field, v := range values { - actual := HGet(testDB, toArgs(key, field)) + actual := HGet(testDB, utils2.ToBytesList(key, field)) expected := reply.MakeBulkReply(v) if !utils.BytesEquals(actual.ToBytes(), expected.ToBytes()) { t.Error(fmt.Sprintf("expected %s, actually %s", string(expected.ToBytes()), string(actual.ToBytes()))) } - actual = HExists(testDB, toArgs(key, field)) + actual = HExists(testDB, utils2.ToBytesList(key, field)) if intResult, _ := actual.(*reply.IntReply); intResult.Code != int64(1) { t.Error(fmt.Sprintf("expected %d, actually %d", 1, intResult.Code)) } } // test hlen - actual := HLen(testDB, toArgs(key)) + actual := HLen(testDB, utils2.ToBytesList(key)) if intResult, _ := actual.(*reply.IntReply); intResult.Code != int64(len(values)) { t.Error(fmt.Sprintf("expected %d, actually %d", len(values), intResult.Code)) } @@ -51,24 +52,24 @@ func TestHDel(t *testing.T) { size := 100 // set values - key := RandString(10) + key := utils2.RandString(10) fields := make([]string, size) for i := 0; i < size; i++ { - value := RandString(10) + value := utils2.RandString(10) field := strconv.Itoa(i) fields[i] = field - HSet(testDB, toArgs(key, field, value)) + HSet(testDB, utils2.ToBytesList(key, field, value)) } // test HDel args := []string{key} args = append(args, fields...) - actual := HDel(testDB, toArgs(args...)) + actual := HDel(testDB, utils2.ToBytesList(args...)) if intResult, _ := actual.(*reply.IntReply); intResult.Code != int64(len(fields)) { t.Error(fmt.Sprintf("expected %d, actually %d", len(fields), intResult.Code)) } - actual = HLen(testDB, toArgs(key)) + actual = HLen(testDB, utils2.ToBytesList(key)) if intResult, _ := actual.(*reply.IntReply); intResult.Code != int64(0) { t.Error(fmt.Sprintf("expected %d, actually %d", 0, intResult.Code)) } @@ -79,16 +80,16 @@ func TestHMSet(t *testing.T) { size := 100 // test hset - key := RandString(10) + key := utils2.RandString(10) fields := make([]string, size) values := make([]string, size) setArgs := []string{key} for i := 0; i < size; i++ { - fields[i] = RandString(10) - values[i] = RandString(10) + fields[i] = utils2.RandString(10) + values[i] = utils2.RandString(10) setArgs = append(setArgs, fields[i], values[i]) } - result := HMSet(testDB, toArgs(setArgs...)) + result := HMSet(testDB, utils2.ToBytesList(setArgs...)) if _, ok := result.(*reply.OkReply); !ok { t.Error(fmt.Sprintf("expected ok, actually %s", string(result.ToBytes()))) } @@ -96,8 +97,8 @@ func TestHMSet(t *testing.T) { // test HMGet getArgs := []string{key} getArgs = append(getArgs, fields...) - actual := HMGet(testDB, toArgs(getArgs...)) - expected := reply.MakeMultiBulkReply(toArgs(values...)) + actual := HMGet(testDB, utils2.ToBytesList(getArgs...)) + expected := reply.MakeMultiBulkReply(utils2.ToBytesList(values...)) if !utils.BytesEquals(actual.ToBytes(), expected.ToBytes()) { t.Error(fmt.Sprintf("expected %s, actually %s", string(expected.ToBytes()), string(actual.ToBytes()))) } @@ -106,22 +107,22 @@ func TestHMSet(t *testing.T) { func TestHGetAll(t *testing.T) { FlushAll(testDB, [][]byte{}) size := 100 - key := RandString(10) + key := utils2.RandString(10) fields := make([]string, size) valueSet := make(map[string]bool, size) valueMap := make(map[string]string) all := make([]string, 0) for i := 0; i < size; i++ { - fields[i] = RandString(10) - value := RandString(10) + fields[i] = utils2.RandString(10) + value := utils2.RandString(10) all = append(all, fields[i], value) valueMap[fields[i]] = value valueSet[value] = true - HSet(testDB, toArgs(key, fields[i], value)) + HSet(testDB, utils2.ToBytesList(key, fields[i], value)) } // test HGetAll - result := HGetAll(testDB, toArgs(key)) + result := HGetAll(testDB, utils2.ToBytesList(key)) multiBulk, ok := result.(*reply.MultiBulkReply) if !ok { t.Error(fmt.Sprintf("expected MultiBulkReply, actually %s", string(result.ToBytes()))) @@ -143,7 +144,7 @@ func TestHGetAll(t *testing.T) { } // test HKeys - result = HKeys(testDB, toArgs(key)) + result = HKeys(testDB, utils2.ToBytesList(key)) multiBulk, ok = result.(*reply.MultiBulkReply) if !ok { t.Error(fmt.Sprintf("expected MultiBulkReply, actually %s", string(result.ToBytes()))) @@ -159,7 +160,7 @@ func TestHGetAll(t *testing.T) { } // test HVals - result = HVals(testDB, toArgs(key)) + result = HVals(testDB, utils2.ToBytesList(key)) multiBulk, ok = result.(*reply.MultiBulkReply) if !ok { t.Error(fmt.Sprintf("expected MultiBulkReply, actually %s", string(result.ToBytes()))) @@ -179,21 +180,21 @@ func TestHGetAll(t *testing.T) { func TestHIncrBy(t *testing.T) { FlushAll(testDB, [][]byte{}) - key := RandString(10) - result := HIncrBy(testDB, toArgs(key, "a", "1")) + key := utils2.RandString(10) + result := HIncrBy(testDB, utils2.ToBytesList(key, "a", "1")) if bulkResult, _ := result.(*reply.BulkReply); string(bulkResult.Arg) != "1" { t.Error(fmt.Sprintf("expected %s, actually %s", "1", string(bulkResult.Arg))) } - result = HIncrBy(testDB, toArgs(key, "a", "1")) + result = HIncrBy(testDB, utils2.ToBytesList(key, "a", "1")) if bulkResult, _ := result.(*reply.BulkReply); string(bulkResult.Arg) != "2" { t.Error(fmt.Sprintf("expected %s, actually %s", "2", string(bulkResult.Arg))) } - result = HIncrByFloat(testDB, toArgs(key, "b", "1.2")) + result = HIncrByFloat(testDB, utils2.ToBytesList(key, "b", "1.2")) if bulkResult, _ := result.(*reply.BulkReply); string(bulkResult.Arg) != "1.2" { t.Error(fmt.Sprintf("expected %s, actually %s", "1.2", string(bulkResult.Arg))) } - result = HIncrByFloat(testDB, toArgs(key, "b", "1.2")) + result = HIncrByFloat(testDB, utils2.ToBytesList(key, "b", "1.2")) if bulkResult, _ := result.(*reply.BulkReply); string(bulkResult.Arg) != "2.4" { t.Error(fmt.Sprintf("expected %s, actually %s", "2.4", string(bulkResult.Arg))) } @@ -201,15 +202,15 @@ func TestHIncrBy(t *testing.T) { func TestHSetNX(t *testing.T) { FlushAll(testDB, [][]byte{}) - key := RandString(10) - field := RandString(10) - value := RandString(10) - result := HSetNX(testDB, toArgs(key, field, value)) + key := utils2.RandString(10) + field := utils2.RandString(10) + value := utils2.RandString(10) + result := HSetNX(testDB, utils2.ToBytesList(key, field, value)) asserts.AssertIntReply(t, result, 1) - value2 := RandString(10) - result = HSetNX(testDB, toArgs(key, field, value2)) + value2 := utils2.RandString(10) + result = HSetNX(testDB, utils2.ToBytesList(key, field, value2)) asserts.AssertIntReply(t, result, 0) - result = HGet(testDB, toArgs(key, field)) + result = HGet(testDB, utils2.ToBytesList(key, field)) asserts.AssertBulkReply(t, result, value) } diff --git a/db/keys_test.go b/db/keys_test.go index 2e47201..e0fffb8 100644 --- a/db/keys_test.go +++ b/db/keys_test.go @@ -2,6 +2,7 @@ package db import ( "fmt" + "github.com/hdt3213/godis/lib/utils" "github.com/hdt3213/godis/redis/reply" "github.com/hdt3213/godis/redis/reply/asserts" "strconv" @@ -11,64 +12,64 @@ import ( func TestExists(t *testing.T) { FlushAll(testDB, [][]byte{}) - key := RandString(10) - value := RandString(10) - Set(testDB, toArgs(key, value)) - result := Exists(testDB, toArgs(key)) + key := utils.RandString(10) + value := utils.RandString(10) + Set(testDB, utils.ToBytesList(key, value)) + result := Exists(testDB, utils.ToBytesList(key)) asserts.AssertIntReply(t, result, 1) - key = RandString(10) - result = Exists(testDB, toArgs(key)) + key = utils.RandString(10) + result = Exists(testDB, utils.ToBytesList(key)) asserts.AssertIntReply(t, result, 0) } func TestType(t *testing.T) { FlushAll(testDB, [][]byte{}) - key := RandString(10) - value := RandString(10) - Set(testDB, toArgs(key, value)) - result := Type(testDB, toArgs(key)) + key := utils.RandString(10) + value := utils.RandString(10) + Set(testDB, utils.ToBytesList(key, value)) + result := Type(testDB, utils.ToBytesList(key)) asserts.AssertStatusReply(t, result, "string") - Del(testDB, toArgs(key)) - result = Type(testDB, toArgs(key)) + Del(testDB, utils.ToBytesList(key)) + result = Type(testDB, utils.ToBytesList(key)) asserts.AssertStatusReply(t, result, "none") - RPush(testDB, toArgs(key, value)) - result = Type(testDB, toArgs(key)) + RPush(testDB, utils.ToBytesList(key, value)) + result = Type(testDB, utils.ToBytesList(key)) asserts.AssertStatusReply(t, result, "list") - Del(testDB, toArgs(key)) - HSet(testDB, toArgs(key, key, value)) - result = Type(testDB, toArgs(key)) + Del(testDB, utils.ToBytesList(key)) + HSet(testDB, utils.ToBytesList(key, key, value)) + result = Type(testDB, utils.ToBytesList(key)) asserts.AssertStatusReply(t, result, "hash") - Del(testDB, toArgs(key)) - SAdd(testDB, toArgs(key, value)) - result = Type(testDB, toArgs(key)) + Del(testDB, utils.ToBytesList(key)) + SAdd(testDB, utils.ToBytesList(key, value)) + result = Type(testDB, utils.ToBytesList(key)) asserts.AssertStatusReply(t, result, "set") - Del(testDB, toArgs(key)) - ZAdd(testDB, toArgs(key, "1", value)) - result = Type(testDB, toArgs(key)) + Del(testDB, utils.ToBytesList(key)) + ZAdd(testDB, utils.ToBytesList(key, "1", value)) + result = Type(testDB, utils.ToBytesList(key)) asserts.AssertStatusReply(t, result, "zset") } func TestRename(t *testing.T) { FlushAll(testDB, [][]byte{}) - key := RandString(10) - value := RandString(10) - newKey := key + RandString(2) - Set(testDB, toArgs(key, value, "ex", "1000")) - result := Rename(testDB, toArgs(key, newKey)) + key := utils.RandString(10) + value := utils.RandString(10) + newKey := key + utils.RandString(2) + Set(testDB, utils.ToBytesList(key, value, "ex", "1000")) + result := Rename(testDB, utils.ToBytesList(key, newKey)) if _, ok := result.(*reply.OkReply); !ok { t.Error("expect ok") return } - result = Exists(testDB, toArgs(key)) + result = Exists(testDB, utils.ToBytesList(key)) asserts.AssertIntReply(t, result, 0) - result = Exists(testDB, toArgs(newKey)) + result = Exists(testDB, utils.ToBytesList(newKey)) asserts.AssertIntReply(t, result, 1) // check ttl - result = TTL(testDB, toArgs(newKey)) + result = TTL(testDB, utils.ToBytesList(newKey)) intResult, ok := result.(*reply.IntReply) if !ok { t.Error(fmt.Sprintf("expected int reply, actually %s", result.ToBytes())) @@ -82,17 +83,17 @@ func TestRename(t *testing.T) { func TestRenameNx(t *testing.T) { FlushAll(testDB, [][]byte{}) - key := RandString(10) - value := RandString(10) - newKey := key + RandString(2) - Set(testDB, toArgs(key, value, "ex", "1000")) - result := RenameNx(testDB, toArgs(key, newKey)) + key := utils.RandString(10) + value := utils.RandString(10) + newKey := key + utils.RandString(2) + Set(testDB, utils.ToBytesList(key, value, "ex", "1000")) + result := RenameNx(testDB, utils.ToBytesList(key, newKey)) asserts.AssertIntReply(t, result, 1) - result = Exists(testDB, toArgs(key)) + result = Exists(testDB, utils.ToBytesList(key)) asserts.AssertIntReply(t, result, 0) - result = Exists(testDB, toArgs(newKey)) + result = Exists(testDB, utils.ToBytesList(newKey)) asserts.AssertIntReply(t, result, 1) - result = TTL(testDB, toArgs(newKey)) + result = TTL(testDB, utils.ToBytesList(newKey)) intResult, ok := result.(*reply.IntReply) if !ok { t.Error(fmt.Sprintf("expected int reply, actually %s", result.ToBytes())) @@ -106,13 +107,13 @@ func TestRenameNx(t *testing.T) { func TestTTL(t *testing.T) { FlushAll(testDB, [][]byte{}) - key := RandString(10) - value := RandString(10) - Set(testDB, toArgs(key, value)) + key := utils.RandString(10) + value := utils.RandString(10) + Set(testDB, utils.ToBytesList(key, value)) - result := Expire(testDB, toArgs(key, "1000")) + result := Expire(testDB, utils.ToBytesList(key, "1000")) asserts.AssertIntReply(t, result, 1) - result = TTL(testDB, toArgs(key)) + result = TTL(testDB, utils.ToBytesList(key)) intResult, ok := result.(*reply.IntReply) if !ok { t.Error(fmt.Sprintf("expected int reply, actually %s", result.ToBytes())) @@ -123,14 +124,14 @@ func TestTTL(t *testing.T) { return } - result = Persist(testDB, toArgs(key)) + result = Persist(testDB, utils.ToBytesList(key)) asserts.AssertIntReply(t, result, 1) - result = TTL(testDB, toArgs(key)) + result = TTL(testDB, utils.ToBytesList(key)) asserts.AssertIntReply(t, result, -1) - result = PExpire(testDB, toArgs(key, "1000000")) + result = PExpire(testDB, utils.ToBytesList(key, "1000000")) asserts.AssertIntReply(t, result, 1) - result = PTTL(testDB, toArgs(key)) + result = PTTL(testDB, utils.ToBytesList(key)) intResult, ok = result.(*reply.IntReply) if !ok { t.Error(fmt.Sprintf("expected int reply, actually %s", result.ToBytes())) @@ -144,14 +145,14 @@ func TestTTL(t *testing.T) { func TestExpireAt(t *testing.T) { FlushAll(testDB, [][]byte{}) - key := RandString(10) - value := RandString(10) - Set(testDB, toArgs(key, value)) + key := utils.RandString(10) + value := utils.RandString(10) + Set(testDB, utils.ToBytesList(key, value)) expireAt := time.Now().Add(time.Minute).Unix() - result := ExpireAt(testDB, toArgs(key, strconv.FormatInt(expireAt, 10))) + result := ExpireAt(testDB, utils.ToBytesList(key, strconv.FormatInt(expireAt, 10))) asserts.AssertIntReply(t, result, 1) - result = TTL(testDB, toArgs(key)) + result = TTL(testDB, utils.ToBytesList(key)) intResult, ok := result.(*reply.IntReply) if !ok { t.Error(fmt.Sprintf("expected int reply, actually %s", result.ToBytes())) @@ -163,9 +164,9 @@ func TestExpireAt(t *testing.T) { } expireAt = time.Now().Add(time.Minute).Unix() - result = PExpireAt(testDB, toArgs(key, strconv.FormatInt(expireAt*1000, 10))) + result = PExpireAt(testDB, utils.ToBytesList(key, strconv.FormatInt(expireAt*1000, 10))) asserts.AssertIntReply(t, result, 1) - result = TTL(testDB, toArgs(key)) + result = TTL(testDB, utils.ToBytesList(key)) intResult, ok = result.(*reply.IntReply) if !ok { t.Error(fmt.Sprintf("expected int reply, actually %s", result.ToBytes())) @@ -179,16 +180,16 @@ func TestExpireAt(t *testing.T) { func TestKeys(t *testing.T) { FlushAll(testDB, [][]byte{}) - key := RandString(10) - value := RandString(10) - Set(testDB, toArgs(key, value)) - Set(testDB, toArgs("a:"+key, value)) - Set(testDB, toArgs("b:"+key, value)) + key := utils.RandString(10) + value := utils.RandString(10) + Set(testDB, utils.ToBytesList(key, value)) + Set(testDB, utils.ToBytesList("a:"+key, value)) + Set(testDB, utils.ToBytesList("b:"+key, value)) - result := Keys(testDB, toArgs("*")) + result := Keys(testDB, utils.ToBytesList("*")) asserts.AssertMultiBulkReplySize(t, result, 3) - result = Keys(testDB, toArgs("a:*")) + result = Keys(testDB, utils.ToBytesList("a:*")) asserts.AssertMultiBulkReplySize(t, result, 1) - result = Keys(testDB, toArgs("?:*")) + result = Keys(testDB, utils.ToBytesList("?:*")) asserts.AssertMultiBulkReplySize(t, result, 2) } diff --git a/db/list_test.go b/db/list_test.go index a278101..74b964a 100644 --- a/db/list_test.go +++ b/db/list_test.go @@ -3,6 +3,7 @@ package db import ( "fmt" "github.com/hdt3213/godis/datastruct/utils" + utils2 "github.com/hdt3213/godis/lib/utils" "github.com/hdt3213/godis/redis/reply" "strconv" "testing" @@ -13,67 +14,67 @@ func TestPush(t *testing.T) { size := 100 // rpush single - key := RandString(10) + key := utils2.RandString(10) values := make([][]byte, size) for i := 0; i < size; i++ { - value := RandString(10) + value := utils2.RandString(10) values[i] = []byte(value) - result := RPush(testDB, toArgs(key, value)) + result := RPush(testDB, utils2.ToBytesList(key, value)) if intResult, _ := result.(*reply.IntReply); intResult.Code != int64(i+1) { t.Error(fmt.Sprintf("expected %d, actually %d", i+1, intResult.Code)) } } - actual := LRange(testDB, toArgs(key, "0", "-1")) + actual := LRange(testDB, utils2.ToBytesList(key, "0", "-1")) expected := reply.MakeMultiBulkReply(values) if !utils.BytesEquals(actual.ToBytes(), expected.ToBytes()) { t.Error("push error") } - Del(testDB, toArgs(key)) + Del(testDB, utils2.ToBytesList(key)) // rpush multi - key = RandString(10) + key = utils2.RandString(10) values = make([][]byte, size+1) values[0] = []byte(key) for i := 0; i < size; i++ { - value := RandString(10) + value := utils2.RandString(10) values[i+1] = []byte(value) } result := RPush(testDB, values) if intResult, _ := result.(*reply.IntReply); intResult.Code != int64(size) { t.Error(fmt.Sprintf("expected %d, actually %d", size, intResult.Code)) } - actual = LRange(testDB, toArgs(key, "0", "-1")) + actual = LRange(testDB, utils2.ToBytesList(key, "0", "-1")) expected = reply.MakeMultiBulkReply(values[1:]) if !utils.BytesEquals(actual.ToBytes(), expected.ToBytes()) { t.Error("push error") } - Del(testDB, toArgs(key)) + Del(testDB, utils2.ToBytesList(key)) // left push single - key = RandString(10) + key = utils2.RandString(10) values = make([][]byte, size) for i := 0; i < size; i++ { - value := RandString(10) + value := utils2.RandString(10) values[size-i-1] = []byte(value) - result = LPush(testDB, toArgs(key, value)) + result = LPush(testDB, utils2.ToBytesList(key, value)) if intResult, _ := result.(*reply.IntReply); intResult.Code != int64(i+1) { t.Error(fmt.Sprintf("expected %d, actually %d", i+1, intResult.Code)) } } - actual = LRange(testDB, toArgs(key, "0", "-1")) + actual = LRange(testDB, utils2.ToBytesList(key, "0", "-1")) expected = reply.MakeMultiBulkReply(values) if !utils.BytesEquals(actual.ToBytes(), expected.ToBytes()) { t.Error("push error") } - Del(testDB, toArgs(key)) + Del(testDB, utils2.ToBytesList(key)) // left push multi - key = RandString(10) + key = utils2.RandString(10) values = make([][]byte, size+1) values[0] = []byte(key) expectedValues := make([][]byte, size) for i := 0; i < size; i++ { - value := RandString(10) + value := utils2.RandString(10) values[i+1] = []byte(value) expectedValues[size-i-1] = []byte(value) } @@ -81,29 +82,29 @@ func TestPush(t *testing.T) { if intResult, _ := result.(*reply.IntReply); intResult.Code != int64(size) { t.Error(fmt.Sprintf("expected %d, actually %d", size, intResult.Code)) } - actual = LRange(testDB, toArgs(key, "0", "-1")) + actual = LRange(testDB, utils2.ToBytesList(key, "0", "-1")) expected = reply.MakeMultiBulkReply(expectedValues) if !utils.BytesEquals(actual.ToBytes(), expected.ToBytes()) { t.Error("push error") } - Del(testDB, toArgs(key)) + Del(testDB, utils2.ToBytesList(key)) } func TestLRange(t *testing.T) { // prepare list FlushAll(testDB, [][]byte{}) size := 100 - key := RandString(10) + key := utils2.RandString(10) values := make([][]byte, size) for i := 0; i < size; i++ { - value := RandString(10) - RPush(testDB, toArgs(key, value)) + value := utils2.RandString(10) + RPush(testDB, utils2.ToBytesList(key, value)) values[i] = []byte(value) } start := "0" end := "9" - actual := LRange(testDB, toArgs(key, start, end)) + actual := LRange(testDB, utils2.ToBytesList(key, start, end)) expected := reply.MakeMultiBulkReply(values[0:10]) if !utils.BytesEquals(actual.ToBytes(), expected.ToBytes()) { t.Error(fmt.Sprintf("range error [%s, %s]", start, end)) @@ -111,7 +112,7 @@ func TestLRange(t *testing.T) { start = "0" end = "200" - actual = LRange(testDB, toArgs(key, start, end)) + actual = LRange(testDB, utils2.ToBytesList(key, start, end)) expected = reply.MakeMultiBulkReply(values) if !utils.BytesEquals(actual.ToBytes(), expected.ToBytes()) { t.Error(fmt.Sprintf("range error [%s, %s]", start, end)) @@ -119,7 +120,7 @@ func TestLRange(t *testing.T) { start = "0" end = "-10" - actual = LRange(testDB, toArgs(key, start, end)) + actual = LRange(testDB, utils2.ToBytesList(key, start, end)) expected = reply.MakeMultiBulkReply(values[0 : size-10+1]) if !utils.BytesEquals(actual.ToBytes(), expected.ToBytes()) { t.Error(fmt.Sprintf("range error [%s, %s]", start, end)) @@ -127,7 +128,7 @@ func TestLRange(t *testing.T) { start = "0" end = "-200" - actual = LRange(testDB, toArgs(key, start, end)) + actual = LRange(testDB, utils2.ToBytesList(key, start, end)) expected = reply.MakeMultiBulkReply(values[0:0]) if !utils.BytesEquals(actual.ToBytes(), expected.ToBytes()) { t.Error(fmt.Sprintf("range error [%s, %s]", start, end)) @@ -135,7 +136,7 @@ func TestLRange(t *testing.T) { start = "-10" end = "-1" - actual = LRange(testDB, toArgs(key, start, end)) + actual = LRange(testDB, utils2.ToBytesList(key, start, end)) expected = reply.MakeMultiBulkReply(values[90:]) if !utils.BytesEquals(actual.ToBytes(), expected.ToBytes()) { t.Error(fmt.Sprintf("range error [%s, %s]", start, end)) @@ -146,21 +147,21 @@ func TestLIndex(t *testing.T) { // prepare list FlushAll(testDB, [][]byte{}) size := 100 - key := RandString(10) + key := utils2.RandString(10) values := make([][]byte, size) for i := 0; i < size; i++ { - value := RandString(10) - RPush(testDB, toArgs(key, value)) + value := utils2.RandString(10) + RPush(testDB, utils2.ToBytesList(key, value)) values[i] = []byte(value) } - result := LLen(testDB, toArgs(key)) + result := LLen(testDB, utils2.ToBytesList(key)) if intResult, _ := result.(*reply.IntReply); intResult.Code != int64(size) { t.Error(fmt.Sprintf("expected %d, actually %d", size, intResult.Code)) } for i := 0; i < size; i++ { - result = LIndex(testDB, toArgs(key, strconv.Itoa(i))) + result = LIndex(testDB, utils2.ToBytesList(key, strconv.Itoa(i))) expected := reply.MakeBulkReply(values[i]) if !utils.BytesEquals(result.ToBytes(), expected.ToBytes()) { t.Error(fmt.Sprintf("expected %s, actually %s", string(expected.ToBytes()), string(result.ToBytes()))) @@ -168,7 +169,7 @@ func TestLIndex(t *testing.T) { } for i := 1; i <= size; i++ { - result = LIndex(testDB, toArgs(key, strconv.Itoa(-i))) + result = LIndex(testDB, utils2.ToBytesList(key, strconv.Itoa(-i))) expected := reply.MakeBulkReply(values[size-i]) if !utils.BytesEquals(result.ToBytes(), expected.ToBytes()) { t.Error(fmt.Sprintf("expected %s, actually %s", string(expected.ToBytes()), string(result.ToBytes()))) @@ -179,33 +180,33 @@ func TestLIndex(t *testing.T) { func TestLRem(t *testing.T) { // prepare list FlushAll(testDB, [][]byte{}) - key := RandString(10) + key := utils2.RandString(10) values := []string{key, "a", "b", "a", "a", "c", "a", "a"} - RPush(testDB, toArgs(values...)) + RPush(testDB, utils2.ToBytesList(values...)) - result := LRem(testDB, toArgs(key, "1", "a")) + result := LRem(testDB, utils2.ToBytesList(key, "1", "a")) if intResult, _ := result.(*reply.IntReply); intResult.Code != 1 { t.Error(fmt.Sprintf("expected %d, actually %d", 1, intResult.Code)) } - result = LLen(testDB, toArgs(key)) + result = LLen(testDB, utils2.ToBytesList(key)) if intResult, _ := result.(*reply.IntReply); intResult.Code != 6 { t.Error(fmt.Sprintf("expected %d, actually %d", 6, intResult.Code)) } - result = LRem(testDB, toArgs(key, "-2", "a")) + result = LRem(testDB, utils2.ToBytesList(key, "-2", "a")) if intResult, _ := result.(*reply.IntReply); intResult.Code != 2 { t.Error(fmt.Sprintf("expected %d, actually %d", 2, intResult.Code)) } - result = LLen(testDB, toArgs(key)) + result = LLen(testDB, utils2.ToBytesList(key)) if intResult, _ := result.(*reply.IntReply); intResult.Code != 4 { t.Error(fmt.Sprintf("expected %d, actually %d", 4, intResult.Code)) } - result = LRem(testDB, toArgs(key, "0", "a")) + result = LRem(testDB, utils2.ToBytesList(key, "0", "a")) if intResult, _ := result.(*reply.IntReply); intResult.Code != 2 { t.Error(fmt.Sprintf("expected %d, actually %d", 2, intResult.Code)) } - result = LLen(testDB, toArgs(key)) + result = LLen(testDB, utils2.ToBytesList(key)) if intResult, _ := result.(*reply.IntReply); intResult.Code != 2 { t.Error(fmt.Sprintf("expected %d, actually %d", 2, intResult.Code)) } @@ -213,20 +214,20 @@ func TestLRem(t *testing.T) { func TestLSet(t *testing.T) { FlushAll(testDB, [][]byte{}) - key := RandString(10) + key := utils2.RandString(10) values := []string{key, "a", "b", "c", "d", "e", "f"} - RPush(testDB, toArgs(values...)) + RPush(testDB, utils2.ToBytesList(values...)) // test positive index size := len(values) - 1 for i := 0; i < size; i++ { indexStr := strconv.Itoa(i) - value := RandString(10) - result := LSet(testDB, toArgs(key, indexStr, value)) + value := utils2.RandString(10) + result := LSet(testDB, utils2.ToBytesList(key, indexStr, value)) if _, ok := result.(*reply.OkReply); !ok { t.Error(fmt.Sprintf("expected OK, actually %s", string(result.ToBytes()))) } - result = LIndex(testDB, toArgs(key, indexStr)) + result = LIndex(testDB, utils2.ToBytesList(key, indexStr)) expected := reply.MakeBulkReply([]byte(value)) if !utils.BytesEquals(result.ToBytes(), expected.ToBytes()) { t.Error(fmt.Sprintf("expected %s, actually %s", string(expected.ToBytes()), string(result.ToBytes()))) @@ -234,12 +235,12 @@ func TestLSet(t *testing.T) { } // test negative index for i := 1; i <= size; i++ { - value := RandString(10) - result := LSet(testDB, toArgs(key, strconv.Itoa(-i), value)) + value := utils2.RandString(10) + result := LSet(testDB, utils2.ToBytesList(key, strconv.Itoa(-i), value)) if _, ok := result.(*reply.OkReply); !ok { t.Error(fmt.Sprintf("expected OK, actually %s", string(result.ToBytes()))) } - result = LIndex(testDB, toArgs(key, strconv.Itoa(len(values)-i-1))) + result = LIndex(testDB, utils2.ToBytesList(key, strconv.Itoa(len(values)-i-1))) expected := reply.MakeBulkReply([]byte(value)) if !utils.BytesEquals(result.ToBytes(), expected.ToBytes()) { t.Error(fmt.Sprintf("expected %s, actually %s", string(expected.ToBytes()), string(result.ToBytes()))) @@ -247,17 +248,17 @@ func TestLSet(t *testing.T) { } // test illegal index - value := RandString(10) - result := LSet(testDB, toArgs(key, strconv.Itoa(-len(values)-1), value)) + value := utils2.RandString(10) + result := LSet(testDB, utils2.ToBytesList(key, strconv.Itoa(-len(values)-1), value)) expected := reply.MakeErrReply("ERR index out of range") if !utils.BytesEquals(result.ToBytes(), expected.ToBytes()) { t.Error(fmt.Sprintf("expected %s, actually %s", string(expected.ToBytes()), string(result.ToBytes()))) } - result = LSet(testDB, toArgs(key, strconv.Itoa(len(values)), value)) + result = LSet(testDB, utils2.ToBytesList(key, strconv.Itoa(len(values)), value)) if !utils.BytesEquals(result.ToBytes(), expected.ToBytes()) { t.Error(fmt.Sprintf("expected %s, actually %s", string(expected.ToBytes()), string(result.ToBytes()))) } - result = LSet(testDB, toArgs(key, "a", value)) + result = LSet(testDB, utils2.ToBytesList(key, "a", value)) expected = reply.MakeErrReply("ERR value is not an integer or out of range") if !utils.BytesEquals(result.ToBytes(), expected.ToBytes()) { t.Error(fmt.Sprintf("expected %s, actually %s", string(expected.ToBytes()), string(result.ToBytes()))) @@ -266,19 +267,19 @@ func TestLSet(t *testing.T) { func TestLPop(t *testing.T) { FlushAll(testDB, [][]byte{}) - key := RandString(10) + key := utils2.RandString(10) values := []string{key, "a", "b", "c", "d", "e", "f"} - RPush(testDB, toArgs(values...)) + RPush(testDB, utils2.ToBytesList(values...)) size := len(values) - 1 for i := 0; i < size; i++ { - result := LPop(testDB, toArgs(key)) + result := LPop(testDB, utils2.ToBytesList(key)) expected := reply.MakeBulkReply([]byte(values[i+1])) if !utils.BytesEquals(result.ToBytes(), expected.ToBytes()) { t.Error(fmt.Sprintf("expected %s, actually %s", string(expected.ToBytes()), string(result.ToBytes()))) } } - result := RPop(testDB, toArgs(key)) + result := RPop(testDB, utils2.ToBytesList(key)) expected := &reply.NullBulkReply{} if !utils.BytesEquals(result.ToBytes(), expected.ToBytes()) { t.Error(fmt.Sprintf("expected %s, actually %s", string(expected.ToBytes()), string(result.ToBytes()))) @@ -287,19 +288,19 @@ func TestLPop(t *testing.T) { func TestRPop(t *testing.T) { FlushAll(testDB, [][]byte{}) - key := RandString(10) + key := utils2.RandString(10) values := []string{key, "a", "b", "c", "d", "e", "f"} - RPush(testDB, toArgs(values...)) + RPush(testDB, utils2.ToBytesList(values...)) size := len(values) - 1 for i := 0; i < size; i++ { - result := RPop(testDB, toArgs(key)) + result := RPop(testDB, utils2.ToBytesList(key)) expected := reply.MakeBulkReply([]byte(values[len(values)-i-1])) if !utils.BytesEquals(result.ToBytes(), expected.ToBytes()) { t.Error(fmt.Sprintf("expected %s, actually %s", string(expected.ToBytes()), string(result.ToBytes()))) } } - result := RPop(testDB, toArgs(key)) + result := RPop(testDB, utils2.ToBytesList(key)) expected := &reply.NullBulkReply{} if !utils.BytesEquals(result.ToBytes(), expected.ToBytes()) { t.Error(fmt.Sprintf("expected %s, actually %s", string(expected.ToBytes()), string(result.ToBytes()))) @@ -308,24 +309,24 @@ func TestRPop(t *testing.T) { func TestRPopLPush(t *testing.T) { FlushAll(testDB, [][]byte{}) - key1 := RandString(10) - key2 := RandString(10) + key1 := utils2.RandString(10) + key2 := utils2.RandString(10) values := []string{key1, "a", "b", "c", "d", "e", "f"} - RPush(testDB, toArgs(values...)) + RPush(testDB, utils2.ToBytesList(values...)) size := len(values) - 1 for i := 0; i < size; i++ { - result := RPopLPush(testDB, toArgs(key1, key2)) + result := RPopLPush(testDB, utils2.ToBytesList(key1, key2)) expected := reply.MakeBulkReply([]byte(values[len(values)-i-1])) if !utils.BytesEquals(result.ToBytes(), expected.ToBytes()) { t.Error(fmt.Sprintf("expected %s, actually %s", string(expected.ToBytes()), string(result.ToBytes()))) } - result = LIndex(testDB, toArgs(key2, "0")) + result = LIndex(testDB, utils2.ToBytesList(key2, "0")) if !utils.BytesEquals(result.ToBytes(), expected.ToBytes()) { t.Error(fmt.Sprintf("expected %s, actually %s", string(expected.ToBytes()), string(result.ToBytes()))) } } - result := RPop(testDB, toArgs(key1)) + result := RPop(testDB, utils2.ToBytesList(key1)) expected := &reply.NullBulkReply{} if !utils.BytesEquals(result.ToBytes(), expected.ToBytes()) { t.Error(fmt.Sprintf("expected %s, actually %s", string(expected.ToBytes()), string(result.ToBytes()))) @@ -334,22 +335,22 @@ func TestRPopLPush(t *testing.T) { func TestRPushX(t *testing.T) { FlushAll(testDB, [][]byte{}) - key := RandString(10) - result := RPushX(testDB, toArgs(key, "1")) + key := utils2.RandString(10) + result := RPushX(testDB, utils2.ToBytesList(key, "1")) expected := reply.MakeIntReply(int64(0)) if !utils.BytesEquals(result.ToBytes(), expected.ToBytes()) { t.Error(fmt.Sprintf("expected %s, actually %s", string(expected.ToBytes()), string(result.ToBytes()))) } - RPush(testDB, toArgs(key, "1")) + RPush(testDB, utils2.ToBytesList(key, "1")) for i := 0; i < 10; i++ { - value := RandString(10) - result := RPushX(testDB, toArgs(key, value)) + value := utils2.RandString(10) + result := RPushX(testDB, utils2.ToBytesList(key, value)) expected := reply.MakeIntReply(int64(i + 2)) if !utils.BytesEquals(result.ToBytes(), expected.ToBytes()) { t.Error(fmt.Sprintf("expected %s, actually %s", string(expected.ToBytes()), string(result.ToBytes()))) } - result = LIndex(testDB, toArgs(key, "-1")) + result = LIndex(testDB, utils2.ToBytesList(key, "-1")) expected2 := reply.MakeBulkReply([]byte(value)) if !utils.BytesEquals(result.ToBytes(), expected2.ToBytes()) { t.Error(fmt.Sprintf("expected %s, actually %s", string(expected2.ToBytes()), string(result.ToBytes()))) @@ -359,22 +360,22 @@ func TestRPushX(t *testing.T) { func TestLPushX(t *testing.T) { FlushAll(testDB, [][]byte{}) - key := RandString(10) - result := RPushX(testDB, toArgs(key, "1")) + key := utils2.RandString(10) + result := RPushX(testDB, utils2.ToBytesList(key, "1")) expected := reply.MakeIntReply(int64(0)) if !utils.BytesEquals(result.ToBytes(), expected.ToBytes()) { t.Error(fmt.Sprintf("expected %s, actually %s", string(expected.ToBytes()), string(result.ToBytes()))) } - LPush(testDB, toArgs(key, "1")) + LPush(testDB, utils2.ToBytesList(key, "1")) for i := 0; i < 10; i++ { - value := RandString(10) - result := LPushX(testDB, toArgs(key, value)) + value := utils2.RandString(10) + result := LPushX(testDB, utils2.ToBytesList(key, value)) expected := reply.MakeIntReply(int64(i + 2)) if !utils.BytesEquals(result.ToBytes(), expected.ToBytes()) { t.Error(fmt.Sprintf("expected %s, actually %s", string(expected.ToBytes()), string(result.ToBytes()))) } - result = LIndex(testDB, toArgs(key, "0")) + result = LIndex(testDB, utils2.ToBytesList(key, "0")) expected2 := reply.MakeBulkReply([]byte(value)) if !utils.BytesEquals(result.ToBytes(), expected2.ToBytes()) { t.Error(fmt.Sprintf("expected %s, actually %s", string(expected2.ToBytes()), string(result.ToBytes()))) diff --git a/db/server_test.go b/db/server_test.go index fbe6c5a..d06cb57 100644 --- a/db/server_test.go +++ b/db/server_test.go @@ -1,16 +1,17 @@ package db import ( + "github.com/hdt3213/godis/lib/utils" "github.com/hdt3213/godis/redis/reply/asserts" "testing" ) func TestPing(t *testing.T) { - actual := Ping(testDB, toArgs()) + actual := Ping(testDB, utils.ToBytesList()) asserts.AssertStatusReply(t, actual, "PONG") - val := RandString(5) - actual = Ping(testDB, toArgs(val)) + val := utils.RandString(5) + actual = Ping(testDB, utils.ToBytesList(val)) asserts.AssertStatusReply(t, actual, val) - actual = Ping(testDB, toArgs(val, val)) + actual = Ping(testDB, utils.ToBytesList(val, val)) asserts.AssertErrReply(t, actual, "ERR wrong number of arguments for 'ping' command") } diff --git a/db/set.go b/db/set.go index c65873e..77c8c93 100644 --- a/db/set.go +++ b/db/set.go @@ -236,7 +236,7 @@ func SInterStore(db *DB, args [][]byte) redis.Reply { } if set == nil { db.Remove(dest) // clean ttl and old value - return &reply.EmptyMultiBulkReply{} + return reply.MakeIntReply(0) } if result == nil { @@ -437,7 +437,7 @@ func SDiffStore(db *DB, args [][]byte) redis.Reply { if i == 0 { // early termination db.Remove(dest) - return &reply.EmptyMultiBulkReply{} + return reply.MakeIntReply(0) } continue } @@ -449,7 +449,7 @@ func SDiffStore(db *DB, args [][]byte) redis.Reply { if result.Len() == 0 { // early termination db.Remove(dest) - return &reply.EmptyMultiBulkReply{} + return reply.MakeIntReply(0) } } } diff --git a/db/set_test.go b/db/set_test.go index 3497700..bb7fa17 100644 --- a/db/set_test.go +++ b/db/set_test.go @@ -2,6 +2,7 @@ package db import ( "fmt" + "github.com/hdt3213/godis/lib/utils" "github.com/hdt3213/godis/redis/reply" "github.com/hdt3213/godis/redis/reply/asserts" "strconv" @@ -14,25 +15,25 @@ func TestSAdd(t *testing.T) { size := 100 // test sadd - key := RandString(10) + key := utils.RandString(10) for i := 0; i < size; i++ { member := strconv.Itoa(i) - result := SAdd(testDB, toArgs(key, member)) + result := SAdd(testDB, utils.ToBytesList(key, member)) asserts.AssertIntReply(t, result, 1) } // test scard - result := SCard(testDB, toArgs(key)) + result := SCard(testDB, utils.ToBytesList(key)) asserts.AssertIntReply(t, result, size) // test is member for i := 0; i < size; i++ { member := strconv.Itoa(i) - result := SIsMember(testDB, toArgs(key, member)) + result := SIsMember(testDB, utils.ToBytesList(key, member)) asserts.AssertIntReply(t, result, 1) } // test members - result = SMembers(testDB, toArgs(key)) + result = SMembers(testDB, utils.ToBytesList(key)) multiBulk, ok := result.(*reply.MultiBulkReply) if !ok { t.Error(fmt.Sprintf("expected bulk reply, actually %s", result.ToBytes())) @@ -49,15 +50,15 @@ func TestSRem(t *testing.T) { size := 100 // mock data - key := RandString(10) + key := utils.RandString(10) for i := 0; i < size; i++ { member := strconv.Itoa(i) - SAdd(testDB, toArgs(key, member)) + SAdd(testDB, utils.ToBytesList(key, member)) } for i := 0; i < size; i++ { member := strconv.Itoa(i) - SRem(testDB, toArgs(key, member)) - result := SIsMember(testDB, toArgs(key, member)) + SRem(testDB, utils.ToBytesList(key, member)) + result := SIsMember(testDB, utils.ToBytesList(key, member)) asserts.AssertIntReply(t, result, 0) } } @@ -70,22 +71,39 @@ func TestSInter(t *testing.T) { keys := make([]string, 0) start := 0 for i := 0; i < 4; i++ { - key := RandString(10) + key := utils.RandString(10) keys = append(keys, key) for j := start; j < size+start; j++ { member := strconv.Itoa(j) - SAdd(testDB, toArgs(key, member)) + SAdd(testDB, utils.ToBytesList(key, member)) } start += step } - result := SInter(testDB, toArgs(keys...)) + result := SInter(testDB, utils.ToBytesList(keys...)) asserts.AssertMultiBulkReplySize(t, result, 70) - destKey := RandString(10) + destKey := utils.RandString(10) keysWithDest := []string{destKey} keysWithDest = append(keysWithDest, keys...) - result = SInterStore(testDB, toArgs(keysWithDest...)) + result = SInterStore(testDB, utils.ToBytesList(keysWithDest...)) asserts.AssertIntReply(t, result, 70) + + // test empty set + FlushAll(testDB, [][]byte{}) + key0 := utils.RandString(10) + Del(testDB, utils.ToBytesList(key0)) + key1 := utils.RandString(10) + SAdd(testDB, utils.ToBytesList(key1, "a", "b")) + key2 := utils.RandString(10) + SAdd(testDB, utils.ToBytesList(key2, "1", "2")) + result = SInter(testDB, utils.ToBytesList(key0, key1, key2)) + asserts.AssertMultiBulkReplySize(t, result, 0) + result = SInter(testDB, utils.ToBytesList(key1, key2)) + asserts.AssertMultiBulkReplySize(t, result, 0) + result = SInterStore(testDB, utils.ToBytesList(utils.RandString(10), key0, key1, key2)) + asserts.AssertIntReply(t, result, 0) + result = SInterStore(testDB, utils.ToBytesList(utils.RandString(10), key1, key2)) + asserts.AssertIntReply(t, result, 0) } func TestSUnion(t *testing.T) { @@ -96,21 +114,21 @@ func TestSUnion(t *testing.T) { keys := make([]string, 0) start := 0 for i := 0; i < 4; i++ { - key := RandString(10) + key := utils.RandString(10) keys = append(keys, key) for j := start; j < size+start; j++ { member := strconv.Itoa(j) - SAdd(testDB, toArgs(key, member)) + SAdd(testDB, utils.ToBytesList(key, member)) } start += step } - result := SUnion(testDB, toArgs(keys...)) + result := SUnion(testDB, utils.ToBytesList(keys...)) asserts.AssertMultiBulkReplySize(t, result, 130) - destKey := RandString(10) + destKey := utils.RandString(10) keysWithDest := []string{destKey} keysWithDest = append(keysWithDest, keys...) - result = SUnionStore(testDB, toArgs(keysWithDest...)) + result = SUnionStore(testDB, utils.ToBytesList(keysWithDest...)) asserts.AssertIntReply(t, result, 130) } @@ -122,39 +140,56 @@ func TestSDiff(t *testing.T) { keys := make([]string, 0) start := 0 for i := 0; i < 3; i++ { - key := RandString(10) + key := utils.RandString(10) keys = append(keys, key) for j := start; j < size+start; j++ { member := strconv.Itoa(j) - SAdd(testDB, toArgs(key, member)) + SAdd(testDB, utils.ToBytesList(key, member)) } start += step } - result := SDiff(testDB, toArgs(keys...)) + result := SDiff(testDB, utils.ToBytesList(keys...)) asserts.AssertMultiBulkReplySize(t, result, step) - destKey := RandString(10) + destKey := utils.RandString(10) keysWithDest := []string{destKey} keysWithDest = append(keysWithDest, keys...) - result = SDiffStore(testDB, toArgs(keysWithDest...)) + result = SDiffStore(testDB, utils.ToBytesList(keysWithDest...)) asserts.AssertIntReply(t, result, step) + + // test empty set + FlushAll(testDB, [][]byte{}) + key0 := utils.RandString(10) + Del(testDB, utils.ToBytesList(key0)) + key1 := utils.RandString(10) + SAdd(testDB, utils.ToBytesList(key1, "a", "b")) + key2 := utils.RandString(10) + SAdd(testDB, utils.ToBytesList(key2, "a", "b")) + result = SDiff(testDB, utils.ToBytesList(key0, key1, key2)) + asserts.AssertMultiBulkReplySize(t, result, 0) + result = SDiff(testDB, utils.ToBytesList(key1, key2)) + asserts.AssertMultiBulkReplySize(t, result, 0) + result = SDiffStore(testDB, utils.ToBytesList(utils.RandString(10), key0, key1, key2)) + asserts.AssertIntReply(t, result, 0) + result = SDiffStore(testDB, utils.ToBytesList(utils.RandString(10), key1, key2)) + asserts.AssertIntReply(t, result, 0) } func TestSRandMember(t *testing.T) { FlushAll(testDB, [][]byte{}) - key := RandString(10) + key := utils.RandString(10) for j := 0; j < 100; j++ { member := strconv.Itoa(j) - SAdd(testDB, toArgs(key, member)) + SAdd(testDB, utils.ToBytesList(key, member)) } - result := SRandMember(testDB, toArgs(key)) + result := SRandMember(testDB, utils.ToBytesList(key)) br, ok := result.(*reply.BulkReply) if !ok && len(br.Arg) > 0 { t.Error(fmt.Sprintf("expected bulk reply, actually %s", result.ToBytes())) return } - result = SRandMember(testDB, toArgs(key, "10")) + result = SRandMember(testDB, utils.ToBytesList(key, "10")) asserts.AssertMultiBulkReplySize(t, result, 10) multiBulk, ok := result.(*reply.MultiBulkReply) if !ok { @@ -170,12 +205,12 @@ func TestSRandMember(t *testing.T) { return } - result = SRandMember(testDB, toArgs(key, "110")) + result = SRandMember(testDB, utils.ToBytesList(key, "110")) asserts.AssertMultiBulkReplySize(t, result, 100) - result = SRandMember(testDB, toArgs(key, "-10")) + result = SRandMember(testDB, utils.ToBytesList(key, "-10")) asserts.AssertMultiBulkReplySize(t, result, 10) - result = SRandMember(testDB, toArgs(key, "-110")) + result = SRandMember(testDB, utils.ToBytesList(key, "-110")) asserts.AssertMultiBulkReplySize(t, result, 110) } diff --git a/db/sortedset_test.go b/db/sortedset_test.go index b227d08..1ab1c3c 100644 --- a/db/sortedset_test.go +++ b/db/sortedset_test.go @@ -1,6 +1,7 @@ package db import ( + "github.com/hdt3213/godis/lib/utils" "github.com/hdt3213/godis/redis/reply/asserts" "math/rand" "strconv" @@ -12,27 +13,27 @@ func TestZAdd(t *testing.T) { size := 100 // add new members - key := RandString(10) + key := utils.RandString(10) members := make([]string, size) scores := make([]float64, size) setArgs := []string{key} for i := 0; i < size; i++ { - members[i] = RandString(10) + members[i] = utils.RandString(10) scores[i] = rand.Float64() setArgs = append(setArgs, strconv.FormatFloat(scores[i], 'f', -1, 64), members[i]) } - result := ZAdd(testDB, toArgs(setArgs...)) + result := ZAdd(testDB, utils.ToBytesList(setArgs...)) asserts.AssertIntReply(t, result, size) // test zscore and zrank for i, member := range members { - result := ZScore(testDB, toArgs(key, member)) + result := ZScore(testDB, utils.ToBytesList(key, member)) score := strconv.FormatFloat(scores[i], 'f', -1, 64) asserts.AssertBulkReply(t, result, score) } // test zcard - result = ZCard(testDB, toArgs(key)) + result = ZCard(testDB, utils.ToBytesList(key)) asserts.AssertIntReply(t, result, size) // update members @@ -41,12 +42,12 @@ func TestZAdd(t *testing.T) { scores[i] = rand.Float64() + 100 setArgs = append(setArgs, strconv.FormatFloat(scores[i], 'f', -1, 64), members[i]) } - result = ZAdd(testDB, toArgs(setArgs...)) + result = ZAdd(testDB, utils.ToBytesList(setArgs...)) asserts.AssertIntReply(t, result, 0) // return number of new members // test updated score for i, member := range members { - result := ZScore(testDB, toArgs(key, member)) + result := ZScore(testDB, utils.ToBytesList(key, member)) score := strconv.FormatFloat(scores[i], 'f', -1, 64) asserts.AssertBulkReply(t, result, score) } @@ -55,23 +56,23 @@ func TestZAdd(t *testing.T) { func TestZRank(t *testing.T) { FlushAll(testDB, [][]byte{}) size := 100 - key := RandString(10) + key := utils.RandString(10) members := make([]string, size) scores := make([]int, size) setArgs := []string{key} for i := 0; i < size; i++ { - members[i] = RandString(10) + members[i] = utils.RandString(10) scores[i] = i setArgs = append(setArgs, strconv.FormatInt(int64(scores[i]), 10), members[i]) } - ZAdd(testDB, toArgs(setArgs...)) + ZAdd(testDB, utils.ToBytesList(setArgs...)) // test zrank for i, member := range members { - result := ZRank(testDB, toArgs(key, member)) + result := ZRank(testDB, utils.ToBytesList(key, member)) asserts.AssertIntReply(t, result, i) - result = ZRevRank(testDB, toArgs(key, member)) + result = ZRevRank(testDB, utils.ToBytesList(key, member)) asserts.AssertIntReply(t, result, size-i-1) } } @@ -80,16 +81,16 @@ func TestZRange(t *testing.T) { // prepare FlushAll(testDB, [][]byte{}) size := 100 - key := RandString(10) + key := utils.RandString(10) members := make([]string, size) scores := make([]int, size) setArgs := []string{key} for i := 0; i < size; i++ { - members[i] = RandString(10) + members[i] = utils.RandString(10) scores[i] = i setArgs = append(setArgs, strconv.FormatInt(int64(scores[i]), 10), members[i]) } - ZAdd(testDB, toArgs(setArgs...)) + ZAdd(testDB, utils.ToBytesList(setArgs...)) reverseMembers := make([]string, size) for i, v := range members { reverseMembers[size-i-1] = v @@ -97,37 +98,39 @@ func TestZRange(t *testing.T) { start := "0" end := "9" - result := ZRange(testDB, toArgs(key, start, end)) + result := ZRange(testDB, utils.ToBytesList(key, start, end)) asserts.AssertMultiBulkReply(t, result, members[0:10]) - result = ZRevRange(testDB, toArgs(key, start, end)) + result = ZRange(testDB, utils.ToBytesList(key, start, end, "WITHSCORES")) + asserts.AssertMultiBulkReplySize(t, result, 20) + result = ZRevRange(testDB, utils.ToBytesList(key, start, end)) asserts.AssertMultiBulkReply(t, result, reverseMembers[0:10]) start = "0" end = "200" - result = ZRange(testDB, toArgs(key, start, end)) + result = ZRange(testDB, utils.ToBytesList(key, start, end)) asserts.AssertMultiBulkReply(t, result, members) - result = ZRevRange(testDB, toArgs(key, start, end)) + result = ZRevRange(testDB, utils.ToBytesList(key, start, end)) asserts.AssertMultiBulkReply(t, result, reverseMembers) start = "0" end = "-10" - result = ZRange(testDB, toArgs(key, start, end)) + result = ZRange(testDB, utils.ToBytesList(key, start, end)) asserts.AssertMultiBulkReply(t, result, members[0:size-10+1]) - result = ZRevRange(testDB, toArgs(key, start, end)) + result = ZRevRange(testDB, utils.ToBytesList(key, start, end)) asserts.AssertMultiBulkReply(t, result, reverseMembers[0:size-10+1]) start = "0" end = "-200" - result = ZRange(testDB, toArgs(key, start, end)) + result = ZRange(testDB, utils.ToBytesList(key, start, end)) asserts.AssertMultiBulkReply(t, result, members[0:0]) - result = ZRevRange(testDB, toArgs(key, start, end)) + result = ZRevRange(testDB, utils.ToBytesList(key, start, end)) asserts.AssertMultiBulkReply(t, result, reverseMembers[0:0]) start = "-10" end = "-1" - result = ZRange(testDB, toArgs(key, start, end)) + result = ZRange(testDB, utils.ToBytesList(key, start, end)) asserts.AssertMultiBulkReply(t, result, members[90:]) - result = ZRevRange(testDB, toArgs(key, start, end)) + result = ZRevRange(testDB, utils.ToBytesList(key, start, end)) asserts.AssertMultiBulkReply(t, result, reverseMembers[90:]) } @@ -143,7 +146,7 @@ func TestZRangeByScore(t *testing.T) { // prepare FlushAll(testDB, [][]byte{}) size := 100 - key := RandString(10) + key := utils.RandString(10) members := make([]string, size) scores := make([]int, size) setArgs := []string{key} @@ -152,48 +155,50 @@ func TestZRangeByScore(t *testing.T) { scores[i] = i setArgs = append(setArgs, strconv.FormatInt(int64(scores[i]), 10), members[i]) } - result := ZAdd(testDB, toArgs(setArgs...)) + result := ZAdd(testDB, utils.ToBytesList(setArgs...)) min := "20" max := "30" - result = ZRangeByScore(testDB, toArgs(key, min, max)) + result = ZRangeByScore(testDB, utils.ToBytesList(key, min, max)) asserts.AssertMultiBulkReply(t, result, members[20:31]) - result = ZRevRangeByScore(testDB, toArgs(key, max, min)) + result = ZRangeByScore(testDB, utils.ToBytesList(key, min, max, "WITHSCORES")) + asserts.AssertMultiBulkReplySize(t, result, 22) + result = ZRevRangeByScore(testDB, utils.ToBytesList(key, max, min)) asserts.AssertMultiBulkReply(t, result, reverse(members[20:31])) min = "-10" max = "10" - result = ZRangeByScore(testDB, toArgs(key, min, max)) + result = ZRangeByScore(testDB, utils.ToBytesList(key, min, max)) asserts.AssertMultiBulkReply(t, result, members[0:11]) - result = ZRevRangeByScore(testDB, toArgs(key, max, min)) + result = ZRevRangeByScore(testDB, utils.ToBytesList(key, max, min)) asserts.AssertMultiBulkReply(t, result, reverse(members[0:11])) min = "90" max = "110" - result = ZRangeByScore(testDB, toArgs(key, min, max)) + result = ZRangeByScore(testDB, utils.ToBytesList(key, min, max)) asserts.AssertMultiBulkReply(t, result, members[90:]) - result = ZRevRangeByScore(testDB, toArgs(key, max, min)) + result = ZRevRangeByScore(testDB, utils.ToBytesList(key, max, min)) asserts.AssertMultiBulkReply(t, result, reverse(members[90:])) min = "(20" max = "(30" - result = ZRangeByScore(testDB, toArgs(key, min, max)) + result = ZRangeByScore(testDB, utils.ToBytesList(key, min, max)) asserts.AssertMultiBulkReply(t, result, members[21:30]) - result = ZRevRangeByScore(testDB, toArgs(key, max, min)) + result = ZRevRangeByScore(testDB, utils.ToBytesList(key, max, min)) asserts.AssertMultiBulkReply(t, result, reverse(members[21:30])) min = "20" max = "40" - result = ZRangeByScore(testDB, toArgs(key, min, max, "LIMIT", "5", "5")) + result = ZRangeByScore(testDB, utils.ToBytesList(key, min, max, "LIMIT", "5", "5")) asserts.AssertMultiBulkReply(t, result, members[25:30]) - result = ZRevRangeByScore(testDB, toArgs(key, max, min, "LIMIT", "5", "5")) + result = ZRevRangeByScore(testDB, utils.ToBytesList(key, max, min, "LIMIT", "5", "5")) asserts.AssertMultiBulkReply(t, result, reverse(members[31:36])) } func TestZRem(t *testing.T) { FlushAll(testDB, [][]byte{}) size := 100 - key := RandString(10) + key := utils.RandString(10) members := make([]string, size) scores := make([]int, size) setArgs := []string{key} @@ -202,19 +207,19 @@ func TestZRem(t *testing.T) { scores[i] = i setArgs = append(setArgs, strconv.FormatInt(int64(scores[i]), 10), members[i]) } - ZAdd(testDB, toArgs(setArgs...)) + ZAdd(testDB, utils.ToBytesList(setArgs...)) args := []string{key} args = append(args, members[0:10]...) - result := ZRem(testDB, toArgs(args...)) + result := ZRem(testDB, utils.ToBytesList(args...)) asserts.AssertIntReply(t, result, 10) - result = ZCard(testDB, toArgs(key)) + result = ZCard(testDB, utils.ToBytesList(key)) asserts.AssertIntReply(t, result, size-10) // test ZRemRangeByRank FlushAll(testDB, [][]byte{}) size = 100 - key = RandString(10) + key = utils.RandString(10) members = make([]string, size) scores = make([]int, size) setArgs = []string{key} @@ -223,17 +228,17 @@ func TestZRem(t *testing.T) { scores[i] = i setArgs = append(setArgs, strconv.FormatInt(int64(scores[i]), 10), members[i]) } - ZAdd(testDB, toArgs(setArgs...)) + ZAdd(testDB, utils.ToBytesList(setArgs...)) - result = ZRemRangeByRank(testDB, toArgs(key, "0", "9")) + result = ZRemRangeByRank(testDB, utils.ToBytesList(key, "0", "9")) asserts.AssertIntReply(t, result, 10) - result = ZCard(testDB, toArgs(key)) + result = ZCard(testDB, utils.ToBytesList(key)) asserts.AssertIntReply(t, result, size-10) // test ZRemRangeByScore FlushAll(testDB, [][]byte{}) size = 100 - key = RandString(10) + key = utils.RandString(10) members = make([]string, size) scores = make([]int, size) setArgs = []string{key} @@ -242,11 +247,11 @@ func TestZRem(t *testing.T) { scores[i] = i setArgs = append(setArgs, strconv.FormatInt(int64(scores[i]), 10), members[i]) } - ZAdd(testDB, toArgs(setArgs...)) + ZAdd(testDB, utils.ToBytesList(setArgs...)) - result = ZRemRangeByScore(testDB, toArgs(key, "0", "9")) + result = ZRemRangeByScore(testDB, utils.ToBytesList(key, "0", "9")) asserts.AssertIntReply(t, result, 10) - result = ZCard(testDB, toArgs(key)) + result = ZCard(testDB, utils.ToBytesList(key)) asserts.AssertIntReply(t, result, size-10) } @@ -254,7 +259,7 @@ func TestZCount(t *testing.T) { // prepare FlushAll(testDB, [][]byte{}) size := 100 - key := RandString(10) + key := utils.RandString(10) members := make([]string, size) scores := make([]int, size) setArgs := []string{key} @@ -263,36 +268,37 @@ func TestZCount(t *testing.T) { scores[i] = i setArgs = append(setArgs, strconv.FormatInt(int64(scores[i]), 10), members[i]) } - ZAdd(testDB, toArgs(setArgs...)) + ZAdd(testDB, utils.ToBytesList(setArgs...)) min := "20" max := "30" - result := ZCount(testDB, toArgs(key, min, max)) + result := ZCount(testDB, utils.ToBytesList(key, min, max)) asserts.AssertIntReply(t, result, 11) min = "-10" max = "10" - result = ZCount(testDB, toArgs(key, min, max)) + result = ZCount(testDB, utils.ToBytesList(key, min, max)) asserts.AssertIntReply(t, result, 11) min = "90" max = "110" - result = ZCount(testDB, toArgs(key, min, max)) + result = ZCount(testDB, utils.ToBytesList(key, min, max)) asserts.AssertIntReply(t, result, 10) min = "(20" max = "(30" - result = ZCount(testDB, toArgs(key, min, max)) + result = ZCount(testDB, utils.ToBytesList(key, min, max)) asserts.AssertIntReply(t, result, 9) } func TestZIncrBy(t *testing.T) { FlushAll(testDB, [][]byte{}) - key := RandString(10) - ZAdd(testDB, toArgs(key, "10", "a")) - result := ZIncrBy(testDB, toArgs(key, "10", "a")) + key := utils.RandString(10) + result := ZIncrBy(testDB, utils.ToBytesList(key, "10", "a")) + asserts.AssertBulkReply(t, result, "10") + result = ZIncrBy(testDB, utils.ToBytesList(key, "10", "a")) asserts.AssertBulkReply(t, result, "20") - result = ZScore(testDB, toArgs(key, "a")) + result = ZScore(testDB, utils.ToBytesList(key, "a")) asserts.AssertBulkReply(t, result, "20") } diff --git a/db/string_test.go b/db/string_test.go index 9128814..b0fe546 100644 --- a/db/string_test.go +++ b/db/string_test.go @@ -3,6 +3,7 @@ package db import ( "fmt" "github.com/hdt3213/godis/datastruct/utils" + utils2 "github.com/hdt3213/godis/lib/utils" "github.com/hdt3213/godis/redis/reply" "github.com/hdt3213/godis/redis/reply/asserts" "strconv" @@ -13,28 +14,28 @@ var testDB = makeTestDB() func TestSet(t *testing.T) { FlushAll(testDB, [][]byte{}) - key := RandString(10) - value := RandString(10) + key := utils2.RandString(10) + value := utils2.RandString(10) // normal set - Set(testDB, toArgs(key, value)) - actual := Get(testDB, toArgs(key)) + Set(testDB, utils2.ToBytesList(key, value)) + actual := Get(testDB, utils2.ToBytesList(key)) expected := reply.MakeBulkReply([]byte(value)) if !utils.BytesEquals(actual.ToBytes(), expected.ToBytes()) { t.Error("expected: " + string(expected.ToBytes()) + ", actual: " + string(actual.ToBytes())) } // set nx - actual = Set(testDB, toArgs(key, value, "NX")) + actual = Set(testDB, utils2.ToBytesList(key, value, "NX")) if _, ok := actual.(*reply.NullBulkReply); !ok { t.Error("expected true actual false") } FlushAll(testDB, [][]byte{}) - key = RandString(10) - value = RandString(10) - Set(testDB, toArgs(key, value, "NX")) - actual = Get(testDB, toArgs(key)) + key = utils2.RandString(10) + value = utils2.RandString(10) + Set(testDB, utils2.ToBytesList(key, value, "NX")) + actual = Get(testDB, utils2.ToBytesList(key)) expected = reply.MakeBulkReply([]byte(value)) if !utils.BytesEquals(actual.ToBytes(), expected.ToBytes()) { t.Error("expected: " + string(expected.ToBytes()) + ", actual: " + string(actual.ToBytes())) @@ -42,25 +43,25 @@ func TestSet(t *testing.T) { // set xx FlushAll(testDB, [][]byte{}) - key = RandString(10) - value = RandString(10) - actual = Set(testDB, toArgs(key, value, "XX")) + key = utils2.RandString(10) + value = utils2.RandString(10) + actual = Set(testDB, utils2.ToBytesList(key, value, "XX")) if _, ok := actual.(*reply.NullBulkReply); !ok { t.Error("expected true actually false ") } - Set(testDB, toArgs(key, value)) - Set(testDB, toArgs(key, value, "XX")) - actual = Get(testDB, toArgs(key)) + Set(testDB, utils2.ToBytesList(key, value)) + Set(testDB, utils2.ToBytesList(key, value, "XX")) + actual = Get(testDB, utils2.ToBytesList(key)) asserts.AssertBulkReply(t, actual, value) // set ex - Del(testDB, toArgs(key)) + Del(testDB, utils2.ToBytesList(key)) ttl := "1000" - Set(testDB, toArgs(key, value, "EX", ttl)) - actual = Get(testDB, toArgs(key)) + Set(testDB, utils2.ToBytesList(key, value, "EX", ttl)) + actual = Get(testDB, utils2.ToBytesList(key)) asserts.AssertBulkReply(t, actual, value) - actual = TTL(testDB, toArgs(key)) + actual = TTL(testDB, utils2.ToBytesList(key)) intResult, ok := actual.(*reply.IntReply) if !ok { t.Error(fmt.Sprintf("expected int reply, actually %s", actual.ToBytes())) @@ -72,12 +73,12 @@ func TestSet(t *testing.T) { } // set px - Del(testDB, toArgs(key)) + Del(testDB, utils2.ToBytesList(key)) ttlPx := "1000000" - Set(testDB, toArgs(key, value, "PX", ttlPx)) - actual = Get(testDB, toArgs(key)) + Set(testDB, utils2.ToBytesList(key, value, "PX", ttlPx)) + actual = Get(testDB, utils2.ToBytesList(key)) asserts.AssertBulkReply(t, actual, value) - actual = TTL(testDB, toArgs(key)) + actual = TTL(testDB, utils2.ToBytesList(key)) intResult, ok = actual.(*reply.IntReply) if !ok { t.Error(fmt.Sprintf("expected int reply, actually %s", actual.ToBytes())) @@ -91,16 +92,16 @@ func TestSet(t *testing.T) { func TestSetNX(t *testing.T) { FlushAll(testDB, [][]byte{}) - key := RandString(10) - value := RandString(10) - SetNX(testDB, toArgs(key, value)) - actual := Get(testDB, toArgs(key)) + key := utils2.RandString(10) + value := utils2.RandString(10) + SetNX(testDB, utils2.ToBytesList(key, value)) + actual := Get(testDB, utils2.ToBytesList(key)) expected := reply.MakeBulkReply([]byte(value)) if !utils.BytesEquals(actual.ToBytes(), expected.ToBytes()) { t.Error("expected: " + string(expected.ToBytes()) + ", actual: " + string(actual.ToBytes())) } - actual = SetNX(testDB, toArgs(key, value)) + actual = SetNX(testDB, utils2.ToBytesList(key, value)) expected2 := reply.MakeIntReply(int64(0)) if !utils.BytesEquals(actual.ToBytes(), expected2.ToBytes()) { t.Error("expected: " + string(expected2.ToBytes()) + ", actual: " + string(actual.ToBytes())) @@ -109,14 +110,14 @@ func TestSetNX(t *testing.T) { func TestSetEX(t *testing.T) { FlushAll(testDB, [][]byte{}) - key := RandString(10) - value := RandString(10) + key := utils2.RandString(10) + value := utils2.RandString(10) ttl := "1000" - SetEX(testDB, toArgs(key, ttl, value)) - actual := Get(testDB, toArgs(key)) + SetEX(testDB, utils2.ToBytesList(key, ttl, value)) + actual := Get(testDB, utils2.ToBytesList(key)) asserts.AssertBulkReply(t, actual, value) - actual = TTL(testDB, toArgs(key)) + actual = TTL(testDB, utils2.ToBytesList(key)) intResult, ok := actual.(*reply.IntReply) if !ok { t.Error(fmt.Sprintf("expected int reply, actually %s", actual.ToBytes())) @@ -130,14 +131,14 @@ func TestSetEX(t *testing.T) { func TestPSetEX(t *testing.T) { FlushAll(testDB, [][]byte{}) - key := RandString(10) - value := RandString(10) + key := utils2.RandString(10) + value := utils2.RandString(10) ttl := "1000000" - PSetEX(testDB, toArgs(key, ttl, value)) - actual := Get(testDB, toArgs(key)) + PSetEX(testDB, utils2.ToBytesList(key, ttl, value)) + actual := Get(testDB, utils2.ToBytesList(key)) asserts.AssertBulkReply(t, actual, value) - actual = PTTL(testDB, toArgs(key)) + actual = PTTL(testDB, utils2.ToBytesList(key)) intResult, ok := actual.(*reply.IntReply) if !ok { t.Error(fmt.Sprintf("expected int reply, actually %s", actual.ToBytes())) @@ -156,13 +157,13 @@ func TestMSet(t *testing.T) { values := make([][]byte, size) args := make([]string, 0, size*2) for i := 0; i < size; i++ { - keys[i] = RandString(10) - value := RandString(10) + keys[i] = utils2.RandString(10) + value := utils2.RandString(10) values[i] = []byte(value) args = append(args, keys[i], value) } - MSet(testDB, toArgs(args...)) - actual := MGet(testDB, toArgs(keys...)) + MSet(testDB, utils2.ToBytesList(args...)) + actual := MGet(testDB, utils2.ToBytesList(keys...)) expected := reply.MakeMultiBulkReply(values) if !utils.BytesEquals(actual.ToBytes(), expected.ToBytes()) { t.Error("expected: " + string(expected.ToBytes()) + ", actual: " + string(actual.ToBytes())) @@ -172,18 +173,18 @@ func TestMSet(t *testing.T) { func TestIncr(t *testing.T) { FlushAll(testDB, [][]byte{}) size := 10 - key := RandString(10) + key := utils2.RandString(10) for i := 0; i < size; i++ { - Incr(testDB, toArgs(key)) - actual := Get(testDB, toArgs(key)) + Incr(testDB, utils2.ToBytesList(key)) + actual := Get(testDB, utils2.ToBytesList(key)) expected := reply.MakeBulkReply([]byte(strconv.FormatInt(int64(i+1), 10))) if !utils.BytesEquals(actual.ToBytes(), expected.ToBytes()) { t.Error("expected: " + string(expected.ToBytes()) + ", actual: " + string(actual.ToBytes())) } } for i := 0; i < size; i++ { - IncrBy(testDB, toArgs(key, "-1")) - actual := Get(testDB, toArgs(key)) + IncrBy(testDB, utils2.ToBytesList(key, "-1")) + actual := Get(testDB, utils2.ToBytesList(key)) expected := reply.MakeBulkReply([]byte(strconv.FormatInt(int64(size-i-1), 10))) if !utils.BytesEquals(actual.ToBytes(), expected.ToBytes()) { t.Error("expected: " + string(expected.ToBytes()) + ", actual: " + string(actual.ToBytes())) @@ -191,19 +192,19 @@ func TestIncr(t *testing.T) { } FlushAll(testDB, [][]byte{}) - key = RandString(10) + key = utils2.RandString(10) for i := 0; i < size; i++ { - IncrBy(testDB, toArgs(key, "1")) - actual := Get(testDB, toArgs(key)) + IncrBy(testDB, utils2.ToBytesList(key, "1")) + actual := Get(testDB, utils2.ToBytesList(key)) expected := reply.MakeBulkReply([]byte(strconv.FormatInt(int64(i+1), 10))) if !utils.BytesEquals(actual.ToBytes(), expected.ToBytes()) { t.Error("expected: " + string(expected.ToBytes()) + ", actual: " + string(actual.ToBytes())) } } - Del(testDB, toArgs(key)) + Del(testDB, utils2.ToBytesList(key)) for i := 0; i < size; i++ { - IncrByFloat(testDB, toArgs(key, "-1.0")) - actual := Get(testDB, toArgs(key)) + IncrByFloat(testDB, utils2.ToBytesList(key, "-1.0")) + actual := Get(testDB, utils2.ToBytesList(key)) expected := -i - 1 bulk, ok := actual.(*reply.BulkReply) if !ok { @@ -225,16 +226,16 @@ func TestIncr(t *testing.T) { func TestDecr(t *testing.T) { FlushAll(testDB, [][]byte{}) size := 10 - key := RandString(10) + key := utils2.RandString(10) for i := 0; i < size; i++ { - Decr(testDB, toArgs(key)) - actual := Get(testDB, toArgs(key)) + Decr(testDB, utils2.ToBytesList(key)) + actual := Get(testDB, utils2.ToBytesList(key)) asserts.AssertBulkReply(t, actual, strconv.Itoa(-i-1)) } - Del(testDB, toArgs(key)) + Del(testDB, utils2.ToBytesList(key)) for i := 0; i < size; i++ { - DecrBy(testDB, toArgs(key, "1")) - actual := Get(testDB, toArgs(key)) + DecrBy(testDB, utils2.ToBytesList(key, "1")) + actual := Get(testDB, utils2.ToBytesList(key)) expected := -i - 1 bulk, ok := actual.(*reply.BulkReply) if !ok { @@ -255,20 +256,20 @@ func TestDecr(t *testing.T) { func TestGetSet(t *testing.T) { FlushAll(testDB, [][]byte{}) - key := RandString(10) - value := RandString(10) + key := utils2.RandString(10) + value := utils2.RandString(10) - result := GetSet(testDB, toArgs(key, value)) + result := GetSet(testDB, utils2.ToBytesList(key, value)) _, ok := result.(*reply.NullBulkReply) if !ok { t.Errorf("expect null bulk reply, get: %s", string(result.ToBytes())) return } - value2 := RandString(10) - result = GetSet(testDB, toArgs(key, value2)) + value2 := utils2.RandString(10) + result = GetSet(testDB, utils2.ToBytesList(key, value2)) asserts.AssertBulkReply(t, result, value) - result = Get(testDB, toArgs(key)) + result = Get(testDB, utils2.ToBytesList(key)) asserts.AssertBulkReply(t, result, value2) } @@ -277,12 +278,12 @@ func TestMSetNX(t *testing.T) { size := 10 args := make([]string, 0, size*2) for i := 0; i < size; i++ { - str := RandString(10) + str := utils2.RandString(10) args = append(args, str, str) } - result := MSetNX(testDB, toArgs(args...)) + result := MSetNX(testDB, utils2.ToBytesList(args...)) asserts.AssertIntReply(t, result, 1) - result = MSetNX(testDB, toArgs(args[0:4]...)) + result = MSetNX(testDB, utils2.ToBytesList(args[0:4]...)) asserts.AssertIntReply(t, result, 0) } diff --git a/db/util_test.go b/db/util_test.go index e7d1ffd..dcbcba6 100644 --- a/db/util_test.go +++ b/db/util_test.go @@ -3,7 +3,6 @@ package db import ( "github.com/hdt3213/godis/datastruct/dict" "github.com/hdt3213/godis/datastruct/lock" - "math/rand" ) func makeTestDB() *DB { @@ -14,20 +13,3 @@ func makeTestDB() *DB { } } -func toArgs(cmd ...string) [][]byte { - args := make([][]byte, len(cmd)) - for i, s := range cmd { - args[i] = []byte(s) - } - return args -} - -var letters = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789") - -func RandString(n int) string { - b := make([]rune, n) - for i := range b { - b[i] = letters[rand.Intn(len(letters))] - } - return string(b) -} diff --git a/lib/timewheel/delay_test.go b/lib/timewheel/delay_test.go new file mode 100644 index 0000000..945d226 --- /dev/null +++ b/lib/timewheel/delay_test.go @@ -0,0 +1,20 @@ +package timewheel + +import ( + "testing" + "time" +) + +func TestDelay(t *testing.T) { + ch := make(chan time.Time) + beginTime := time.Now() + Delay(time.Second, "", func() { + ch <- time.Now() + }) + execAt := <-ch + delayDuration := execAt.Sub(beginTime) + // usually 1.0~2.0 s + if delayDuration < time.Second || delayDuration > 3*time.Second { + t.Error("wrong execute time") + } +} diff --git a/lib/utils/convert.go b/lib/utils/convert.go new file mode 100644 index 0000000..04a8b4a --- /dev/null +++ b/lib/utils/convert.go @@ -0,0 +1,9 @@ +package utils + +func ToBytesList(cmd ...string) [][]byte { + args := make([][]byte, len(cmd)) + for i, s := range cmd { + args[i] = []byte(s) + } + return args +} diff --git a/lib/utils/rand_string.go b/lib/utils/rand_string.go new file mode 100644 index 0000000..364e855 --- /dev/null +++ b/lib/utils/rand_string.go @@ -0,0 +1,13 @@ +package utils + +import "math/rand" + +var letters = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789") + +func RandString(n int) string { + b := make([]rune, n) + for i := range b { + b[i] = letters[rand.Intn(len(letters))] + } + return string(b) +} diff --git a/lib/wildcard/wildcard_test.go b/lib/wildcard/wildcard_test.go index 190840a..c943eaa 100644 --- a/lib/wildcard/wildcard_test.go +++ b/lib/wildcard/wildcard_test.go @@ -3,7 +3,11 @@ package wildcard import "testing" func TestWildCard(t *testing.T) { - p := CompilePattern("a") + p := CompilePattern("") + if !p.IsMatch("") { + t.Error("expect true actually false") + } + p = CompilePattern("a") if !p.IsMatch("a") { t.Error("expect true actually false") } diff --git a/pubsub/pubsub.go b/pubsub/pubsub.go index 169d030..0465b2e 100644 --- a/pubsub/pubsub.go +++ b/pubsub/pubsub.go @@ -121,6 +121,7 @@ func UnSubscribe(db *Hub, c redis.Connection, args [][]byte) redis.Reply { return &reply.NoReply{} } +// Publish send msg to all subscribing client func Publish(hub *Hub, args [][]byte) redis.Reply { if len(args) != 2 { return &reply.ArgNumErrReply{Cmd: "publish"} diff --git a/redis/client/client.go b/redis/client/client.go index 5b538b2..95a8028 100644 --- a/redis/client/client.go +++ b/redis/client/client.go @@ -184,7 +184,7 @@ func (client *Client) finishRequest(reply redis.Reply) { } func (client *Client) handleRead() error { - ch := parser.Parse(client.conn) + ch := parser.ParseStream(client.conn) for payload := range ch { if payload.Err != nil { client.finishRequest(reply.MakeErrReply(payload.Err.Error())) diff --git a/redis/server/conn.go b/redis/connection/conn.go similarity index 81% rename from redis/server/conn.go rename to redis/connection/conn.go index a2ab057..b6d9b8f 100644 --- a/redis/server/conn.go +++ b/redis/connection/conn.go @@ -1,6 +1,7 @@ -package server +package connection import ( + "bytes" "github.com/hdt3213/godis/lib/sync/wait" "net" "sync" @@ -21,6 +22,11 @@ type Connection struct { subs map[string]bool } +// RemoteAddr returns the remote network address +func (c *Connection) RemoteAddr() net.Addr { + return c.conn.RemoteAddr() +} + // Close disconnect with the client func (c *Connection) Close() error { c.waitingReply.WaitWithTimeout(10 * time.Second) @@ -90,3 +96,21 @@ func (c *Connection) GetChannels() []string { } return channels } + +type FakeConn struct { + Connection + buf bytes.Buffer +} + +func (c *FakeConn) Write(b []byte) error { + c.buf.Write(b) + return nil +} + +func (c *FakeConn) Clean() { + c.buf.Reset() +} + +func (c *FakeConn) Bytes() []byte { + return c.buf.Bytes() +} \ No newline at end of file diff --git a/redis/parser/parser.go b/redis/parser/parser.go index 2bf1c45..90beca8 100644 --- a/redis/parser/parser.go +++ b/redis/parser/parser.go @@ -2,6 +2,7 @@ package parser import ( "bufio" + "bytes" "errors" "github.com/hdt3213/godis/interface/redis" "github.com/hdt3213/godis/lib/logger" @@ -17,19 +18,25 @@ type Payload struct { Err error } -func Parse(reader io.Reader) <-chan *Payload { +// ParseStream reads data from io.Reader and send payloads through channel +func ParseStream(reader io.Reader) <-chan *Payload { ch := make(chan *Payload) - go func() { - defer func() { - if err := recover(); err != nil { - logger.Error(debug.Stack()) - } - }() - parse0(reader, ch) - }() + go parse0(reader, ch) return ch } +// ParseOne reads data from []byte and return the first payload +func ParseOne(data []byte) (redis.Reply, error) { + ch := make(chan *Payload) + reader := bytes.NewReader(data) + go parse0(reader, ch) + payload := <-ch // parse0 will close the channel + if payload == nil { + return nil, errors.New("no reply") + } + return payload.Data, payload.Err +} + type readState struct { downloading bool expectedArgsCount int @@ -44,6 +51,11 @@ func (s *readState) finished() bool { } func parse0(reader io.Reader, ch chan<- *Payload) { + defer func() { + if err := recover(); err != nil { + logger.Error(string(debug.Stack())) + } + }() bufReader := bufio.NewReader(reader) var state readState var err error diff --git a/redis/parser/parser_test.go b/redis/parser/parser_test.go index fe55d6e..0494cdc 100644 --- a/redis/parser/parser_test.go +++ b/redis/parser/parser_test.go @@ -9,7 +9,7 @@ import ( "testing" ) -func TestParse(t *testing.T) { +func TestParseStream(t *testing.T) { replies := []redis.Reply{ reply.MakeIntReply(1), reply.MakeStatusReply("OK"), @@ -33,7 +33,7 @@ func TestParse(t *testing.T) { []byte("set"), []byte("a"), []byte("a"), })) - ch := Parse(bytes.NewReader(reqs.Bytes())) + ch := ParseStream(bytes.NewReader(reqs.Bytes())) i := 0 for payload := range ch { if payload.Err != nil { @@ -54,3 +54,28 @@ func TestParse(t *testing.T) { } } } + +func TestParseOne(t *testing.T) { + replies := []redis.Reply{ + reply.MakeIntReply(1), + reply.MakeStatusReply("OK"), + reply.MakeErrReply("ERR unknown"), + reply.MakeBulkReply([]byte("a\r\nb")), // test binary safe + reply.MakeNullBulkReply(), + reply.MakeMultiBulkReply([][]byte{ + []byte("a"), + []byte("\r\n"), + }), + reply.MakeEmptyMultiBulkReply(), + } + for _, re := range replies { + result, err := ParseOne(re.ToBytes()) + if err != nil { + t.Error(err) + continue + } + if !utils.BytesEquals(result.ToBytes(), re.ToBytes()) { + t.Error("parse failed: " + string(re.ToBytes())) + } + } +} diff --git a/redis/reply/asserts/assert.go b/redis/reply/asserts/assert.go index bbd1763..e10d4af 100644 --- a/redis/reply/asserts/assert.go +++ b/redis/reply/asserts/assert.go @@ -115,6 +115,10 @@ func AssertMultiBulkReply(t *testing.T, actual redis.Reply, expected []string) { func AssertMultiBulkReplySize(t *testing.T, actual redis.Reply, expected int) { multiBulk, ok := actual.(*reply.MultiBulkReply) if !ok { + if expected == 0 && + utils.BytesEquals(actual.ToBytes(), reply.MakeEmptyMultiBulkReply().ToBytes()) { + return + } t.Errorf("expected bulk reply, actually %s, %s", actual.ToBytes(), printStack()) return } diff --git a/redis/server/pubsub_test.go b/redis/server/pubsub_test.go new file mode 100644 index 0000000..b49eb74 --- /dev/null +++ b/redis/server/pubsub_test.go @@ -0,0 +1,50 @@ +package server + +import ( + "github.com/hdt3213/godis/lib/utils" + "github.com/hdt3213/godis/pubsub" + "github.com/hdt3213/godis/redis/connection" + "github.com/hdt3213/godis/redis/parser" + "github.com/hdt3213/godis/redis/reply/asserts" + "testing" +) + +func TestPublish(t *testing.T) { + hub := pubsub.MakeHub() + channel := utils.RandString(5) + msg := utils.RandString(5) + conn := &connection.FakeConn{} + pubsub.Subscribe(hub, conn, utils.ToBytesList(channel)) + conn.Clean() // clean subscribe success + pubsub.Publish(hub, utils.ToBytesList(channel, msg)) + data := conn.Bytes() + ret, err := parser.ParseOne(data) + if err != nil { + t.Error(err) + return + } + asserts.AssertMultiBulkReply(t, ret, []string{ + "message", + channel, + msg, + }) + + // unsubscribe + pubsub.UnSubscribe(hub, conn, utils.ToBytesList(channel)) + conn.Clean() + pubsub.Publish(hub, utils.ToBytesList(channel, msg)) + data = conn.Bytes() + if len(data) > 0 { + t.Error("expect no msg") + } + + // unsubscribe all + pubsub.Subscribe(hub, conn, utils.ToBytesList(channel)) + pubsub.UnSubscribe(hub, conn, utils.ToBytesList()) + conn.Clean() + pubsub.Publish(hub, utils.ToBytesList(channel, msg)) + data = conn.Bytes() + if len(data) > 0 { + t.Error("expect no msg") + } +} diff --git a/redis/server/server.go b/redis/server/server.go index 5d1af29..347265c 100644 --- a/redis/server/server.go +++ b/redis/server/server.go @@ -12,6 +12,7 @@ import ( "github.com/hdt3213/godis/interface/db" "github.com/hdt3213/godis/lib/logger" "github.com/hdt3213/godis/lib/sync/atomic" + "github.com/hdt3213/godis/redis/connection" "github.com/hdt3213/godis/redis/parser" "github.com/hdt3213/godis/redis/reply" "io" @@ -45,7 +46,7 @@ func MakeHandler() *Handler { } } -func (h *Handler) closeClient(client *Connection) { +func (h *Handler) closeClient(client *connection.Connection) { _ = client.Close() h.db.AfterClientClose(client) h.activeConn.Delete(client) @@ -58,10 +59,10 @@ func (h *Handler) Handle(ctx context.Context, conn net.Conn) { _ = conn.Close() } - client := NewConn(conn) + client := connection.NewConn(conn) h.activeConn.Store(client, 1) - ch := parser.Parse(conn) + ch := parser.ParseStream(conn) for payload := range ch { if payload.Err != nil { if payload.Err == io.EOF || @@ -69,7 +70,7 @@ func (h *Handler) Handle(ctx context.Context, conn net.Conn) { strings.Contains(payload.Err.Error(), "use of closed network connection") { // connection closed h.closeClient(client) - logger.Info("connection closed: " + client.conn.RemoteAddr().String()) + logger.Info("connection closed: " + client.RemoteAddr().String()) return } else { // protocol err @@ -77,7 +78,7 @@ func (h *Handler) Handle(ctx context.Context, conn net.Conn) { err := client.Write(errReply.ToBytes()) if err != nil { h.closeClient(client) - logger.Info("connection closed: " + client.conn.RemoteAddr().String()) + logger.Info("connection closed: " + client.RemoteAddr().String()) return } continue @@ -107,7 +108,7 @@ func (h *Handler) Close() error { h.closing.Set(true) // TODO: concurrent wait h.activeConn.Range(func(key interface{}, val interface{}) bool { - client := key.(*Connection) + client := key.(*connection.Connection) _ = client.Close() return true }) diff --git a/redis/server/server_test.go b/redis/server/server_test.go index 12fde3a..0c69c95 100644 --- a/redis/server/server_test.go +++ b/redis/server/server_test.go @@ -5,6 +5,7 @@ import ( "github.com/hdt3213/godis/tcp" "net" "testing" + "time" ) func TestListenAndServe(t *testing.T) { @@ -39,4 +40,5 @@ func TestListenAndServe(t *testing.T) { return } closeChan <- struct{}{} + time.Sleep(time.Second) } diff --git a/tcp/echo_test.go b/tcp/echo_test.go index 4d20946..1bc211c 100644 --- a/tcp/echo_test.go +++ b/tcp/echo_test.go @@ -6,6 +6,7 @@ import ( "net" "strconv" "testing" + "time" ) func TestListenAndServe(t *testing.T) { @@ -42,5 +43,11 @@ func TestListenAndServe(t *testing.T) { return } } + _ = conn.Close() + for i := 0; i < 5; i++ { + // create idle connection + _, _ = net.Dial("tcp", addr) + } closeChan <- struct{}{} + time.Sleep(time.Second) }