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
-            }
-        }
-    }
-}