525 lines
11 KiB
Go
525 lines
11 KiB
Go
|
package memcache
|
||
|
|
||
|
import (
|
||
|
"bytes"
|
||
|
"encoding/json"
|
||
|
"errors"
|
||
|
test "go-common/library/cache/memcache/test"
|
||
|
"testing"
|
||
|
"time"
|
||
|
|
||
|
"github.com/bouk/monkey"
|
||
|
"github.com/gogo/protobuf/proto"
|
||
|
)
|
||
|
|
||
|
var s = []string{"test", "test1"}
|
||
|
var c Conn
|
||
|
|
||
|
var item = &Item{
|
||
|
Key: "test",
|
||
|
Value: []byte("test"),
|
||
|
Flags: FlagRAW,
|
||
|
Expiration: 60,
|
||
|
cas: 0,
|
||
|
}
|
||
|
|
||
|
var item2 = &Item{
|
||
|
Key: "test1",
|
||
|
Value: []byte("test"),
|
||
|
Flags: 0,
|
||
|
Expiration: 1000,
|
||
|
cas: 0,
|
||
|
}
|
||
|
|
||
|
var item3 = &Item{
|
||
|
Key: "test2",
|
||
|
Value: []byte("0"),
|
||
|
Flags: 0,
|
||
|
Expiration: 60,
|
||
|
cas: 0,
|
||
|
}
|
||
|
|
||
|
type TestItem struct {
|
||
|
Name string
|
||
|
Age int
|
||
|
}
|
||
|
|
||
|
func (t *TestItem) Compare(nt *TestItem) bool {
|
||
|
return t.Name == nt.Name && t.Age == nt.Age
|
||
|
}
|
||
|
|
||
|
func prepareEnv(t *testing.T) {
|
||
|
if c != nil {
|
||
|
return
|
||
|
}
|
||
|
var err error
|
||
|
cnop := DialConnectTimeout(time.Duration(2 * time.Second))
|
||
|
rdop := DialReadTimeout(time.Duration(2 * time.Second))
|
||
|
wrop := DialWriteTimeout(time.Duration(2 * time.Second))
|
||
|
c, err = Dial("tcp", testMemcacheAddr, cnop, rdop, wrop)
|
||
|
if err != nil {
|
||
|
t.Errorf("Dial() error(%v)", err)
|
||
|
t.FailNow()
|
||
|
}
|
||
|
c.Delete("test")
|
||
|
c.Delete("test1")
|
||
|
c.Delete("test2")
|
||
|
}
|
||
|
|
||
|
func TestRaw(t *testing.T) {
|
||
|
prepareEnv(t)
|
||
|
if err := c.Set(item); err != nil {
|
||
|
t.Errorf("conn.Store() error(%v)", err)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func TestAdd(t *testing.T) {
|
||
|
var (
|
||
|
key = "test_add"
|
||
|
item = &Item{
|
||
|
Key: key,
|
||
|
Value: []byte("0"),
|
||
|
Flags: 0,
|
||
|
Expiration: 60,
|
||
|
cas: 0,
|
||
|
}
|
||
|
)
|
||
|
prepareEnv(t)
|
||
|
c.Delete(key)
|
||
|
if err := c.Add(item); err != nil {
|
||
|
t.Errorf("c.Add() error(%v)", err)
|
||
|
}
|
||
|
if err := c.Add(item); err != ErrNotStored {
|
||
|
t.Errorf("c.Add() error(%v)", err)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func TestSetErr(t *testing.T) {
|
||
|
prepareEnv(t)
|
||
|
//set
|
||
|
st := &TestItem{Name: "jsongzip", Age: 10}
|
||
|
itemx := &Item{Key: "jsongzip", Object: st}
|
||
|
if err := c.Set(itemx); err != ErrItem {
|
||
|
t.Errorf("conn.Set() error(%v)", err)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func TestSetErr2(t *testing.T) {
|
||
|
prepareEnv(t)
|
||
|
//set
|
||
|
itemx := &Item{Key: "jsongzip", Flags: FlagJSON | FlagGzip}
|
||
|
if err := c.Set(itemx); err != ErrItem {
|
||
|
t.Errorf("conn.Set() error(%v)", err)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func TestSetErr3(t *testing.T) {
|
||
|
prepareEnv(t)
|
||
|
//set
|
||
|
itemx := &Item{Key: "jsongzip", Value: []byte("test"), Flags: FlagJSON}
|
||
|
if err := c.Set(itemx); err != ErrItem {
|
||
|
t.Errorf("conn.Set() error(%v)", err)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func TestJSONGzip(t *testing.T) {
|
||
|
prepareEnv(t)
|
||
|
//set
|
||
|
st := &TestItem{Name: "jsongzip", Age: 10}
|
||
|
itemx := &Item{Key: "jsongzip", Object: st, Flags: FlagJSON | FlagGzip}
|
||
|
if err := c.Set(itemx); err != nil {
|
||
|
t.Errorf("conn.Set() error(%v)", err)
|
||
|
}
|
||
|
if r, err := c.Get("jsongzip"); err != nil {
|
||
|
t.Errorf("conn.Get() error(%v)", err)
|
||
|
} else {
|
||
|
var nst TestItem
|
||
|
scanAndCompare(t, r, st, &nst)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func TestJSON(t *testing.T) {
|
||
|
prepareEnv(t)
|
||
|
st := &TestItem{Name: "json", Age: 10}
|
||
|
itemx := &Item{Key: "json", Object: st, Flags: FlagJSON}
|
||
|
if err := c.Set(itemx); err != nil {
|
||
|
t.Errorf("conn.Set() error(%v)", err)
|
||
|
}
|
||
|
if r, err := c.Get("json"); err != nil {
|
||
|
t.Errorf("conn.Get() error(%v)", err)
|
||
|
} else {
|
||
|
var nst TestItem
|
||
|
scanAndCompare(t, r, st, &nst)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func BenchmarkJSON(b *testing.B) {
|
||
|
st := &TestItem{Name: "json", Age: 10}
|
||
|
itemx := &Item{Key: "json", Object: st, Flags: FlagJSON}
|
||
|
var (
|
||
|
eb bytes.Buffer
|
||
|
je *json.Encoder
|
||
|
ir bytes.Reader
|
||
|
jd *json.Decoder
|
||
|
jr reader
|
||
|
nst test.TestItem
|
||
|
)
|
||
|
jd = json.NewDecoder(&jr)
|
||
|
je = json.NewEncoder(&eb)
|
||
|
eb.Grow(_encodeBuf)
|
||
|
// NOTE reuse bytes.Buffer internal buf
|
||
|
// DON'T concurrency call Scan
|
||
|
b.ResetTimer()
|
||
|
for i := 0; i < b.N; i++ {
|
||
|
eb.Reset()
|
||
|
if err := je.Encode(itemx.Object); err != nil {
|
||
|
return
|
||
|
}
|
||
|
data := eb.Bytes()
|
||
|
ir.Reset(data)
|
||
|
jr.Reset(&ir)
|
||
|
jd.Decode(&nst)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func BenchmarkProtobuf(b *testing.B) {
|
||
|
st := &test.TestItem{Name: "protobuf", Age: 10}
|
||
|
itemx := &Item{Key: "protobuf", Object: st, Flags: FlagJSON}
|
||
|
var (
|
||
|
eb bytes.Buffer
|
||
|
nst test.TestItem
|
||
|
ped *proto.Buffer
|
||
|
)
|
||
|
ped = proto.NewBuffer(eb.Bytes())
|
||
|
eb.Grow(_encodeBuf)
|
||
|
b.ResetTimer()
|
||
|
for i := 0; i < b.N; i++ {
|
||
|
ped.Reset()
|
||
|
pb, ok := itemx.Object.(proto.Message)
|
||
|
if !ok {
|
||
|
return
|
||
|
}
|
||
|
if err := ped.Marshal(pb); err != nil {
|
||
|
return
|
||
|
}
|
||
|
data := ped.Bytes()
|
||
|
ped.SetBuf(data)
|
||
|
ped.Unmarshal(&nst)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func TestGob(t *testing.T) {
|
||
|
prepareEnv(t)
|
||
|
st := &TestItem{Name: "gob", Age: 10}
|
||
|
itemx := &Item{Key: "gob", Object: st, Flags: FlagGOB}
|
||
|
if err := c.Set(itemx); err != nil {
|
||
|
t.Errorf("conn.Set() error(%v)", err)
|
||
|
}
|
||
|
if r, err := c.Get("gob"); err != nil {
|
||
|
t.Errorf("conn.Get() error(%v)", err)
|
||
|
} else {
|
||
|
var nst TestItem
|
||
|
scanAndCompare(t, r, st, &nst)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func TestGobGzip(t *testing.T) {
|
||
|
prepareEnv(t)
|
||
|
st := &TestItem{Name: "gobgzip", Age: 10}
|
||
|
itemx := &Item{Key: "gobgzip", Object: st, Flags: FlagGOB | FlagGzip}
|
||
|
if err := c.Set(itemx); err != nil {
|
||
|
t.Errorf("conn.Set() error(%v)", err)
|
||
|
}
|
||
|
if r, err := c.Get("gobgzip"); err != nil {
|
||
|
t.Errorf("conn.Get() error(%v)", err)
|
||
|
} else {
|
||
|
var nst TestItem
|
||
|
scanAndCompare(t, r, st, &nst)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func TestGzip(t *testing.T) {
|
||
|
prepareEnv(t)
|
||
|
st := &TestItem{Name: "gzip", Age: 123}
|
||
|
itemx := &Item{Key: "gzip", Object: st, Flags: FlagGOB | FlagGzip}
|
||
|
if err := c.Set(itemx); err != nil {
|
||
|
t.Errorf("conn.Set() error(%v)", err)
|
||
|
}
|
||
|
if r, err := c.Get("gzip"); err != nil {
|
||
|
t.Errorf("conn.Get() error(%v)", err)
|
||
|
} else {
|
||
|
var nst TestItem
|
||
|
scanAndCompare(t, r, st, &nst)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func TestProtobuf(t *testing.T) {
|
||
|
prepareEnv(t)
|
||
|
var (
|
||
|
err error
|
||
|
// value []byte
|
||
|
r *Item
|
||
|
nst test.TestItem
|
||
|
)
|
||
|
st := &test.TestItem{Name: "proto", Age: 3021}
|
||
|
itemx := &Item{Key: "proto", Object: st, Flags: FlagProtobuf}
|
||
|
if err = c.Set(itemx); err != nil {
|
||
|
t.Errorf("conn.Set() error(%v)", err)
|
||
|
}
|
||
|
if r, err = c.Get("proto"); err != nil {
|
||
|
t.Errorf("conn.Get() error(%v)", err)
|
||
|
}
|
||
|
if err = c.Scan(r, &nst); err != nil {
|
||
|
t.Errorf("decode() error(%v)", err)
|
||
|
t.FailNow()
|
||
|
} else {
|
||
|
scanAndCompare2(t, r, st, &nst)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func TestProtobufGzip(t *testing.T) {
|
||
|
prepareEnv(t)
|
||
|
var (
|
||
|
err error
|
||
|
// value []byte
|
||
|
r *Item
|
||
|
nst test.TestItem
|
||
|
)
|
||
|
st := &test.TestItem{Name: "protogzip", Age: 3021}
|
||
|
itemx := &Item{Key: "protogzip", Object: st, Flags: FlagProtobuf | FlagGzip}
|
||
|
if err = c.Set(itemx); err != nil {
|
||
|
t.Errorf("conn.Set() error(%v)", err)
|
||
|
}
|
||
|
if r, err = c.Get("protogzip"); err != nil {
|
||
|
t.Errorf("conn.Get() error(%v)", err)
|
||
|
}
|
||
|
if err = c.Scan(r, &nst); err != nil {
|
||
|
t.Errorf("decode() error(%v)", err)
|
||
|
t.FailNow()
|
||
|
} else {
|
||
|
scanAndCompare2(t, r, st, &nst)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func TestGet(t *testing.T) {
|
||
|
prepareEnv(t)
|
||
|
// get
|
||
|
if r, err := c.Get("test"); err != nil {
|
||
|
t.Errorf("conn.Get() error(%v)", err)
|
||
|
} else if r.Key != "test" || !bytes.Equal(r.Value, []byte("test")) || r.Flags != 0 {
|
||
|
t.Error("conn.Get() error, value")
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func TestGetHasErr(t *testing.T) {
|
||
|
prepareEnv(t)
|
||
|
|
||
|
st := &TestItem{Name: "json", Age: 10}
|
||
|
itemx := &Item{Key: "test", Object: st, Flags: FlagJSON}
|
||
|
c.Set(itemx)
|
||
|
|
||
|
expected := errors.New("some error")
|
||
|
monkey.Patch(scanGetReply, func(line []byte, item *Item) (size int, err error) {
|
||
|
return 0, expected
|
||
|
})
|
||
|
|
||
|
if _, err := c.Get("test"); err.Error() != expected.Error() {
|
||
|
t.Errorf("conn.Get() unexpected error(%v)", err)
|
||
|
}
|
||
|
if err := c.(*conn).err; err.Error() != expected.Error() {
|
||
|
t.Errorf("unexpected error(%v)", err)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func TestGet2(t *testing.T) {
|
||
|
prepareEnv(t)
|
||
|
// get not exist
|
||
|
if _, err := c.Get("not_exist"); err != ErrNotFound {
|
||
|
t.Errorf("conn.Get() error(%v)", err)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func TestGetMulti(t *testing.T) {
|
||
|
prepareEnv(t)
|
||
|
// getMulti
|
||
|
if _, err := c.GetMulti(s); err != nil {
|
||
|
t.Errorf("conn.GetMulti() error(%v)", err)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func TestGetMulti2(t *testing.T) {
|
||
|
prepareEnv(t)
|
||
|
//set
|
||
|
if err := c.Set(item); err != nil {
|
||
|
t.Errorf("conn.Set() error(%v)", err)
|
||
|
}
|
||
|
if err := c.Set(item2); err != nil {
|
||
|
t.Errorf("conn.Set() error(%v)", err)
|
||
|
}
|
||
|
if res, err := c.GetMulti(s); err != nil {
|
||
|
t.Errorf("conn.Get() error(%v)", err)
|
||
|
} else {
|
||
|
if len(res) != 2 {
|
||
|
t.Error("conn.Get() error, length", len(res))
|
||
|
}
|
||
|
reply := res["test"]
|
||
|
compareItem2(t, reply, item)
|
||
|
reply = res["test1"]
|
||
|
compareItem2(t, reply, item2)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func TestIncrement(t *testing.T) {
|
||
|
// set
|
||
|
if err := c.Set(item3); err != nil {
|
||
|
t.Errorf("conn.Set() error(%v)", err)
|
||
|
}
|
||
|
// incr
|
||
|
if d, err := c.Increment("test2", 4); err != nil {
|
||
|
t.Errorf("conn.Set() error(%v)", err)
|
||
|
} else {
|
||
|
if d != 4 {
|
||
|
t.Error("conn.IncrDecr value error")
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func TestDecrement(t *testing.T) {
|
||
|
// decr
|
||
|
if d, err := c.Decrement("test2", 3); err != nil {
|
||
|
t.Errorf("conn.Store() error(%v)", err)
|
||
|
} else {
|
||
|
if d != 1 {
|
||
|
t.Error("conn.IncrDecr value error", d)
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func TestTouch(t *testing.T) {
|
||
|
// touch
|
||
|
if err := c.Touch("test2", 1); err != nil {
|
||
|
t.Errorf("conn.Touch error(%v)", err)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func TestCompareAndSwap(t *testing.T) {
|
||
|
prepareEnv(t)
|
||
|
if err := c.Set(item3); err != nil {
|
||
|
t.Errorf("conn.Set() error(%v)", err)
|
||
|
}
|
||
|
//cas
|
||
|
if r, err := c.Get("test2"); err != nil {
|
||
|
t.Errorf("conn.Get() error(%v)", err)
|
||
|
} else {
|
||
|
r.Value = []byte("fuck")
|
||
|
if err := c.CompareAndSwap(r); err != nil {
|
||
|
t.Errorf("conn.CompareAndSwap() error(%v)", err)
|
||
|
}
|
||
|
if r, err := c.Get("test2"); err != nil {
|
||
|
t.Errorf("conn.Get() error(%v)", err)
|
||
|
} else {
|
||
|
itemx := &Item{Key: "test2", Value: []byte("fuck"), Flags: 0}
|
||
|
compareItem2(t, r, itemx)
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func TestReplace(t *testing.T) {
|
||
|
prepareEnv(t)
|
||
|
if err := c.Set(item); err != nil {
|
||
|
t.Errorf("conn.Set() error(%v)", err)
|
||
|
}
|
||
|
if r, err := c.Get("test"); err != nil {
|
||
|
t.Errorf("conn.Get() error(%v)", err)
|
||
|
} else {
|
||
|
r.Value = []byte("go")
|
||
|
if err := c.Replace(r); err != nil {
|
||
|
t.Errorf("conn.CompareAndSwap() error(%v)", err)
|
||
|
}
|
||
|
if r, err := c.Get("test"); err != nil {
|
||
|
t.Errorf("conn.Get() error(%v)", err)
|
||
|
} else {
|
||
|
itemx := &Item{Key: "test", Value: []byte("go"), Flags: 0}
|
||
|
compareItem2(t, r, itemx)
|
||
|
}
|
||
|
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func scanAndCompare(t *testing.T, item *Item, st *TestItem, nst *TestItem) {
|
||
|
if err := c.Scan(item, nst); err != nil {
|
||
|
t.Errorf("decode() error(%v)", err)
|
||
|
t.FailNow()
|
||
|
}
|
||
|
if !st.Compare(nst) {
|
||
|
t.Errorf("st: %v, use of closed network connection nst: %v", st, &nst)
|
||
|
t.FailNow()
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func scanAndCompare2(t *testing.T, item *Item, st *test.TestItem, nst *test.TestItem) {
|
||
|
if err := c.Scan(item, nst); err != nil {
|
||
|
t.Errorf("decode() error(%v)", err)
|
||
|
t.FailNow()
|
||
|
}
|
||
|
if st.Age != nst.Age || st.Name != nst.Name {
|
||
|
t.Errorf("st: %v, use of closed network connection nst: %v", st, &nst)
|
||
|
t.FailNow()
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func compareItem2(t *testing.T, r, item *Item) {
|
||
|
if r.Key != item.Key || !bytes.Equal(r.Value, item.Value) || r.Flags != item.Flags {
|
||
|
t.Error("conn.Get() error, value")
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func Test_legalKey(t *testing.T) {
|
||
|
type args struct {
|
||
|
key string
|
||
|
}
|
||
|
tests := []struct {
|
||
|
name string
|
||
|
args args
|
||
|
want bool
|
||
|
}{
|
||
|
{
|
||
|
name: "test empty key",
|
||
|
want: false,
|
||
|
},
|
||
|
{
|
||
|
name: "test too large key",
|
||
|
args: args{func() string {
|
||
|
var data []byte
|
||
|
for i := 0; i < 255; i++ {
|
||
|
data = append(data, 'k')
|
||
|
}
|
||
|
return string(data)
|
||
|
}()},
|
||
|
want: false,
|
||
|
},
|
||
|
{
|
||
|
name: "test invalid char",
|
||
|
args: args{"hello world"},
|
||
|
want: false,
|
||
|
},
|
||
|
{
|
||
|
name: "test invalid char",
|
||
|
args: args{string([]byte{0x7f})},
|
||
|
want: false,
|
||
|
},
|
||
|
{
|
||
|
name: "test normal key",
|
||
|
args: args{"hello"},
|
||
|
want: true,
|
||
|
},
|
||
|
}
|
||
|
for _, tt := range tests {
|
||
|
t.Run(tt.name, func(t *testing.T) {
|
||
|
if got := legalKey(tt.args.key); got != tt.want {
|
||
|
t.Errorf("legalKey() = %v, want %v", got, tt.want)
|
||
|
}
|
||
|
})
|
||
|
}
|
||
|
}
|