You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@kvrocks.apache.org by ti...@apache.org on 2022/09/13 03:03:17 UTC
[incubator-kvrocks] branch unstable updated: Move TCL test unit/type/set to Go case (#858)
This is an automated email from the ASF dual-hosted git repository.
tison pushed a commit to branch unstable
in repository https://gitbox.apache.org/repos/asf/incubator-kvrocks.git
The following commit(s) were added to refs/heads/unstable by this push:
new 500a766 Move TCL test unit/type/set to Go case (#858)
500a766 is described below
commit 500a766334f994cc239ead05d9fc62c8f52b5123
Author: Ruixiang Tan <81...@qq.com>
AuthorDate: Tue Sep 13 11:03:13 2022 +0800
Move TCL test unit/type/set to Go case (#858)
---
tests/gocase/unit/type/set/set_test.go | 704 +++++++++++++++++++++++++++++++++
tests/gocase/util/random.go | 5 +
tests/tcl/tests/test_helper.tcl | 1 -
tests/tcl/tests/unit/type/set.tcl | 521 ------------------------
4 files changed, 709 insertions(+), 522 deletions(-)
diff --git a/tests/gocase/unit/type/set/set_test.go b/tests/gocase/unit/type/set/set_test.go
new file mode 100644
index 0000000..c578d76
--- /dev/null
+++ b/tests/gocase/unit/type/set/set_test.go
@@ -0,0 +1,704 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package set
+
+import (
+ "context"
+ "sort"
+ "strconv"
+ "testing"
+
+ "github.com/apache/incubator-kvrocks/tests/gocase/util"
+ "github.com/go-redis/redis/v9"
+ "github.com/stretchr/testify/require"
+)
+
+func CreateSet(t *testing.T, rdb *redis.Client, ctx context.Context, key string, entries []interface{}) {
+ require.NoError(t, rdb.Del(ctx, key).Err())
+ for _, entry := range entries {
+ switch entry := entry.(type) {
+ default:
+ require.NoError(t, rdb.SAdd(ctx, key, entry).Err())
+ }
+ }
+}
+
+func GetArrayUnion(arrays ...[]string) []string {
+ result := []string{}
+ var vis = make(map[string]bool)
+ for _, array := range arrays {
+ for _, value := range array {
+ _, ok := vis[value]
+ if ok {
+ continue
+ }
+ vis[value] = true
+ result = append(result, value)
+ }
+ }
+ return result
+}
+
+func TestSet(t *testing.T) {
+ srv := util.StartServer(t, map[string]string{})
+ defer srv.Close()
+ ctx := context.Background()
+ rdb := srv.NewClient()
+ defer func() { require.NoError(t, rdb.Close()) }()
+
+ t.Run("SADD, SCARD, SISMEMBER, SMISMEMBER, SMEMBERS basics - regular set", func(t *testing.T) {
+ CreateSet(t, rdb, ctx, "myset", []interface{}{"foo"})
+ require.EqualValues(t, 1, rdb.SAdd(ctx, "myset", "bar").Val())
+ require.EqualValues(t, 0, rdb.SAdd(ctx, "myset", "bar").Val())
+ require.EqualValues(t, 2, rdb.SCard(ctx, "myset").Val())
+ require.EqualValues(t, true, rdb.SIsMember(ctx, "myset", "foo").Val())
+ require.EqualValues(t, true, rdb.SIsMember(ctx, "myset", "bar").Val())
+ require.EqualValues(t, false, rdb.SIsMember(ctx, "myset", "bla").Val())
+ require.EqualValues(t, []bool{true}, rdb.SMIsMember(ctx, "myset", "foo").Val())
+ require.EqualValues(t, []bool{true, true}, rdb.SMIsMember(ctx, "myset", "foo", "bar").Val())
+ require.EqualValues(t, []bool{true, false}, rdb.SMIsMember(ctx, "myset", "foo", "bla").Val())
+ require.EqualValues(t, []bool{false, true}, rdb.SMIsMember(ctx, "myset", "bla", "foo").Val())
+ require.EqualValues(t, []bool{false}, rdb.SMIsMember(ctx, "myset", "bla").Val())
+ cmd := rdb.SMembers(ctx, "myset")
+ require.NoError(t, cmd.Err())
+ sort.Strings(cmd.Val())
+ require.EqualValues(t, []string{"bar", "foo"}, cmd.Val())
+ })
+
+ t.Run("SADD, SCARD, SISMEMBER, SMISMEMBER, SMEMBERS basics - intset", func(t *testing.T) {
+ CreateSet(t, rdb, ctx, "myset", []interface{}{17})
+ require.EqualValues(t, 1, rdb.SAdd(ctx, "myset", 16).Val())
+ require.EqualValues(t, 0, rdb.SAdd(ctx, "myset", 16).Val())
+ require.EqualValues(t, 2, rdb.SCard(ctx, "myset").Val())
+ require.EqualValues(t, true, rdb.SIsMember(ctx, "myset", 16).Val())
+ require.EqualValues(t, true, rdb.SIsMember(ctx, "myset", 17).Val())
+ require.EqualValues(t, false, rdb.SIsMember(ctx, "myset", 18).Val())
+ require.EqualValues(t, []bool{true}, rdb.SMIsMember(ctx, "myset", 16).Val())
+ require.EqualValues(t, []bool{true, true}, rdb.SMIsMember(ctx, "myset", 16, 17).Val())
+ require.EqualValues(t, []bool{true, false}, rdb.SMIsMember(ctx, "myset", 16, 18).Val())
+ require.EqualValues(t, []bool{false, true}, rdb.SMIsMember(ctx, "myset", 18, 16).Val())
+ require.EqualValues(t, []bool{false}, rdb.SMIsMember(ctx, "myset", 18).Val())
+ cmd := rdb.SMembers(ctx, "myset")
+ require.NoError(t, cmd.Err())
+ sort.Strings(cmd.Val())
+ require.EqualValues(t, []string{"16", "17"}, cmd.Val())
+ })
+
+ t.Run("SMISMEMBER against non set", func(t *testing.T) {
+ require.ErrorContains(t, rdb.LPush(ctx, "myset", "foo").Err(), "WRONGTYPE")
+ })
+
+ t.Run("SMISMEMBER non existing key", func(t *testing.T) {
+ require.EqualValues(t, []bool{false}, rdb.SMIsMember(ctx, "myset1", "foo").Val())
+ require.EqualValues(t, []bool{false, false}, rdb.SMIsMember(ctx, "myset1", "foo", "bar").Val())
+ })
+
+ t.Run("SMISMEMBER requires one or more members", func(t *testing.T) {
+ require.NoError(t, rdb.Del(ctx, "zmscoretest").Err())
+ require.NoError(t, rdb.ZAdd(ctx, "zmscoretest", redis.Z{Score: 10, Member: "x"}).Err())
+ require.NoError(t, rdb.ZAdd(ctx, "zmscoretest", redis.Z{Score: 20, Member: "y"}).Err())
+ util.ErrorRegexp(t, rdb.Do(ctx, "smismember", "zmscoretest").Err(), ".*ERR.*wrong.*number.*arg.*")
+ })
+
+ t.Run("SADD against non set", func(t *testing.T) {
+ require.NoError(t, rdb.LPush(ctx, "mylist", "foo").Err())
+ util.ErrorRegexp(t, rdb.SAdd(ctx, "mylist", "bar").Err(), ".*WRONGTYPE.*")
+ })
+
+ t.Run("SADD a non-integer against an intset", func(t *testing.T) {
+ CreateSet(t, rdb, ctx, "myset", []interface{}{1, 2, 3})
+ require.EqualValues(t, 1, rdb.SAdd(ctx, "myset", "a").Val())
+ })
+
+ t.Run("SADD an integer larger than 64 bits", func(t *testing.T) {
+ CreateSet(t, rdb, ctx, "myset", []interface{}{"213244124402402314402033402"})
+ require.EqualValues(t, true, rdb.SIsMember(ctx, "myset", "213244124402402314402033402").Val())
+ require.EqualValues(t, []bool{true}, rdb.SMIsMember(ctx, "myset", "213244124402402314402033402").Val())
+ })
+
+ t.Run("SADD overflows the maximum allowed integers in an intset", func(t *testing.T) {
+ require.NoError(t, rdb.Del(ctx, "myset").Err())
+ for i := 0; i < 512; i++ {
+ require.NoError(t, rdb.SAdd(ctx, "myset", i).Err())
+ }
+ require.EqualValues(t, 1, rdb.SAdd(ctx, "myset", 512).Val())
+ })
+
+ t.Run("Variadic SADD", func(t *testing.T) {
+ require.NoError(t, rdb.Del(ctx, "myset").Err())
+ require.EqualValues(t, 3, rdb.SAdd(ctx, "myset", "a", "b", "c").Val())
+ require.EqualValues(t, 2, rdb.SAdd(ctx, "myset", "A", "a", "b", "c", "B").Val())
+ cmd := rdb.SMembers(ctx, "myset")
+ require.NoError(t, cmd.Err())
+ sort.Strings(cmd.Val())
+ require.EqualValues(t, []string{"A", "B", "a", "b", "c"}, cmd.Val())
+ })
+
+ t.Run("SREM basics - regular set", func(t *testing.T) {
+ CreateSet(t, rdb, ctx, "myset", []interface{}{"foo", "bar", "ciao"})
+ require.EqualValues(t, 0, rdb.SRem(ctx, "myset", "qux").Val())
+ require.EqualValues(t, 1, rdb.SRem(ctx, "myset", "foo").Val())
+ cmd := rdb.SMembers(ctx, "myset")
+ require.NoError(t, cmd.Err())
+ sort.Strings(cmd.Val())
+ require.EqualValues(t, []string{"bar", "ciao"}, cmd.Val())
+ })
+
+ t.Run("SREM basics - intset", func(t *testing.T) {
+ CreateSet(t, rdb, ctx, "myset", []interface{}{3, 4, 5})
+ require.EqualValues(t, 0, rdb.SRem(ctx, "myset", 6).Val())
+ require.EqualValues(t, 1, rdb.SRem(ctx, "myset", 4).Val())
+ cmd := rdb.SMembers(ctx, "myset")
+ require.NoError(t, cmd.Err())
+ sort.Strings(cmd.Val())
+ require.EqualValues(t, []string{"3", "5"}, cmd.Val())
+ })
+
+ t.Run("SREM with multiple arguments", func(t *testing.T) {
+ CreateSet(t, rdb, ctx, "myset", []interface{}{"a", "b", "c", "d"})
+ require.EqualValues(t, 0, rdb.SRem(ctx, "myset", "k", "k", "k").Val())
+ require.EqualValues(t, 2, rdb.SRem(ctx, "myset", "b", "d", "x", "y").Val())
+ cmd := rdb.SMembers(ctx, "myset")
+ require.NoError(t, cmd.Err())
+ sort.Strings(cmd.Val())
+ require.EqualValues(t, []string{"a", "c"}, cmd.Val())
+ })
+
+ t.Run("SREM variadic version with more args needed to destroy the key", func(t *testing.T) {
+ CreateSet(t, rdb, ctx, "myset", []interface{}{1, 2, 3})
+ require.EqualValues(t, 3, rdb.SRem(ctx, "myset", 1, 2, 3, 4, 5, 6, 7, 8).Val())
+ })
+
+ t.Run("SREM variadic version with more args needed to destroy the key", func(t *testing.T) {
+ CreateSet(t, rdb, ctx, "myset", []interface{}{1, 2, 3})
+ require.EqualValues(t, 3, rdb.SRem(ctx, "myset", 1, 2, 3, 4, 5, 6, 7, 8).Val())
+ })
+
+ for _, dstype := range [2]string{"hashtable", "intset"} {
+ for i := 1; i <= 5; i++ {
+ require.NoError(t, rdb.Del(ctx, "set"+strconv.Itoa(i)).Err())
+ }
+ for i := 0; i < 200; i++ {
+ require.NoError(t, rdb.SAdd(ctx, "set1", i).Err())
+ require.NoError(t, rdb.SAdd(ctx, "set2", i+195).Err())
+ }
+ for _, i := range []int{199, 195, 100, 2000} {
+ require.NoError(t, rdb.SAdd(ctx, "set3", i).Err())
+ }
+ for i := 5; i < 200; i++ {
+ require.NoError(t, rdb.SAdd(ctx, "set4", i).Err())
+ }
+ var larger interface{}
+ larger = 200
+ require.NoError(t, rdb.SAdd(ctx, "set5", 0).Err())
+ if dstype == "hashtable" {
+ larger = "foo"
+ }
+ for i := 1; i <= 5; i++ {
+ switch larger := larger.(type) {
+ default:
+ require.NoError(t, rdb.SAdd(ctx, "set"+strconv.Itoa(i), larger).Err())
+ }
+ }
+ t.Run("SINTER with two sets - "+dstype, func(t *testing.T) {
+ cmd := rdb.SInter(ctx, "set1", "set2")
+ require.NoError(t, cmd.Err())
+ sort.Strings(cmd.Val())
+ switch larger := larger.(type) {
+ case int:
+ require.EqualValues(t, []string{"195", "196", "197", "198", "199", strconv.Itoa(larger)}, cmd.Val())
+ case string:
+ require.EqualValues(t, []string{"195", "196", "197", "198", "199", larger}, cmd.Val())
+ }
+ })
+
+ t.Run("SINTERSTORE with two sets - "+dstype, func(t *testing.T) {
+ require.NoError(t, rdb.SInterStore(ctx, "setres", "set1", "set2").Err())
+ cmd := rdb.SMembers(ctx, "setres")
+ require.NoError(t, cmd.Err())
+ sort.Strings(cmd.Val())
+
+ switch larger := larger.(type) {
+ case int:
+ require.EqualValues(t, []string{"195", "196", "197", "198", "199", strconv.Itoa(larger)}, cmd.Val())
+ case string:
+ require.EqualValues(t, []string{"195", "196", "197", "198", "199", larger}, cmd.Val())
+ }
+ })
+
+ t.Run("SUNION with two sets - "+dstype, func(t *testing.T) {
+ set1 := rdb.SMembers(ctx, "set1")
+ require.NoError(t, set1.Err())
+ set2 := rdb.SMembers(ctx, "set2")
+ require.NoError(t, set2.Err())
+ expect := GetArrayUnion(set1.Val(), set2.Val())
+ sort.Strings(expect)
+ cmd := rdb.SUnion(ctx, "set1", "set2")
+ require.NoError(t, cmd.Err())
+ sort.Strings(cmd.Val())
+ require.EqualValues(t, expect, cmd.Val())
+ })
+
+ t.Run("SUNIONSTORE with two sets - "+dstype, func(t *testing.T) {
+ set1 := rdb.SMembers(ctx, "set1")
+ require.NoError(t, set1.Err())
+ set2 := rdb.SMembers(ctx, "set2")
+ require.NoError(t, set2.Err())
+ expect := GetArrayUnion(set1.Val(), set2.Val())
+ sort.Strings(expect)
+ require.NoError(t, rdb.SUnionStore(ctx, "setres", "set1", "set2").Err())
+ cmd := rdb.SMembers(ctx, "setres")
+ require.NoError(t, cmd.Err())
+ sort.Strings(cmd.Val())
+ require.EqualValues(t, expect, cmd.Val())
+ })
+
+ t.Run("SINTER against three sets - "+dstype, func(t *testing.T) {
+ cmd := rdb.SInter(ctx, "set1", "set2", "set3")
+ require.NoError(t, cmd.Err())
+ sort.Strings(cmd.Val())
+ switch larger := larger.(type) {
+ case int:
+ require.EqualValues(t, []string{"195", "199", strconv.Itoa(larger)}, cmd.Val())
+ case string:
+ require.EqualValues(t, []string{"195", "199", larger}, cmd.Val())
+ }
+ })
+
+ t.Run("SINTERSTORE with three sets - "+dstype, func(t *testing.T) {
+ require.NoError(t, rdb.SInterStore(ctx, "setres", "set1", "set2", "set3").Err())
+ cmd := rdb.SMembers(ctx, "setres")
+ require.NoError(t, cmd.Err())
+ sort.Strings(cmd.Val())
+ switch larger := larger.(type) {
+ case int:
+ require.EqualValues(t, []string{"195", "199", strconv.Itoa(larger)}, cmd.Val())
+ case string:
+ require.EqualValues(t, []string{"195", "199", larger}, cmd.Val())
+ }
+ })
+
+ t.Run("SUNION with non existing keys - "+dstype, func(t *testing.T) {
+ set1 := rdb.SMembers(ctx, "set1")
+ require.NoError(t, set1.Err())
+ set2 := rdb.SMembers(ctx, "set2")
+ require.NoError(t, set2.Err())
+ expect := GetArrayUnion(set1.Val(), set2.Val())
+ sort.Strings(expect)
+ cmd := rdb.SUnion(ctx, "nokey1", "set1", "set2", "nokey2")
+ require.NoError(t, cmd.Err())
+ sort.Strings(cmd.Val())
+ require.EqualValues(t, expect, cmd.Val())
+ })
+
+ t.Run("SDIFF with two sets - "+dstype, func(t *testing.T) {
+ cmd := rdb.SDiff(ctx, "set1", "set4")
+ require.NoError(t, cmd.Err())
+ sort.Strings(cmd.Val())
+ require.EqualValues(t, []string{"0", "1", "2", "3", "4"}, cmd.Val())
+ })
+
+ t.Run("SDIFF with three sets - "+dstype, func(t *testing.T) {
+ cmd := rdb.SDiff(ctx, "set1", "set4", "set5")
+ require.NoError(t, cmd.Err())
+ sort.Strings(cmd.Val())
+ require.EqualValues(t, []string{"1", "2", "3", "4"}, cmd.Val())
+ })
+
+ t.Run("SDIFFSTORE with three sets - "+dstype, func(t *testing.T) {
+ require.NoError(t, rdb.SDiffStore(ctx, "setres", "set1", "set4", "set5").Err())
+ cmd := rdb.SMembers(ctx, "setres")
+ require.NoError(t, cmd.Err())
+ sort.Strings(cmd.Val())
+ require.EqualValues(t, []string{"1", "2", "3", "4"}, cmd.Val())
+ })
+ }
+
+ t.Run("SDIFF with first set empty", func(t *testing.T) {
+ require.NoError(t, rdb.Del(ctx, "set1", "set2", "set3").Err())
+ require.NoError(t, rdb.SAdd(ctx, "set2", 1, 2, 3, 4).Err())
+ require.NoError(t, rdb.SAdd(ctx, "set3", "a", "b", "c", "d").Err())
+ cmd := rdb.SDiff(ctx, "set1", "set2", "set3")
+ require.NoError(t, cmd.Err())
+ sort.Strings(cmd.Val())
+ require.EqualValues(t, []string{}, cmd.Val())
+ })
+
+ t.Run("SDIFF with same set two times", func(t *testing.T) {
+ require.NoError(t, rdb.Del(ctx, "set1").Err())
+ require.NoError(t, rdb.SAdd(ctx, "set1", "a", "b", "c", 1, 2, 3, 4, 5, 6).Err())
+ cmd := rdb.SDiff(ctx, "set1", "set1")
+ require.NoError(t, cmd.Err())
+ sort.Strings(cmd.Val())
+ require.EqualValues(t, []string{}, cmd.Val())
+ })
+
+ t.Run("SDIFF fuzzing", func(t *testing.T) {
+ for j := 0; j < 100; j++ {
+ s := make(map[string]bool)
+ var args []string
+ numSets := util.RandomInt(10) + 1
+ for i := 0; i < int(numSets); i++ {
+ numElements := util.RandomInt(100)
+ require.NoError(t, rdb.Del(ctx, "set_"+strconv.Itoa(i)).Err())
+ args = append(args, "set_"+strconv.Itoa(i))
+ for numElements > 0 {
+ ele := util.RandomValue()
+ require.NoError(t, rdb.SAdd(ctx, "set_"+strconv.Itoa(i), ele).Err())
+ if i == 0 {
+ s[ele] = true
+ } else {
+ delete(s, ele)
+ }
+ numElements -= 1
+ }
+ }
+ cmd := rdb.SDiff(ctx, args...)
+ require.NoError(t, cmd.Err())
+ sort.Strings(cmd.Val())
+ expect := make([]string, 0, 12)
+ for key := range s {
+ expect = append(expect, key)
+ }
+ sort.Strings(expect)
+ require.EqualValues(t, expect, cmd.Val())
+ }
+ })
+
+ t.Run("SINTER against non-set should throw error", func(t *testing.T) {
+ require.NoError(t, rdb.Set(ctx, "key1", "x", 0).Err())
+ util.ErrorRegexp(t, rdb.SInter(ctx, "key1", "noset").Err(), ".*WRONGTYPE.*")
+ })
+
+ t.Run("SUNION against non-set should throw error", func(t *testing.T) {
+ require.NoError(t, rdb.Set(ctx, "key1", "x", 0).Err())
+ util.ErrorRegexp(t, rdb.SUnion(ctx, "key1", "noset").Err(), ".*WRONGTYPE.*")
+ })
+
+ t.Run("SINTER should handle non existing key as empty", func(t *testing.T) {
+ require.NoError(t, rdb.Del(ctx, "set1", "set2", "set3").Err())
+ require.NoError(t, rdb.SAdd(ctx, "set1", "a", "b", "c").Err())
+ require.NoError(t, rdb.SAdd(ctx, "set2", "b", "c", "d").Err())
+ require.EqualValues(t, []string{}, rdb.SInter(ctx, "set1", "set2", "key3").Val())
+ })
+
+ t.Run("SINTER with same integer elements but different encoding", func(t *testing.T) {
+ require.NoError(t, rdb.Del(ctx, "set1", "set2").Err())
+ require.NoError(t, rdb.SAdd(ctx, "set1", 1, 2, 3).Err())
+ require.NoError(t, rdb.SAdd(ctx, "set2", 1, 2, 3, "a").Err())
+ require.NoError(t, rdb.SRem(ctx, "set2", "a").Err())
+ require.EqualValues(t, []string{"1", "2", "3"}, rdb.SInter(ctx, "set1", "set2").Val())
+ })
+
+ t.Run("SINTERSTORE against non existing keys should delete dstkey", func(t *testing.T) {
+ require.NoError(t, rdb.Set(ctx, "setres", "xxx", 0).Err())
+ require.EqualValues(t, 0, rdb.SInterStore(ctx, "setres", "foo111", "bar222").Val())
+ require.EqualValues(t, 0, rdb.Exists(ctx, "setres").Val())
+ })
+
+ t.Run("SUNIONSTORE against non existing keys should delete dstkey", func(t *testing.T) {
+ require.NoError(t, rdb.Set(ctx, "setres", "xxx", 0).Err())
+ require.EqualValues(t, 0, rdb.SUnionStore(ctx, "setres", "foo111", "bar222").Val())
+ require.EqualValues(t, 0, rdb.Exists(ctx, "setres").Val())
+ })
+
+ t.Run("SPOP basics - hashtable", func(t *testing.T) {
+ CreateSet(t, rdb, ctx, "myset", []interface{}{"a", "b", "c"})
+ var array []string
+ for i := 0; i < 3; i++ {
+ cmd := rdb.SPop(ctx, "myset")
+ require.NoError(t, cmd.Err())
+ array = append(array, cmd.Val())
+ }
+ sort.Strings(array)
+ require.EqualValues(t, []string{"a", "b", "c"}, array)
+ })
+
+ t.Run("SPOP with <count>=1 - hashtable", func(t *testing.T) {
+ CreateSet(t, rdb, ctx, "myset", []interface{}{"a", "b", "c"})
+ var array []string
+ for i := 0; i < 3; i++ {
+ cmd := rdb.SPopN(ctx, "myset", 1)
+ require.NoError(t, cmd.Err())
+ array = append(array, cmd.Val()...)
+ }
+ sort.Strings(array)
+ require.EqualValues(t, []string{"a", "b", "c"}, array)
+ require.EqualValues(t, 0, rdb.SCard(ctx, "myset").Val())
+ })
+
+ t.Run("SPOP basics - intset", func(t *testing.T) {
+ CreateSet(t, rdb, ctx, "myset", []interface{}{1, 2, 3})
+ var array []string
+ for i := 0; i < 3; i++ {
+ cmd := rdb.SPop(ctx, "myset")
+ require.NoError(t, cmd.Err())
+ array = append(array, cmd.Val())
+ }
+ sort.Strings(array)
+ require.EqualValues(t, []string{"1", "2", "3"}, array)
+ })
+
+ t.Run("SPOP with <count>=1 - intset", func(t *testing.T) {
+ CreateSet(t, rdb, ctx, "myset", []interface{}{1, 2, 3})
+ var array []string
+ for i := 0; i < 3; i++ {
+ cmd := rdb.SPopN(ctx, "myset", 1)
+ require.NoError(t, cmd.Err())
+ array = append(array, cmd.Val()...)
+ }
+ sort.Strings(array)
+ require.EqualValues(t, []string{"1", "2", "3"}, array)
+ require.EqualValues(t, 0, rdb.SCard(ctx, "myset").Val())
+ })
+
+ t.Run("SPOP with <count> hashtable", func(t *testing.T) {
+ CreateSet(t, rdb, ctx, "myset", []interface{}{"a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z"})
+ var array []string
+ var popNum = []int64{11, 9, 0, 4, 1, 0, 1, 9}
+ for _, i := range popNum {
+ cmd := rdb.SPopN(ctx, "myset", i)
+ require.NoError(t, cmd.Err())
+ array = append(array, cmd.Val()...)
+ }
+ sort.Strings(array)
+ require.EqualValues(t, []string{"a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z"}, array)
+ require.EqualValues(t, 0, rdb.SCard(ctx, "myset").Val())
+ })
+
+ t.Run("SPOP with <count> intset", func(t *testing.T) {
+ CreateSet(t, rdb, ctx, "myset", []interface{}{1, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 2, 20, 21, 22, 23, 24, 25, 26, 3, 4, 5, 6, 7, 8, 9})
+ var array []string
+ var popNum = []int64{11, 9, 0, 4, 1, 0, 1, 9}
+ for _, i := range popNum {
+ cmd := rdb.SPopN(ctx, "myset", i)
+ require.NoError(t, cmd.Err())
+ array = append(array, cmd.Val()...)
+ }
+ sort.Strings(array)
+ require.EqualValues(t, []string{"1", "10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "2", "20", "21", "22", "23", "24", "25", "26", "3", "4", "5", "6", "7", "8", "9"}, array)
+ require.EqualValues(t, 0, rdb.SCard(ctx, "myset").Val())
+ })
+
+ t.Run("SPOP using integers, testing Knuth's and Floyd's algorithm", func(t *testing.T) {
+ CreateSet(t, rdb, ctx, "myset", []interface{}{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20})
+ var popNum = []int64{1, 2, 3, 4, 10, 10}
+ var nowsize = 20
+ for _, i := range popNum {
+ require.EqualValues(t, nowsize, rdb.SCard(ctx, "myset").Val())
+ cmd := rdb.SPopN(ctx, "myset", i)
+ require.NoError(t, cmd.Err())
+ nowsize -= len(cmd.Val())
+ }
+ require.EqualValues(t, nowsize, rdb.SCard(ctx, "myset").Val())
+ })
+
+ t.Run("SPOP using integers with Knuth's algorithm", func(t *testing.T) {
+ require.EqualValues(t, []string{}, rdb.SPopN(ctx, "nonexisting_key", 100).Val())
+ })
+
+ t.Run("SPOP new implementation: code path #1", func(t *testing.T) {
+ CreateSet(t, rdb, ctx, "myset", []interface{}{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20})
+ cmd := rdb.SPopN(ctx, "myset", 30)
+ require.NoError(t, cmd.Err())
+ sort.Strings(cmd.Val())
+ require.EqualValues(t, []string{"1", "10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "2", "20", "3", "4", "5", "6", "7", "8", "9"}, cmd.Val())
+ })
+
+ t.Run("SPOP new implementation: code path #2", func(t *testing.T) {
+ CreateSet(t, rdb, ctx, "myset", []interface{}{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20})
+ cmd := rdb.SPopN(ctx, "myset", 2)
+ require.NoError(t, cmd.Err())
+ require.EqualValues(t, 2, len(cmd.Val()))
+ require.EqualValues(t, 18, rdb.SCard(ctx, "myset").Val())
+ var array = cmd.Val()
+ cmd = rdb.SMembers(ctx, "myset")
+ require.NoError(t, cmd.Err())
+ array = append(array, cmd.Val()...)
+ sort.Strings(array)
+ require.EqualValues(t, []string{"1", "10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "2", "20", "3", "4", "5", "6", "7", "8", "9"}, array)
+ })
+
+ t.Run("SPOP new implementation: code path #3", func(t *testing.T) {
+ CreateSet(t, rdb, ctx, "myset", []interface{}{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20})
+ cmd := rdb.SPopN(ctx, "myset", 18)
+ require.NoError(t, cmd.Err())
+ require.EqualValues(t, 18, len(cmd.Val()))
+ require.EqualValues(t, 2, rdb.SCard(ctx, "myset").Val())
+ var array = cmd.Val()
+ cmd = rdb.SMembers(ctx, "myset")
+ require.NoError(t, cmd.Err())
+ array = append(array, cmd.Val()...)
+ sort.Strings(array)
+ require.EqualValues(t, []string{"1", "10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "2", "20", "3", "4", "5", "6", "7", "8", "9"}, array)
+ })
+
+ t.Run("SRANDMEMBER with <count> against non existing key", func(t *testing.T) {
+ require.EqualValues(t, "", rdb.SRandMember(ctx, "nonexisting_key").Val())
+ })
+
+ SetupMove := func() {
+ require.NoError(t, rdb.Del(ctx, "myset3", "myset4").Err())
+ CreateSet(t, rdb, ctx, "myset1", []interface{}{1, "a", "b"})
+ CreateSet(t, rdb, ctx, "myset2", []interface{}{2, 3, 4})
+ }
+
+ t.Run("SMOVE basics - from regular set to intset", func(t *testing.T) {
+ // move a non-integer element to an intset should convert encoding
+ SetupMove()
+ require.EqualValues(t, true, rdb.SMove(ctx, "myset1", "myset2", "a").Val())
+ cmd := rdb.SMembers(ctx, "myset1")
+ require.NoError(t, cmd.Err())
+ sort.Strings(cmd.Val())
+ require.EqualValues(t, []string{"1", "b"}, cmd.Val())
+ cmd = rdb.SMembers(ctx, "myset2")
+ require.NoError(t, cmd.Err())
+ sort.Strings(cmd.Val())
+ require.EqualValues(t, []string{"2", "3", "4", "a"}, cmd.Val())
+ SetupMove()
+ // move an integer element should not convert the encoding
+ require.EqualValues(t, true, rdb.SMove(ctx, "myset1", "myset2", 1).Val())
+ cmd = rdb.SMembers(ctx, "myset1")
+ require.NoError(t, cmd.Err())
+ sort.Strings(cmd.Val())
+ require.EqualValues(t, []string{"a", "b"}, cmd.Val())
+ cmd = rdb.SMembers(ctx, "myset2")
+ require.NoError(t, cmd.Err())
+ sort.Strings(cmd.Val())
+ require.EqualValues(t, []string{"1", "2", "3", "4"}, cmd.Val())
+ SetupMove()
+ })
+
+ t.Run("SMOVE basics - from intset to regular set", func(t *testing.T) {
+ SetupMove()
+ require.EqualValues(t, true, rdb.SMove(ctx, "myset2", "myset1", 2).Val())
+ cmd := rdb.SMembers(ctx, "myset1")
+ require.NoError(t, cmd.Err())
+ sort.Strings(cmd.Val())
+ require.EqualValues(t, []string{"1", "2", "a", "b"}, cmd.Val())
+ cmd = rdb.SMembers(ctx, "myset2")
+ require.NoError(t, cmd.Err())
+ sort.Strings(cmd.Val())
+ require.EqualValues(t, []string{"3", "4"}, cmd.Val())
+ })
+
+ t.Run("SMOVE non existing key", func(t *testing.T) {
+ SetupMove()
+ require.EqualValues(t, false, rdb.SMove(ctx, "myset1", "myset2", "foo").Val())
+ require.EqualValues(t, false, rdb.SMove(ctx, "myset1", "myset1", "foo").Val())
+ cmd := rdb.SMembers(ctx, "myset1")
+ require.NoError(t, cmd.Err())
+ sort.Strings(cmd.Val())
+ require.EqualValues(t, []string{"1", "a", "b"}, cmd.Val())
+ cmd = rdb.SMembers(ctx, "myset2")
+ require.NoError(t, cmd.Err())
+ sort.Strings(cmd.Val())
+ require.EqualValues(t, []string{"2", "3", "4"}, cmd.Val())
+ })
+
+ t.Run("SMOVE non existing src set", func(t *testing.T) {
+ SetupMove()
+ require.EqualValues(t, false, rdb.SMove(ctx, "noset", "myset2", "foo").Val())
+ cmd := rdb.SMembers(ctx, "myset2")
+ require.NoError(t, cmd.Err())
+ sort.Strings(cmd.Val())
+ require.EqualValues(t, []string{"2", "3", "4"}, cmd.Val())
+ })
+
+ t.Run("SMOVE from regular set to non existing destination set", func(t *testing.T) {
+ SetupMove()
+ require.EqualValues(t, true, rdb.SMove(ctx, "myset1", "myset3", "a").Val())
+ cmd := rdb.SMembers(ctx, "myset1")
+ require.NoError(t, cmd.Err())
+ sort.Strings(cmd.Val())
+ require.EqualValues(t, []string{"1", "b"}, cmd.Val())
+ cmd = rdb.SMembers(ctx, "myset3")
+ require.NoError(t, cmd.Err())
+ sort.Strings(cmd.Val())
+ require.EqualValues(t, []string{"a"}, cmd.Val())
+ })
+
+ t.Run("SMOVE from intset to non existing destination set", func(t *testing.T) {
+ SetupMove()
+ require.EqualValues(t, true, rdb.SMove(ctx, "myset2", "myset3", 2).Val())
+ cmd := rdb.SMembers(ctx, "myset2")
+ require.NoError(t, cmd.Err())
+ sort.Strings(cmd.Val())
+ require.EqualValues(t, []string{"3", "4"}, cmd.Val())
+ cmd = rdb.SMembers(ctx, "myset3")
+ require.NoError(t, cmd.Err())
+ sort.Strings(cmd.Val())
+ require.EqualValues(t, []string{"2"}, cmd.Val())
+ })
+
+ t.Run("SMOVE wrong src key type", func(t *testing.T) {
+ require.NoError(t, rdb.Set(ctx, "x", 10, 0).Err())
+ util.ErrorRegexp(t, rdb.SMove(ctx, "x", "myset2", "foo").Err(), ".*WRONGTYPE.*")
+ })
+
+ t.Run("SMOVE wrong dst key type", func(t *testing.T) {
+ require.NoError(t, rdb.Set(ctx, "str", 10, 0).Err())
+ util.ErrorRegexp(t, rdb.SMove(ctx, "myset2", "str", "foo").Err(), ".*WRONGTYPE.*")
+ })
+
+ t.Run("SMOVE with identical source and destination", func(t *testing.T) {
+ require.NoError(t, rdb.Del(ctx, "set").Err())
+ require.NoError(t, rdb.SAdd(ctx, "set", "a", "b", "c").Err())
+ require.NoError(t, rdb.SMove(ctx, "set", "set", "b").Err())
+ cmd := rdb.SMembers(ctx, "set")
+ require.NoError(t, cmd.Err())
+ sort.Strings(cmd.Val())
+ require.EqualValues(t, []string{"a", "b", "c"}, cmd.Val())
+ })
+
+ t.Run("intsets implementation stress testing", func(t *testing.T) {
+ for j := 0; j < 20; j++ {
+ s := make(map[string]bool)
+ require.NoError(t, rdb.Del(ctx, "s").Err())
+ opNum := util.RandomInt(1024)
+ for i := 0; i < int(opNum); i++ {
+ data := util.RandomValue()
+ s[data] = true
+ require.NoError(t, rdb.SAdd(ctx, "s", data).Err())
+ }
+ cmd := rdb.SMembers(ctx, "s")
+ require.NoError(t, cmd.Err())
+ sort.Strings(cmd.Val())
+ expect := make([]string, 0, 1025)
+ for key := range s {
+ expect = append(expect, key)
+ }
+ sort.Strings(expect)
+ require.EqualValues(t, expect, cmd.Val())
+
+ opNum = int64(len(expect))
+ for i := 0; i < int(opNum); i++ {
+ cmd := rdb.SPop(ctx, "s")
+ require.NoError(t, cmd.Err())
+ if _, ok := s[cmd.Val()]; !ok {
+ t.FailNow()
+ }
+ delete(s, cmd.Val())
+ }
+ require.EqualValues(t, 0, rdb.SCard(ctx, "s").Val())
+ require.EqualValues(t, 0, len(s))
+ }
+ })
+}
diff --git a/tests/gocase/util/random.go b/tests/gocase/util/random.go
index c681a78..c8b949b 100644
--- a/tests/gocase/util/random.go
+++ b/tests/gocase/util/random.go
@@ -40,6 +40,11 @@ func randomSignedInt(max int32) int64 {
return rand.Int63n(int64(max)*2-1) - int64(max) + 1
}
+// Random integer in [0, max)
+func RandomInt(max int64) int64 {
+ return rand.Int63() % int64(max)
+}
+
type RandStringType int
const (
diff --git a/tests/tcl/tests/test_helper.tcl b/tests/tcl/tests/test_helper.tcl
index a0cf6d5..17629ce 100644
--- a/tests/tcl/tests/test_helper.tcl
+++ b/tests/tcl/tests/test_helper.tcl
@@ -36,7 +36,6 @@ set ::all_tests {
unit/scan
unit/type/string
unit/type/list
- unit/type/set
unit/type/zset
unit/type/hash
unit/type/bitmap
diff --git a/tests/tcl/tests/unit/type/set.tcl b/tests/tcl/tests/unit/type/set.tcl
deleted file mode 100644
index e4bc9fa..0000000
--- a/tests/tcl/tests/unit/type/set.tcl
+++ /dev/null
@@ -1,521 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements. See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership. The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License. You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied. See the License for the
-# specific language governing permissions and limitations
-# under the License.
-
-# Copyright (c) 2006-2020, Salvatore Sanfilippo
-# See bundled license file licenses/LICENSE.redis for details.
-
-# This file is copied and modified from the Redis project,
-# which started out as: https://github.com/redis/redis/blob/dbcc0a8/tests/unit/type/set.tcl
-
-start_server {
- tags {"set"}
-} {
- proc create_set {key entries} {
- r del $key
- foreach entry $entries { r sadd $key $entry }
- }
-
- test {SADD, SCARD, SISMEMBER, SMISMEMBER, SMEMBERS basics - regular set} {
- create_set myset {foo}
- #assert_encoding hashtable myset
- assert_equal 1 [r sadd myset bar]
- assert_equal 0 [r sadd myset bar]
- assert_equal 2 [r scard myset]
- assert_equal 1 [r sismember myset foo]
- assert_equal 1 [r sismember myset bar]
- assert_equal 0 [r sismember myset bla]
- assert_equal {1} [r smismember myset foo]
- assert_equal {1 1} [r smismember myset foo bar]
- assert_equal {1 0} [r smismember myset foo bla]
- assert_equal {0 1} [r smismember myset bla foo]
- assert_equal {0} [r smismember myset bla]
- assert_equal {bar foo} [lsort [r smembers myset]]
- }
-
- test {SADD, SCARD, SISMEMBER, SMISMEMBER, SMEMBERS basics - intset} {
- create_set myset {17}
- #assert_encoding intset myset
- assert_equal 1 [r sadd myset 16]
- assert_equal 0 [r sadd myset 16]
- assert_equal 2 [r scard myset]
- assert_equal 1 [r sismember myset 16]
- assert_equal 1 [r sismember myset 17]
- assert_equal 0 [r sismember myset 18]
- assert_equal {1} [r smismember myset 16]
- assert_equal {1 1} [r smismember myset 16 17]
- assert_equal {1 0} [r smismember myset 16 18]
- assert_equal {0 1} [r smismember myset 18 16]
- assert_equal {0} [r smismember myset 18]
- assert_equal {16 17} [lsort [r smembers myset]]
- }
-
- test {SMISMEMBER against non set} {
- r lpush mylist foo
- assert_error *WRONGTYPE* {r smismember mylist bar}
- }
-
- test {SMISMEMBER non existing key} {
- assert_equal {0} [r smismember myset1 foo]
- assert_equal {0 0} [r smismember myset1 foo bar]
- }
-
- test {SMISMEMBER requires one or more members} {
- r del zmscoretest
- r zadd zmscoretest 10 x
- r zadd zmscoretest 20 y
-
- catch {r smismember zmscoretest} e
- assert_match {*ERR*wrong*number*arg*} $e
- }
-
- test {SADD against non set} {
- r lpush mylist foo
- assert_error *WRONGTYPE* {r sadd mylist bar}
- }
-
- test "SADD a non-integer against an intset" {
- create_set myset {1 2 3}
- #assert_encoding intset myset
- assert_equal 1 [r sadd myset a]
- #assert_encoding hashtable myset
- }
-
- test "SADD an integer larger than 64 bits" {
- create_set myset {213244124402402314402033402}
- #assert_encoding hashtable myset
- assert_equal 1 [r sismember myset 213244124402402314402033402]
- assert_equal {1} [r smismember myset 213244124402402314402033402]
- }
-
- test "SADD overflows the maximum allowed integers in an intset" {
- r del myset
- for {set i 0} {$i < 512} {incr i} { r sadd myset $i }
- #assert_encoding intset myset
- assert_equal 1 [r sadd myset 512]
- #assert_encoding hashtable myset
- }
-
- test {Variadic SADD} {
- r del myset
- assert_equal 3 [r sadd myset a b c]
- assert_equal 2 [r sadd myset A a b c B]
- assert_equal [lsort {A a b c B}] [lsort [r smembers myset]]
- }
-
- test {SREM basics - regular set} {
- create_set myset {foo bar ciao}
- #assert_encoding hashtable myset
- assert_equal 0 [r srem myset qux]
- assert_equal 1 [r srem myset foo]
- assert_equal {bar ciao} [lsort [r smembers myset]]
- }
-
- test {SREM basics - intset} {
- create_set myset {3 4 5}
- #assert_encoding intset myset
- assert_equal 0 [r srem myset 6]
- assert_equal 1 [r srem myset 4]
- assert_equal {3 5} [lsort [r smembers myset]]
- }
-
- test {SREM with multiple arguments} {
- r del myset
- r sadd myset a b c d
- assert_equal 0 [r srem myset k k k]
- assert_equal 2 [r srem myset b d x y]
- lsort [r smembers myset]
- } {a c}
-
- test {SREM variadic version with more args needed to destroy the key} {
- r del myset
- r sadd myset 1 2 3
- r srem myset 1 2 3 4 5 6 7 8
- } {3}
-
- foreach {type} {hashtable intset} {
- for {set i 1} {$i <= 5} {incr i} {
- r del [format "set%d" $i]
- }
- for {set i 0} {$i < 200} {incr i} {
- r sadd set1 $i
- r sadd set2 [expr $i+195]
- }
- foreach i {199 195 1000 2000} {
- r sadd set3 $i
- }
- for {set i 5} {$i < 200} {incr i} {
- r sadd set4 $i
- }
- r sadd set5 0
-
- # To make sure the sets are encoded as the type we are testing -- also
- # when the VM is enabled and the values may be swapped in and out
- # while the tests are running -- an extra element is added to every
- # set that determines its encoding.
- set large 200
- if {$type eq "hashtable"} {
- set large foo
- }
-
- for {set i 1} {$i <= 5} {incr i} {
- r sadd [format "set%d" $i] $large
- }
-
- test "Generated sets must be encoded as $type" {
- for {set i 1} {$i <= 5} {incr i} {
- #assert_encoding $type [format "set%d" $i]
- }
- }
-
- test "SINTER with two sets - $type" {
- assert_equal [list 195 196 197 198 199 $large] [lsort [r sinter set1 set2]]
- }
-
- test "SINTERSTORE with two sets - $type" {
- r sinterstore setres set1 set2
- #assert_encoding $type setres
- assert_equal [list 195 196 197 198 199 $large] [lsort [r smembers setres]]
- }
-
- test "SUNION with two sets - $type" {
- set expected [lsort -uniq "[r smembers set1] [r smembers set2]"]
- assert_equal $expected [lsort [r sunion set1 set2]]
- }
-
- test "SUNIONSTORE with two sets - $type" {
- r sunionstore setres set1 set2
- #assert_encoding $type setres
- set expected [lsort -uniq "[r smembers set1] [r smembers set2]"]
- assert_equal $expected [lsort [r smembers setres]]
- }
-
- test "SINTER against three sets - $type" {
- assert_equal [list 195 199 $large] [lsort [r sinter set1 set2 set3]]
- }
-
- test "SINTERSTORE with three sets - $type" {
- r sinterstore setres set1 set2 set3
- assert_equal [list 195 199 $large] [lsort [r smembers setres]]
- }
-
- test "SUNION with non existing keys - $type" {
- set expected [lsort -uniq "[r smembers set1] [r smembers set2]"]
- assert_equal $expected [lsort [r sunion nokey1 set1 set2 nokey2]]
- }
-
- test "SDIFF with two sets - $type" {
- assert_equal {0 1 2 3 4} [lsort [r sdiff set1 set4]]
- }
-
- test "SDIFF with three sets - $type" {
- assert_equal {1 2 3 4} [lsort [r sdiff set1 set4 set5]]
- }
-
- test "SDIFFSTORE with three sets - $type" {
- r sdiffstore setres set1 set4 set5
- # When we start with intsets, we should always end with intsets.
- if {$type eq {intset}} {
- #assert_encoding intset setres
- }
- assert_equal {1 2 3 4} [lsort [r smembers setres]]
- }
- }
-
- test "SDIFF with first set empty" {
- r del set1 set2 set3
- r sadd set2 1 2 3 4
- r sadd set3 a b c d
- r sdiff set1 set2 set3
- } {}
-
- test "SDIFF with same set two times" {
- r del set1
- r sadd set1 a b c 1 2 3 4 5 6
- r sdiff set1 set1
- } {}
-
- test "SDIFF fuzzing" {
- for {set j 0} {$j < 100} {incr j} {
- unset -nocomplain s
- array set s {}
- set args {}
- set num_sets [expr {[randomInt 10]+1}]
- for {set i 0} {$i < $num_sets} {incr i} {
- set num_elements [randomInt 100]
- r del set_$i
- lappend args set_$i
- while {$num_elements} {
- set ele [randomValue]
- r sadd set_$i $ele
- if {$i == 0} {
- set s($ele) x
- } else {
- unset -nocomplain s($ele)
- }
- incr num_elements -1
- }
- }
- set result [lsort [r sdiff {*}$args]]
- assert_equal $result [lsort [array names s]]
- }
- }
-
- test "SINTER against non-set should throw error" {
- r set key1 x
- assert_error "*WRONGTYPE*" {r sinter key1 noset}
- }
-
- test "SUNION against non-set should throw error" {
- r set key1 x
- assert_error "*WRONGTYPE*" {r sunion key1 noset}
- }
-
- test "SINTER should handle non existing key as empty" {
- r del set1 set2 set3
- r sadd set1 a b c
- r sadd set2 b c d
- r sinter set1 set2 set3
- } {}
-
- test "SINTER with same integer elements but different encoding" {
- r del set1 set2
- r sadd set1 1 2 3
- r sadd set2 1 2 3 a
- r srem set2 a
- #assert_encoding intset set1
- #assert_encoding hashtable set2
- lsort [r sinter set1 set2]
- } {1 2 3}
-
- test "SINTERSTORE against non existing keys should delete dstkey" {
- r set setres xxx
- assert_equal 0 [r sinterstore setres foo111 bar222]
- assert_equal 0 [r exists setres]
- }
-
- test "SUNIONSTORE against non existing keys should delete dstkey" {
- r set setres xxx
- assert_equal 0 [r sunionstore setres foo111 bar222]
- assert_equal 0 [r exists setres]
- }
-
- foreach {type contents} {hashtable {a b c} intset {1 2 3}} {
- test "SPOP basics - $type" {
- create_set myset $contents
- #assert_encoding $type myset
- assert_equal $contents [lsort [list [r spop myset] [r spop myset] [r spop myset]]]
- assert_equal 0 [r scard myset]
- }
-
- test "SPOP with <count>=1 - $type" {
- create_set myset $contents
- #assert_encoding $type myset
- assert_equal $contents [lsort [list [r spop myset 1] [r spop myset 1] [r spop myset 1]]]
- assert_equal 0 [r scard myset]
- }
-
- # test "SRANDMEMBER - $type" {
- # create_set myset $contents
- # unset -nocomplain myset
- # array set myset {}
- # for {set i 0} {$i < 100} {incr i} {
- # set myset([r srandmember myset]) 1
- # }
- # assert_equal $contents [lsort [array names myset]]
- # }
- }
-
- foreach {type contents} {
- hashtable {a b c d e f g h i j k l m n o p q r s t u v w x y z}
- intset {1 10 11 12 13 14 15 16 17 18 19 2 20 21 22 23 24 25 26 3 4 5 6 7 8 9}
- } {
- test "SPOP with <count>" {
- create_set myset $contents
- #assert_encoding $type myset
- assert_equal $contents [lsort [concat [r spop myset 11] [r spop myset 9] [r spop myset 0] [r spop myset 4] [r spop myset 1] [r spop myset 0] [r spop myset 1] [r spop myset 0]]]
- assert_equal 0 [r scard myset]
- }
- }
-
- # As seen in intsetRandomMembers
- test "SPOP using integers, testing Knuth's and Floyd's algorithm" {
- create_set myset {1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20}
- #assert_encoding intset myset
- assert_equal 20 [r scard myset]
- r spop myset 1
- assert_equal 19 [r scard myset]
- r spop myset 2
- assert_equal 17 [r scard myset]
- r spop myset 3
- assert_equal 14 [r scard myset]
- r spop myset 10
- assert_equal 4 [r scard myset]
- r spop myset 10
- assert_equal 0 [r scard myset]
- r spop myset 1
- assert_equal 0 [r scard myset]
- } {}
-
- test "SPOP using integers with Knuth's algorithm" {
- r spop nonexisting_key 100
- } {}
-
- test "SPOP new implementation: code path #1" {
- set content {1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20}
- create_set myset $content
- set res [r spop myset 30]
- assert {[lsort $content] eq [lsort $res]}
- }
-
- test "SPOP new implementation: code path #2" {
- set content {1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20}
- create_set myset $content
- set res [r spop myset 2]
- assert {[llength $res] == 2}
- assert {[r scard myset] == 18}
- set union [concat [r smembers myset] $res]
- assert {[lsort $union] eq [lsort $content]}
- }
-
- test "SPOP new implementation: code path #3" {
- set content {1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20}
- create_set myset $content
- set res [r spop myset 18]
- assert {[llength $res] == 18}
- assert {[r scard myset] == 2}
- set union [concat [r smembers myset] $res]
- assert {[lsort $union] eq [lsort $content]}
- }
-
- test "SRANDMEMBER with <count> against non existing key" {
- r srandmember nonexisting_key 100
- } {}
-
- proc setup_move {} {
- r del myset3 myset4
- create_set myset1 {1 a b}
- create_set myset2 {2 3 4}
- #assert_encoding hashtable myset1
- #assert_encoding intset myset2
- }
-
- test "SMOVE basics - from regular set to intset" {
- # move a non-integer element to an intset should convert encoding
- setup_move
- assert_equal 1 [r smove myset1 myset2 a]
- assert_equal {1 b} [lsort [r smembers myset1]]
- assert_equal {2 3 4 a} [lsort [r smembers myset2]]
- #assert_encoding hashtable myset2
-
- # move an integer element should not convert the encoding
- setup_move
- assert_equal 1 [r smove myset1 myset2 1]
- assert_equal {a b} [lsort [r smembers myset1]]
- assert_equal {1 2 3 4} [lsort [r smembers myset2]]
- #assert_encoding intset myset2
- }
-
- test "SMOVE basics - from intset to regular set" {
- setup_move
- assert_equal 1 [r smove myset2 myset1 2]
- assert_equal {1 2 a b} [lsort [r smembers myset1]]
- assert_equal {3 4} [lsort [r smembers myset2]]
- }
-
- test "SMOVE non existing key" {
- setup_move
- assert_equal 0 [r smove myset1 myset2 foo]
- assert_equal 0 [r smove myset1 myset1 foo]
- assert_equal {1 a b} [lsort [r smembers myset1]]
- assert_equal {2 3 4} [lsort [r smembers myset2]]
- }
-
- test "SMOVE non existing src set" {
- setup_move
- assert_equal 0 [r smove noset myset2 foo]
- assert_equal {2 3 4} [lsort [r smembers myset2]]
- }
-
- test "SMOVE from regular set to non existing destination set" {
- setup_move
- assert_equal 1 [r smove myset1 myset3 a]
- assert_equal {1 b} [lsort [r smembers myset1]]
- assert_equal {a} [lsort [r smembers myset3]]
- #assert_encoding hashtable myset3
- }
-
- test "SMOVE from intset to non existing destination set" {
- setup_move
- assert_equal 1 [r smove myset2 myset3 2]
- assert_equal {3 4} [lsort [r smembers myset2]]
- assert_equal {2} [lsort [r smembers myset3]]
- #assert_encoding intset myset3
- }
-
- test "SMOVE wrong src key type" {
- r set x 10
- assert_error "*WRONGTYPE*" {r smove x myset2 foo}
- }
-
- test "SMOVE wrong dst key type" {
- r set str 10
- assert_error "*WRONGTYPE*" {r smove myset2 str foo}
- }
-
- test "SMOVE with identical source and destination" {
- r del set
- r sadd set a b c
- r smove set set b
- lsort [r smembers set]
- } {a b c}
-
- tags {slow} {
- test {intsets implementation stress testing} {
- for {set j 0} {$j < 20} {incr j} {
- unset -nocomplain s
- array set s {}
- r del s
- set len [randomInt 1024]
- for {set i 0} {$i < $len} {incr i} {
- randpath {
- set data [randomInt 65536]
- } {
- set data [randomInt 4294967296]
- } {
- set data [randomInt 18446744073709551616]
- }
- set s($data) {}
- r sadd s $data
- }
- assert_equal [lsort [r smembers s]] [lsort [array names s]]
- set len [array size s]
- for {set i 0} {$i < $len} {incr i} {
- set e [r spop s]
- if {![info exists s($e)]} {
- puts "Can't find '$e' on local array"
- puts "Local array: [lsort [r smembers s]]"
- puts "Remote array: [lsort [array names s]]"
- error "exception"
- }
- array unset s $e
- }
- assert_equal [r scard s] 0
- assert_equal [array size s] 0
- }
- }
- }
-}