// Copyright 2014 Wandoujia Inc. All Rights Reserved. // Licensed under the MIT (MIT-LICENSE.txt) license. package rdb import ( "bytes" "encoding/hex" "fmt" "math" "strconv" "strings" "testing" ) func AssertNoError(t *testing.T, err error) { if err == nil { return } t.Fatal(err) } func Assert(t *testing.T, b bool) { if b { return } t.Fatal("assertion failed") } func DecodeHexRdb(t *testing.T, s string, n int) map[string]*Entry { p, err := hex.DecodeString(strings.NewReplacer("\t", "", "\r", "", "\n", "", " ", "").Replace(s)) AssertNoError(t, err) r := bytes.NewReader(p) l := NewLoader(r) AssertNoError(t, l.LoadHeader()) entries := make(map[string]*Entry) var i int = 0 for { e, err := l.LoadEntry() AssertNoError(t, err) if e == nil { break } Assert(t, e.DB == 0) entries[string(e.Key)] = e i++ } AssertNoError(t, l.LoadChecksum()) Assert(t, r.Len() == 0) Assert(t, len(entries) == i && i == n) return entries } func getobj(t *testing.T, entries map[string]*Entry, key string) (*Entry, interface{}) { e := entries[key] Assert(t, e != nil) val, err := DecodeDump(e.ValDump) AssertNoError(t, err) return e, val } /* #!/bin/bash ./redis-cli flushall for i in 1 255 256 65535 65536 2147483647 2147483648 4294967295 4294967296 -2147483648; do ./redis-cli set string_${i} ${i} done ./redis-cli save && xxd -p -c 32 dump.rdb */ func TestLoadIntString(t *testing.T) { s := ` 524544495330303036fe00000a737472696e675f323535c1ff00000873747269 6e675f31c0010011737472696e675f343239343936373239360a343239343936 373239360011737472696e675f343239343936373239350a3432393439363732 39350012737472696e675f2d32313437343833363438c200000080000c737472 696e675f3635353335c2ffff00000011737472696e675f323134373438333634 380a32313437343833363438000c737472696e675f3635353336c20000010000 0a737472696e675f323536c100010011737472696e675f323134373438333634 37c2ffffff7fffe49d9f131fb5c3b5 ` values := []int{1, 255, 256, 65535, 65536, 2147483647, 2147483648, 4294967295, 4294967296, -2147483648} entries := DecodeHexRdb(t, s, len(values)) for _, value := range values { key := fmt.Sprintf("string_%d", value) _, obj := getobj(t, entries, key) val := obj.(String) Assert(t, bytes.Equal([]byte(val), []byte(strconv.Itoa(value)))) } } /* #!/bin/bash ./redis-cli flushall ./redis-cli set string_ttls string_ttls ./redis-cli expireat string_ttls 1500000000 ./redis-cli set string_ttlms string_ttlms ./redis-cli pexpireat string_ttlms 1500000000000 ./redis-cli save && xxd -p -c 32 dump.rdb */ func TestLoadStringTTL(t *testing.T) { s := ` 524544495330303036fe00fc0098f73e5d010000000c737472696e675f74746c 6d730c737472696e675f74746c6d73fc0098f73e5d010000000b737472696e67 5f74746c730b737472696e675f74746c73ffd15acd935a3fe949 ` expireat := uint64(1500000000000) entries := DecodeHexRdb(t, s, 2) keys := []string{"string_ttls", "string_ttlms"} for _, key := range keys { e, obj := getobj(t, entries, key) val := obj.(String) Assert(t, bytes.Equal([]byte(val), []byte(key))) Assert(t, e.ExpireAt == expireat) } } /* #!/bin/bash s="01" for ((i=0;i<15;i++)); do s=$s$s done ./redis-cli flushall ./redis-cli set string_long $s ./redis-cli save && xxd -p -c 32 dump.rdb */ func TestLoadLongString(t *testing.T) { s := ` 524544495330303036fe00000b737472696e675f6c6f6e67c342f28000010000 02303130e0ff01e0ff01e0ff01e0ff01e0ff01e0ff01e0ff01e0ff01e0ff01e0 ff01e0ff01e0ff01e0ff01e0ff01e0ff01e0ff01e0ff01e0ff01e0ff01e0ff01 e0ff01e0ff01e0ff01e0ff01e0ff01e0ff01e0ff01e0ff01e0ff01e0ff01e0ff 01e0ff01e0ff01e0ff01e0ff01e0ff01e0ff01e0ff01e0ff01e0ff01e0ff01e0 ff01e0ff01e0ff01e0ff01e0ff01e0ff01e0ff01e0ff01e0ff01e0ff01e0ff01 e0ff01e0ff01e0ff01e0ff01e0ff01e0ff01e0ff01e0ff01e0ff01e0ff01e0ff 01e0ff01e0ff01e0ff01e0ff01e0ff01e0ff01e0ff01e0ff01e0ff01e0ff01e0 ff01e0ff01e0ff01e0ff01e0ff01e0ff01e0ff01e0ff01e0ff01e0ff01e0ff01 e0ff01e0ff01e0ff01e0ff01e0ff01e0ff01e0ff01e0ff01e0ff01e0ff01e0ff 01e0ff01e0ff01e0ff01e0ff01e0ff01e0ff01e0ff01e0ff01e0ff01e0ff01e0 ff01e0ff01e0ff01e0ff01e0ff01e0ff01e0ff01e0ff01e0ff01e0ff01e0ff01 e0ff01e0ff01e0ff01e0ff01e0ff01e0ff01e0ff01e0ff01e0ff01e0ff01e0ff 01e0ff01e0ff01e0ff01e0ff01e0ff01e0ff01e0ff01e0ff01e0ff01e0ff01e0 ff01e0ff01e0ff01e0ff01e0ff01e0ff01e0ff01e0ff01e0ff01e0ff01e0ff01 e0ff01e0ff01e0ff01e0ff01e0ff01e0ff01e0ff01e0ff01e0ff01e0ff01e0ff 01e0ff01e0ff01e0ff01e0ff01e0ff01e0ff01e0ff01e0ff01e0ff01e0ff01e0 ff01e0ff01e0ff01e0ff01e0ff01e0ff01e0ff01e0ff01e0ff01e0ff01e0ff01 e0ff01e0ff01e0ff01e0ff01e0ff01e0ff01e0ff01e0ff01e0ff01e0ff01e0ff 01e0ff01e0ff01e0ff01e0ff01e0ff01e0ff01e0ff01e0ff01e0ff01e0ff01e0 ff01e0ff01e0ff01e0ff01e0ff01e0ff01e0ff01e0ff01e0ff01e0ff01e0ff01 e0ff01e0ff01e0ff01e0ff01e0ff01e0ff01e0ff01e0ff01e0ff01e0ff01e0ff 01e0ff01e0ff01e0ff01e0ff01e0ff01e0ff01e0ff01e0ff01e0ff01e0ff01e0 ff01e0ff01e0ff01e0ff01e0ff01e0ff01e0ff01e0ff01e0ff01e0ff01e0ff01 e0ff01e0ff01e0ff01e0ff01e03201013031ffdfdb02bd6d5da5e6 ` entries := DecodeHexRdb(t, s, 1) _, obj := getobj(t, entries, "string_long") val := []byte(obj.(String)) for i := 0; i < (1 << 15); i++ { var c uint8 = '0' if i%2 != 0 { c = '1' } Assert(t, val[i] == c) } } /* #!/bin/bash ./redis-cli flushall for ((i=0;i<256;i++)); do ./redis-cli rpush list_lzf 0 ./redis-cli rpush list_lzf 1 done ./redis-cli save && xxd -p -c 32 dump.rdb */ func TestLoadListZipmap(t *testing.T) { s := ` 524544495330303036fe000a086c6973745f6c7a66c31f440b040b0400000820 0306000200f102f202e0ff03e1ff07e1ff07e1d90701f2ffff6a1c2d51c02301 16 ` entries := DecodeHexRdb(t, s, 1) _, obj := getobj(t, entries, "list_lzf") val := obj.(List) Assert(t, len(val) == 512) for i := 0; i < 256; i++ { var s string = "0" if i%2 != 0 { s = "1" } Assert(t, string(val[i]) == s) } } /* #!/bin/bash ./redis-cli flushall for ((i=0;i<32;i++)); do ./redis-cli rpush list ${i} done ./redis-cli save && xxd -p -c 32 dump.rdb */ func TestLoadList(t *testing.T) { s := ` 524544495330303036fe0001046c69737420c000c001c002c003c004c005c006 c007c008c009c00ac00bc00cc00dc00ec00fc010c011c012c013c014c015c016 c017c018c019c01ac01bc01cc01dc01ec01fff756ea1fa90adefe3 ` entries := DecodeHexRdb(t, s, 1) _, obj := getobj(t, entries, "list") val := obj.(List) Assert(t, len(val) == 32) for i := 0; i < 32; i++ { Assert(t, string(val[i]) == strconv.Itoa(i)) } } /* #!/bin/bash ./redis-cli flushall for ((i=0;i<16;i++)); do ./redis-cli sadd set1 ${i} done for ((i=0;i<32;i++)); do ./redis-cli sadd set2 ${i} done ./redis-cli save && xxd -p -c 32 dump.rdb */ func TestLoadSetAndSetIntset(t *testing.T) { s := ` 524544495330303036fe0002047365743220c016c00dc01bc012c01ac004c014 c002c017c01dc01cc013c019c01ec008c006c000c001c007c00fc009c01fc00e c003c00ac015c010c00bc018c011c00cc0050b04736574312802000000100000 0000000100020003000400050006000700080009000a000b000c000d000e000f 00ff3a0a9697324d19c3 ` entries := DecodeHexRdb(t, s, 2) _, obj1 := getobj(t, entries, "set1") val1 := obj1.(Set) set1 := make(map[string]bool) for _, mem := range val1 { set1[string(mem)] = true } Assert(t, len(set1) == 16) Assert(t, len(set1) == len(val1)) for i := 0; i < 16; i++ { _, ok := set1[strconv.Itoa(i)] Assert(t, ok) } _, obj2 := getobj(t, entries, "set2") val2 := obj2.(Set) set2 := make(map[string]bool) for _, mem := range val2 { set2[string(mem)] = true } Assert(t, len(set2) == 32) Assert(t, len(set2) == len(val2)) for i := 0; i < 32; i++ { _, ok := set2[strconv.Itoa(i)] Assert(t, ok) } } /* #!/bin/bash ./redis-cli flushall for ((i=0;i<16;i++)); do ./redis-cli hset hash1 ${i} done for ((i=-16;i<16;i++)); do ./redis-cli hset hash2 ${i} done ./redis-cli save && xxd -p -c 32 dump.rdb */ func TestLoadHashAndHashZiplist(t *testing.T) { s := ` 524544495330303036fe000405686173683220c00dc00dc0fcc0fcc0ffc0ffc0 04c004c002c002c0fbc0fbc0f0c0f0c0f9c0f9c008c008c0fac0fac006c006c0 00c000c001c001c0fec0fec007c007c0f6c0f6c00fc00fc009c009c0f7c0f7c0 fdc0fdc0f1c0f1c0f2c0f2c0f3c0f3c00ec00ec003c003c00ac00ac00bc00bc0 f8c0f8c00cc00cc0f5c0f5c0f4c0f4c005c0050d056861736831405151000000 4d000000200000f102f102f202f202f302f302f402f402f502f502f602f602f7 02f702f802f802f902f902fa02fa02fb02fb02fc02fc02fd02fd02fe0d03fe0d 03fe0e03fe0e03fe0f03fe0fffffa423d3036c15e534 ` entries := DecodeHexRdb(t, s, 2) _, obj1 := getobj(t, entries, "hash1") val1 := obj1.(Hash) hash1 := make(map[string]string) for _, ent := range val1 { hash1[string(ent.Field)] = string(ent.Value) } Assert(t, len(hash1) == 16) Assert(t, len(hash1) == len(val1)) for i := 0; i < 16; i++ { s := strconv.Itoa(i) Assert(t, hash1[s] == s) } _, obj2 := getobj(t, entries, "hash2") val2 := obj2.(Hash) hash2 := make(map[string]string) for _, ent := range val2 { hash2[string(ent.Field)] = string(ent.Value) } Assert(t, len(hash2) == 32) Assert(t, len(hash2) == len(val2)) for i := -16; i < 16; i++ { s := strconv.Itoa(i) Assert(t, hash2[s] == s) } } /* #!/bin/bash ./redis-cli flushall for ((i=0;i<16;i++)); do ./redis-cli zadd zset1 ${i} ${i} done for ((i=0;i<32;i++)); do ./redis-cli zadd zset2 -${i} ${i} done ./redis-cli save && xxd -p -c 32 dump.rdb */ func TestLoadZSetAndZSetZiplist(t *testing.T) { s := ` 524544495330303036fe0003057a7365743220c016032d3232c00d032d3133c0 1b032d3237c012032d3138c01a032d3236c004022d34c014032d3230c002022d 32c017032d3233c01d032d3239c01c032d3238c013032d3139c019032d3235c0 1e032d3330c008022d38c006022d36c000022d30c001022d31c007022d37c009 022d39c00f032d3135c01f032d3331c00e032d3134c003022d33c00a032d3130 c015032d3231c010032d3136c00b032d3131c018032d3234c011032d3137c00c 032d3132c005022d350c057a736574314051510000004d000000200000f102f1 02f202f202f302f302f402f402f502f502f602f602f702f702f802f802f902f9 02fa02fa02fb02fb02fc02fc02fd02fd02fe0d03fe0d03fe0e03fe0e03fe0f03 fe0fffff2addedbf4f5a8f93 ` entries := DecodeHexRdb(t, s, 2) _, obj1 := getobj(t, entries, "zset1") val1 := obj1.(ZSet) zset1 := make(map[string]float64) for _, ent := range val1 { zset1[string(ent.Member)] = ent.Score } Assert(t, len(zset1) == 16) Assert(t, len(zset1) == len(val1)) for i := 0; i < 16; i++ { s := strconv.Itoa(i) score, ok := zset1[s] Assert(t, ok) Assert(t, math.Abs(score-float64(i)) < 1e-10) } _, obj2 := getobj(t, entries, "zset2") val2 := obj2.(ZSet) zset2 := make(map[string]float64) for _, ent := range val2 { zset2[string(ent.Member)] = ent.Score } Assert(t, len(zset2) == 32) Assert(t, len(zset2) == len(val2)) for i := 0; i < 32; i++ { s := strconv.Itoa(i) score, ok := zset2[s] Assert(t, ok) Assert(t, math.Abs(score+float64(i)) < 1e-10) } }