You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
524 lines
11 KiB
524 lines
11 KiB
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) |
|
} |
|
}) |
|
} |
|
}
|
|
|