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/10/17 00:01:41 UTC

[incubator-kvrocks] branch unstable updated: Move TCL test unit/geo to Go case (#998)

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 8c370355 Move TCL test unit/geo to Go case (#998)
8c370355 is described below

commit 8c3703557fe842ef2f81c20d91c0ded5dfd927d4
Author: Ruixiang Tan <81...@qq.com>
AuthorDate: Mon Oct 17 08:01:35 2022 +0800

    Move TCL test unit/geo to Go case (#998)
---
 tests/gocase/unit/geo/geo_test.go | 315 ++++++++++++++++++++++++
 tests/tcl/tests/test_helper.tcl   |   1 -
 tests/tcl/tests/unit/geo.tcl      | 487 --------------------------------------
 3 files changed, 315 insertions(+), 488 deletions(-)

diff --git a/tests/gocase/unit/geo/geo_test.go b/tests/gocase/unit/geo/geo_test.go
new file mode 100644
index 00000000..9dcfb3c8
--- /dev/null
+++ b/tests/gocase/unit/geo/geo_test.go
@@ -0,0 +1,315 @@
+/*
+ * 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 geo
+
+import (
+	"context"
+	"math"
+	"math/rand"
+	"reflect"
+	"sort"
+	"strconv"
+	"testing"
+	"time"
+
+	"github.com/apache/incubator-kvrocks/tests/gocase/util"
+	"github.com/go-redis/redis/v9"
+	"github.com/stretchr/testify/require"
+)
+
+func geoDegrad(deg float64) float64 {
+	return deg * math.Atan(1) * 8 / 360
+}
+
+func geoRandomPoint() (float64, float64) {
+	lon := (-180 + rand.Float64()*360)
+	lat := (-70 + rand.Float64()*140)
+	return lon, lat
+}
+
+func geoDistance(lon1d, lat1d, lon2d, lat2d float64) float64 {
+	lon1r := geoDegrad(lon1d)
+	lat1r := geoDegrad(lat1d)
+	lon2r := geoDegrad(lon2d)
+	lat2r := geoDegrad(lat2d)
+	v := math.Sin((lon2r - lon1r) / 2)
+	u := math.Sin((lat2r - lat1r) / 2)
+	return 2.0 * 6372797.560856 * math.Asin(math.Sqrt(u*u+math.Cos(lat1r)*math.Cos(lat2r)*v*v))
+}
+
+func compareLists(list1, list2 []string) []string {
+	vis := make(map[string]int)
+	var result []string
+	for i := 0; i < len(list1); i++ {
+		j := i
+		for j+1 < len(list1) && list1[j+1] == list1[i] {
+			j++
+		}
+		vis[list1[i]] += 1
+		i = j
+	}
+	for i := 0; i < len(list2); i++ {
+		j := i
+		for j+1 < len(list2) && list2[j+1] == list2[i] {
+			j++
+		}
+		vis[list2[i]] += 1
+		i = j
+	}
+	for _, i := range list1 {
+		if val, ok := vis[i]; ok && val == 1 {
+			result = append(result, i)
+		}
+	}
+	for _, i := range list2 {
+		if val, ok := vis[i]; ok && val == 1 {
+			result = append(result, i)
+		}
+	}
+	return result
+}
+
+func TestGeo(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("GEOADD create", func(t *testing.T) {
+		require.EqualValues(t, 1, rdb.GeoAdd(ctx, "nyc", &redis.GeoLocation{Name: "lic market", Longitude: -73.9454966, Latitude: 40.747533}).Val())
+	})
+
+	t.Run("GEOADD update", func(t *testing.T) {
+		require.EqualValues(t, 0, rdb.GeoAdd(ctx, "nyc", &redis.GeoLocation{Name: "lic market", Longitude: -73.9454966, Latitude: 40.747533}).Val())
+	})
+
+	t.Run("GEOADD invalid coordinates", func(t *testing.T) {
+		require.ErrorContains(t, rdb.Do(ctx, "geoadd", "nyc", -73.9454966, 40.747533, "lic market", "foo", "bar", "luck market").Err(), "valid")
+	})
+
+	t.Run("GEOADD multi add", func(t *testing.T) {
+		require.EqualValues(t, 6, rdb.GeoAdd(ctx, "nyc", &redis.GeoLocation{Name: "central park n/q/r", Longitude: -73.9733487, Latitude: 40.7648057},
+			&redis.GeoLocation{Name: "union square", Longitude: -73.9903085, Latitude: 40.7362513},
+			&redis.GeoLocation{Name: "wtc one", Longitude: -74.0131604, Latitude: 40.7126674},
+			&redis.GeoLocation{Name: "jfk", Longitude: -73.7858139, Latitude: 40.6428986},
+			&redis.GeoLocation{Name: "q4", Longitude: -73.9375699, Latitude: 40.7498929},
+			&redis.GeoLocation{Name: "4545", Longitude: -73.9564142, Latitude: 40.7480973}).Val())
+	})
+
+	t.Run("Check geoset values", func(t *testing.T) {
+		require.EqualValues(t, []redis.Z([]redis.Z{{Score: 1.79187397205302e+15, Member: "wtc one"}, {Score: 1.791875485187452e+15, Member: "union square"}, {Score: 1.791875761332224e+15, Member: "central park n/q/r"}, {Score: 1.791875796750882e+15, Member: "4545"}, {Score: 1.791875804419201e+15, Member: "lic market"}, {Score: 1.791875830079666e+15, Member: "q4"}, {Score: 1.791895905559723e+15, Member: "jfk"}}), rdb.ZRangeWithScores(ctx, "nyc", 0, -1).Val())
+	})
+
+	t.Run("GEORADIUS simple (sorted)", func(t *testing.T) {
+		require.EqualValues(t, []redis.GeoLocation([]redis.GeoLocation{{Name: "central park n/q/r", Longitude: 0, Latitude: 0, Dist: 0, GeoHash: 0}, {Name: "4545", Longitude: 0, Latitude: 0, Dist: 0, GeoHash: 0}, {Name: "union square", Longitude: 0, Latitude: 0, Dist: 0, GeoHash: 0}}), rdb.GeoRadius(ctx, "nyc", -73.9798091, 40.7598464, &redis.GeoRadiusQuery{Radius: 3, Unit: "km", Sort: "asc"}).Val())
+	})
+
+	t.Run("GEORADIUS with COUNT", func(t *testing.T) {
+		require.EqualValues(t, []redis.GeoLocation([]redis.GeoLocation{{Name: "central park n/q/r", Longitude: 0, Latitude: 0, Dist: 0, GeoHash: 0}, {Name: "4545", Longitude: 0, Latitude: 0, Dist: 0, GeoHash: 0}, {Name: "union square", Longitude: 0, Latitude: 0, Dist: 0, GeoHash: 0}}), rdb.GeoRadius(ctx, "nyc", -73.9798091, 40.7598464, &redis.GeoRadiusQuery{Radius: 10, Unit: "km", Sort: "asc", Count: 3}).Val())
+	})
+
+	t.Run("GEORADIUS HUGE, (redis issue #2767)", func(t *testing.T) {
+		require.NoError(t, rdb.GeoAdd(ctx, "users", &redis.GeoLocation{Name: "user_000000", Longitude: -47.271613776683807, Latitude: -54.534504198047678}).Err())
+		require.EqualValues(t, 1, len(rdb.GeoRadius(ctx, "users", 0, 0, &redis.GeoRadiusQuery{Radius: 50000, Unit: "km", WithCoord: true}).Val()))
+	})
+
+	t.Run("GEORADIUSBYMEMBER simple (sorted)", func(t *testing.T) {
+		require.EqualValues(t, []redis.GeoLocation([]redis.GeoLocation{{Name: "wtc one", Longitude: 0, Latitude: 0, Dist: 0, GeoHash: 0}, {Name: "union square", Longitude: 0, Latitude: 0, Dist: 0, GeoHash: 0}, {Name: "central park n/q/r", Longitude: 0, Latitude: 0, Dist: 0, GeoHash: 0}, {Name: "4545", Longitude: 0, Latitude: 0, Dist: 0, GeoHash: 0}, {Name: "lic market", Longitude: 0, Latitude: 0, Dist: 0, GeoHash: 0}}), rdb.GeoRadiusByMember(ctx, "nyc", "wtc one", &redis.GeoRadiusQuery{Radius: [...]
+	})
+
+	t.Run("GEOHASH is able to return geohash strings", func(t *testing.T) {
+		require.NoError(t, rdb.Del(ctx, "points").Err())
+		require.NoError(t, rdb.GeoAdd(ctx, "points", &redis.GeoLocation{Name: "test", Longitude: -5.6, Latitude: 42.6}).Err())
+		require.EqualValues(t, []string([]string{"ezs42e44yx0"}), rdb.GeoHash(ctx, "points", "test").Val())
+	})
+
+	t.Run("GEOPOS simple", func(t *testing.T) {
+		require.NoError(t, rdb.Del(ctx, "points").Err())
+		require.NoError(t, rdb.GeoAdd(ctx, "points", &redis.GeoLocation{Name: "a", Longitude: 10, Latitude: 20}, &redis.GeoLocation{Name: "b", Longitude: 30, Latitude: 40}).Err())
+		cmd := rdb.GeoPos(ctx, "points", "a", "b")
+		require.Less(t, math.Abs(cmd.Val()[0].Longitude-10), 0.001)
+		require.Less(t, math.Abs(cmd.Val()[0].Latitude-20), 0.001)
+		require.Less(t, math.Abs(cmd.Val()[1].Longitude-30), 0.001)
+		require.Less(t, math.Abs(cmd.Val()[1].Latitude-40), 0.001)
+	})
+
+	t.Run("GEOPOS missing element", func(t *testing.T) {
+		require.NoError(t, rdb.Del(ctx, "points").Err())
+		require.NoError(t, rdb.GeoAdd(ctx, "points", &redis.GeoLocation{Name: "a", Longitude: 10, Latitude: 20}, &redis.GeoLocation{Name: "b", Longitude: 30, Latitude: 40}).Err())
+		require.Nil(t, rdb.GeoPos(ctx, "points", "a", "x", "b").Val()[1])
+	})
+
+	t.Run("GEODIST simple & unit", func(t *testing.T) {
+		require.NoError(t, rdb.Del(ctx, "points").Err())
+		require.NoError(t, rdb.GeoAdd(ctx, "points", &redis.GeoLocation{Name: "Palermo", Longitude: 13.361389, Latitude: 38.115556}, &redis.GeoLocation{Name: "Catania", Longitude: 15.087269, Latitude: 37.502669}).Err())
+		posVal := rdb.GeoDist(ctx, "points", "Palermo", "Catania", "m").Val()
+		require.Greater(t, posVal, 166274.0)
+		require.Less(t, posVal, 166275.0)
+		distVal := rdb.GeoDist(ctx, "points", "Palermo", "Catania", "km").Val()
+		require.Greater(t, distVal, 166.2)
+		require.Less(t, distVal, 166.3)
+	})
+
+	t.Run("GEODIST missing elements", func(t *testing.T) {
+		require.NoError(t, rdb.Del(ctx, "points").Err())
+		require.NoError(t, rdb.GeoAdd(ctx, "points", &redis.GeoLocation{Name: "Palermo", Longitude: 13.361389, Latitude: 38.115556}, &redis.GeoLocation{Name: "Catania", Longitude: 15.087269, Latitude: 37.502669}).Err())
+		require.EqualValues(t, 0, rdb.GeoDist(ctx, "points", "Palermo", "Agrigento", "").Val())
+		require.EqualValues(t, 0, rdb.GeoDist(ctx, "points", "Ragusa", "Agrigento", "").Val())
+		require.EqualValues(t, 0, rdb.GeoDist(ctx, "empty_key", "Palermo", "Catania", "").Val())
+	})
+
+	t.Run("GEORADIUS STORE option: syntax error", func(t *testing.T) {
+		require.NoError(t, rdb.Del(ctx, "points").Err())
+		require.NoError(t, rdb.GeoAdd(ctx, "points", &redis.GeoLocation{Name: "Palermo", Longitude: 13.361389, Latitude: 38.115556}, &redis.GeoLocation{Name: "Catania", Longitude: 15.087269, Latitude: 37.502669}).Err())
+		require.ErrorContains(t, rdb.Do(ctx, "georadius", "points", 13.361389, 38.115556, 50, "km", "store").Err(), "syntax")
+	})
+
+	t.Run("GEORADIUS missing key", func(t *testing.T) {
+		require.NoError(t, rdb.Del(ctx, "points").Err())
+		require.EqualValues(t, []redis.GeoLocation([]redis.GeoLocation{}), rdb.GeoRadius(ctx, "points", 13.361389, 38.115556, &redis.GeoRadiusQuery{Radius: 50, Unit: "km"}).Val())
+	})
+
+	t.Run("GEORANGE STORE option: plain usage", func(t *testing.T) {
+		require.NoError(t, rdb.Del(ctx, "points").Err())
+		require.NoError(t, rdb.GeoAdd(ctx, "points", &redis.GeoLocation{Name: "Palermo", Longitude: 13.361389, Latitude: 38.115556}, &redis.GeoLocation{Name: "Catania", Longitude: 15.087269, Latitude: 37.502669}).Err())
+		rdb.GeoRadiusStore(ctx, "points", 13.361389, 38.115556, &redis.GeoRadiusQuery{Radius: 500, Unit: "km", Store: "points2"})
+		require.EqualValues(t, rdb.ZRange(ctx, "points", 0, -1).Val(), rdb.ZRange(ctx, "points2", 0, -1).Val())
+	})
+
+	type item struct {
+		seed int64
+		km   int64
+		lon  float64
+		lat  float64
+	}
+	regressionVectors := []item{
+		{1482225976969, 7083, 81.634948934258375, 30.561509253718668},
+		{1482340074151, 5416, -70.863281847379767, -46.347003465679947},
+		{1499014685896, 6064, -89.818768962202014, -40.463868561416803},
+		{1412, 156, 149.29737817929004, 15.95807862745508},
+		{441574, 143, 59.235461856813856, 66.269555127373678},
+		{160645, 187, -101.88575239939883, 49.061997951502917},
+		{750269, 154, -90.187939661642517, 66.615930412251487},
+		{342880, 145, 163.03472387745728, 64.012747720821181},
+		{729955, 143, 137.86663517256579, 63.986745399416776},
+		{939895, 151, 59.149620271823181, 65.204186651485145},
+		{1412, 156, 149.29737817929004, 15.95807862745508},
+		{564862, 149, 84.062063109158544, -65.685403922426232},
+		{1546032440391, 16751, -1.8175081637769495, 20.665668878082954},
+	}
+
+	t.Run("GEOADD + GEORANGE randomized test", func(t *testing.T) {
+		for attempt := 0; attempt < 30; attempt++ {
+			var debuginfo string
+			if attempt < len(regressionVectors) {
+				rand.Seed(regressionVectors[attempt].seed)
+				debuginfo += "rand seed is " + strconv.FormatInt(regressionVectors[attempt].seed, 10)
+			} else {
+				tmp := time.Now().UnixNano()
+				rand.Seed(tmp)
+				debuginfo += "rand seed is " + strconv.FormatInt(tmp, 10)
+			}
+			require.NoError(t, rdb.Del(ctx, "mypoints").Err())
+			var radiusKm int64
+			if util.RandomInt(10) == 0 {
+				radiusKm = util.RandomInt(50000) + 10
+			} else {
+				radiusKm = util.RandomInt(200) + 10
+			}
+			if attempt < len(regressionVectors) {
+				radiusKm = regressionVectors[attempt].km
+			}
+			radiusM := radiusKm * 1000
+			searchLon, searchLat := geoRandomPoint()
+			if attempt < len(regressionVectors) {
+				searchLon = regressionVectors[attempt].lon
+				searchLat = regressionVectors[attempt].lat
+			}
+			debuginfo += "Search area: " + strconv.FormatFloat(searchLon, 'f', 10, 64) + "," + strconv.FormatFloat(searchLat, 'f', 10, 64) + " " + strconv.FormatInt(radiusKm, 10) + " km"
+			var result []string
+			var argvs []*redis.GeoLocation
+			for j := 0; j < 20000; j++ {
+				lon, lat := geoRandomPoint()
+				argvs = append(argvs, &redis.GeoLocation{Longitude: lon, Latitude: lat, Name: "place:" + strconv.Itoa(j)})
+				distance := geoDistance(lon, lat, searchLon, searchLat)
+				if distance < float64(radiusM) {
+					result = append(result, "place:"+strconv.Itoa(j))
+				}
+				debuginfo += "place:" + strconv.FormatInt(int64(j), 10) + " " + strconv.FormatInt(int64(lon), 10) + " " + strconv.FormatInt(int64(lat), 10) + " " + strconv.FormatInt(int64(distance)/1000, 10) + " km"
+			}
+			require.NoError(t, rdb.GeoAdd(ctx, "mypoints", argvs...).Err())
+			cmd := rdb.GeoRadius(ctx, "mypoints", searchLon, searchLat, &redis.GeoRadiusQuery{Radius: float64(radiusKm), Unit: "km"})
+			sort.Strings(result)
+			var res []string
+			for _, i := range cmd.Val() {
+				res = append(res, i.Name)
+			}
+			sort.Strings(res)
+			equal := reflect.DeepEqual(res, result)
+			testResult := true
+			if !equal {
+				roundingErrors := 0
+				diff := compareLists(res, result)
+				for _, i := range diff {
+					cmd := rdb.GeoPos(ctx, "mypoints", i)
+					mydist := geoDistance(cmd.Val()[0].Longitude, cmd.Val()[0].Latitude, searchLon, searchLat) / 1000
+					if mydist/float64(radiusKm) > 0.999 {
+						roundingErrors += 1
+						continue
+					}
+					if mydist < float64(radiusM) {
+						roundingErrors += 1
+						continue
+					}
+				}
+				if len(diff) == roundingErrors {
+					equal = true
+				}
+			}
+			if !equal {
+				diff := compareLists(res, result)
+				t.Log("Redis: ", res)
+				t.Log("Gotest: ", result)
+				t.Log("Diff: ", diff)
+				t.Log("debuginfo: ", debuginfo)
+				vis := make(map[string]int)
+				for _, i := range result {
+					vis[i] += 1
+				}
+				for _, i := range diff {
+					var where string
+					if _, ok := vis[i]; ok {
+						where = "(only in Go test)"
+					} else {
+						where = "(only in Kvrocks)"
+					}
+					cmd := rdb.GeoPos(ctx, "mypoints", i)
+					require.NoError(t, cmd.Err())
+					mydis := geoDistance(cmd.Val()[0].Longitude, cmd.Val()[0].Latitude, searchLon, searchLat) / 1000
+					t.Logf("%v -> %v %v %v", i, rdb.GeoPos(ctx, "mypoints", i).Val()[0], mydis, where)
+				}
+				testResult = false
+			}
+			if !testResult {
+				require.FailNow(t, "not equal")
+			}
+		}
+	})
+}
diff --git a/tests/tcl/tests/test_helper.tcl b/tests/tcl/tests/test_helper.tcl
index f66cd9d0..3b5c87ab 100644
--- a/tests/tcl/tests/test_helper.tcl
+++ b/tests/tcl/tests/test_helper.tcl
@@ -35,7 +35,6 @@ source tests/support/util.tcl
 set ::all_tests {
     unit/type/list
     unit/type/stream
-    unit/geo
     integration/slotmigrate
 }
 
diff --git a/tests/tcl/tests/unit/geo.tcl b/tests/tcl/tests/unit/geo.tcl
deleted file mode 100644
index 3eedc0d9..00000000
--- a/tests/tcl/tests/unit/geo.tcl
+++ /dev/null
@@ -1,487 +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/geo.tcl
-
-# Helper functions to simulate search-in-radius in the Tcl side in order to
-# verify the Redis implementation with a fuzzy test.
-proc geo_degrad deg {expr {$deg*atan(1)*8/360}}
-
-proc geo_distance {lon1d lat1d lon2d lat2d} {
-    set lon1r [geo_degrad $lon1d]
-    set lat1r [geo_degrad $lat1d]
-    set lon2r [geo_degrad $lon2d]
-    set lat2r [geo_degrad $lat2d]
-    set v [expr {sin(($lon2r - $lon1r) / 2)}]
-    set u [expr {sin(($lat2r - $lat1r) / 2)}]
-    expr {2.0 * 6372797.560856 * \
-            asin(sqrt($u * $u + cos($lat1r) * cos($lat2r) * $v * $v))}
-}
-
-proc geo_random_point {lonvar latvar} {
-    upvar 1 $lonvar lon
-    upvar 1 $latvar lat
-    # Note that the actual latitude limit should be -85 to +85, we restrict
-    # the test to -70 to +70 since in this range the algorithm is more precise
-    # while outside this range occasionally some element may be missing.
-    set lon [expr {-180 + rand()*360}]
-    set lat [expr {-70 + rand()*140}]
-}
-
-# Return elements non common to both the lists.
-# This code is from http://wiki.tcl.tk/15489
-proc compare_lists {List1 List2} {
-   set DiffList {}
-   foreach Item $List1 {
-      if {[lsearch -exact $List2 $Item] == -1} {
-         lappend DiffList $Item
-      }
-   }
-   foreach Item $List2 {
-      if {[lsearch -exact $List1 $Item] == -1} {
-         if {[lsearch -exact $DiffList $Item] == -1} {
-            lappend DiffList $Item
-         }
-      }
-   }
-   return $DiffList
-}
-
-# The following list represents sets of random seed, search position
-# and radius that caused bugs in the past. It is used by the randomized
-# test later as a starting point. When the regression vectors are scanned
-# the code reverts to using random data.
-#
-# The format is: seed km lon lat
-set regression_vectors {
-    {1482225976969 7083 81.634948934258375 30.561509253718668}
-    {1482340074151 5416 -70.863281847379767 -46.347003465679947}
-    {1499014685896 6064 -89.818768962202014 -40.463868561416803}
-    {1412 156 149.29737817929004 15.95807862745508}
-    {441574 143 59.235461856813856 66.269555127373678}
-    {160645 187 -101.88575239939883 49.061997951502917}
-    {750269 154 -90.187939661642517 66.615930412251487}
-    {342880 145 163.03472387745728 64.012747720821181}
-    {729955 143 137.86663517256579 63.986745399416776}
-    {939895 151 59.149620271823181 65.204186651485145}
-    {1412 156 149.29737817929004 15.95807862745508}
-    {564862 149 84.062063109158544 -65.685403922426232}
-    {1546032440391 16751 -1.8175081637769495 20.665668878082954}
-}
-set rv_idx 0
-
-start_server {tags {"geo"}} {
-    test {GEOADD create} {
-        r geoadd nyc -73.9454966 40.747533 "lic market"
-    } {1}
-
-    test {GEOADD update} {
-        r geoadd nyc -73.9454966 40.747533 "lic market"
-    } {0}
-
-    # test {GEOADD update with CH option} {
-    #     assert_equal 1 [r geoadd nyc CH 40.747533 -73.9454966 "lic market"]
-    #     lassign [lindex [r geopos nyc "lic market"] 0] x1 y1
-    #     assert {abs($x1) - 40.747 < 0.001}
-    #     assert {abs($y1) - 73.945 < 0.001}
-    # } {}
-
-    # test {GEOADD update with NX option} {
-    #     assert_equal 0 [r geoadd nyc NX -73.9454966 40.747533 "lic market"]
-    #     lassign [lindex [r geopos nyc "lic market"] 0] x1 y1
-    #     assert {abs($x1) - 40.747 < 0.001}
-    #     assert {abs($y1) - 73.945 < 0.001}
-    # } {}
-
-    # test {GEOADD update with XX option} {
-    #     assert_equal 0 [r geoadd nyc XX -83.9454966 40.747533 "lic market"]
-    #     lassign [lindex [r geopos nyc "lic market"] 0] x1 y1
-    #     assert {abs($x1) - 83.945 < 0.001}
-    #     assert {abs($y1) - 40.747 < 0.001}
-    # } {}
-
-    # test {GEOADD update with CH NX option} {
-    #     r geoadd nyc CH NX -73.9454966 40.747533 "lic market"
-    # } {0}
-
-    # test {GEOADD update with CH XX option} {
-    #     r geoadd nyc CH XX -73.9454966 40.747533 "lic market"
-    # } {1}
-
-    # test {GEOADD update with XX NX option will return syntax error} {
-    #     catch {
-    #         r geoadd nyc xx nx -73.9454966 40.747533 "lic market"
-    #     } err
-    #     set err
-    # } {ERR*syntax*}
-
-    # test {GEOADD update with invalid option} {
-    #     catch {
-    #         r geoadd nyc ch xx foo -73.9454966 40.747533 "lic market"
-    #     } err
-    #     set err
-    # } {ERR*syntax*}
-
-    test {GEOADD invalid coordinates} {
-        catch {
-            r geoadd nyc -73.9454966 40.747533 "lic market" \
-                foo bar "luck market"
-        } err
-        set err
-    } {*valid*}
-
-    test {GEOADD multi add} {
-        r geoadd nyc -73.9733487 40.7648057 "central park n/q/r" -73.9903085 40.7362513 "union square" -74.0131604 40.7126674 "wtc one" -73.7858139 40.6428986 "jfk" -73.9375699 40.7498929 "q4" -73.9564142 40.7480973 4545
-    } {6}
-
-    test {Check geoset values} {
-        r zrange nyc 0 -1 withscores
-    } {{wtc one} 1791873972053020 {union square} 1791875485187452 {central park n/q/r} 1791875761332224 4545 1791875796750882 {lic market} 1791875804419201 q4 1791875830079666 jfk 1791895905559723}
-
-    test {GEORADIUS simple (sorted)} {
-        r georadius nyc -73.9798091 40.7598464 3 km asc
-    } {{central park n/q/r} 4545 {union square}}
-
-    # test {GEOSEARCH simple (sorted)} {
-    #     r geosearch nyc fromlonlat -73.9798091 40.7598464 bybox 6 6 km asc
-    # } {{central park n/q/r} 4545 {union square} {lic market}}
-
-    # test {GEOSEARCH FROMLONLAT and FROMMEMBER cannot exist at the same time} {
-    #     catch {r geosearch nyc fromlonlat -73.9798091 40.7598464 frommember xxx bybox 6 6 km asc} e
-    #     set e
-    # } {ERR*syntax*}
-
-    # test {GEOSEARCH FROMLONLAT and FROMMEMBER one must exist} {
-    #     catch {r geosearch nyc bybox 3 3 km asc desc withhash withdist withcoord} e
-    #     set e
-    # } {ERR*exactly one of FROMMEMBER or FROMLONLAT*}
-
-    # test {GEOSEARCH BYRADIUS and BYBOX cannot exist at the same time} {
-    #     catch {r geosearch nyc fromlonlat -73.9798091 40.7598464 byradius 3 km bybox 3 3 km asc} e
-    #     set e
-    # } {ERR*syntax*}
-
-    # test {GEOSEARCH BYRADIUS and BYBOX one must exist} {
-    #     catch {r geosearch nyc fromlonlat -73.9798091 40.7598464 asc desc withhash withdist withcoord} e
-    #     set e
-    # } {ERR*exactly one of BYRADIUS and BYBOX*}
-
-    # test {GEOSEARCH with STOREDIST option} {
-    #     catch {r geosearch nyc fromlonlat -73.9798091 40.7598464 bybox 6 6 km asc storedist} e
-    #     set e
-    # } {ERR*syntax*}
-
-    # test {GEORADIUS withdist (sorted)} {
-    #     r georadius nyc -73.9798091 40.7598464 3 km withdist asc
-    # } {{{central park n/q/r} 0.7750} {4545 2.3651} {{union square} 2.7697}}
-
-    # test {GEOSEARCH withdist (sorted)} {
-    #     r geosearch nyc fromlonlat -73.9798091 40.7598464 bybox 6 6 km withdist asc
-    # } {{{central park n/q/r} 0.7750} {4545 2.3651} {{union square} 2.7697} {{lic market} 3.1991}}
-
-    test {GEORADIUS with COUNT} {
-        r georadius nyc -73.9798091 40.7598464 10 km COUNT 3
-    } {{central park n/q/r} 4545 {union square}}
-
-    # test {GEORADIUS with ANY not sorted by default} {
-    #     r georadius nyc -73.9798091 40.7598464 10 km COUNT 3 ANY
-    # } {{wtc one} {union square} {central park n/q/r}}
-
-    # test {GEORADIUS with ANY sorted by ASC} {
-    #     r georadius nyc -73.9798091 40.7598464 10 km COUNT 3 ANY ASC
-    # } {{central park n/q/r} {union square} {wtc one}}
-
-    # test {GEORADIUS with ANY but no COUNT} {
-    #     catch {r georadius nyc -73.9798091 40.7598464 10 km ANY ASC} e
-    #     set e
-    # } {ERR*ANY*requires*COUNT*}
-
-    # test {GEORADIUS with COUNT but missing integer argument} {
-    #     catch {r georadius nyc -73.9798091 40.7598464 10 km COUNT} e
-    #     set e
-    # } {ERR*syntax*}
-
-    # test {GEORADIUS with COUNT DESC} {
-    #     r georadius nyc -73.9798091 40.7598464 10 km COUNT 2 DESC
-    # } {{wtc one} q4}
-
-    test {GEORADIUS HUGE, issue #2767} {
-        r geoadd users -47.271613776683807 -54.534504198047678 user_000000
-        llength [r GEORADIUS users 0 0 50000 km WITHCOORD]
-    } {1}
-
-    test {GEORADIUSBYMEMBER simple (sorted)} {
-        r georadiusbymember nyc "wtc one" 7 km
-    } {{wtc one} {union square} {central park n/q/r} 4545 {lic market}}
-
-    # test {GEOSEARCH FROMMEMBER simple (sorted)} {
-    #     r geosearch nyc frommember "wtc one" bybox 14 14 km
-    # } {{wtc one} {union square} {central park n/q/r} 4545 {lic market} q4}
-
-    # test {GEOSEARCH vs GEORADIUS} {
-    #     r del Sicily
-    #     r geoadd Sicily 13.361389 38.115556 "Palermo" 15.087269 37.502669 "Catania"
-    #     r geoadd Sicily 12.758489 38.788135 "edge1"   17.241510 38.788135 "eage2"
-    #     set ret1 [r georadius Sicily 15 37 200 km asc]
-    #     assert_equal $ret1 {Catania Palermo}
-    #     set ret2 [r geosearch Sicily fromlonlat 15 37 bybox 400 400 km asc]
-    #     assert_equal $ret2 {Catania Palermo eage2 edge1}
-    # }
-
-    # test {GEOSEARCH non square, long and narrow} {
-    #     r del Sicily
-    #     r geoadd Sicily 12.75 37.00 "test1"
-    #     r geoadd Sicily 12.75 36.50 "test2"
-    #     r geoadd Sicily 13.00 36.50 "test3"
-    #     # box height=2km width=400km
-    #     set ret1 [r geosearch Sicily fromlonlat 15 37 bybox 2 400 km]
-    #     assert_equal $ret1 {test1}
-
-    #     # Add a western Hemisphere point
-    #     r geoadd Sicily -1 37.00 "test3"
-    #     set ret2 [r geosearch Sicily fromlonlat 15 37 bybox 2 3000 km asc]
-    #     assert_equal $ret2 {test1 test3}
-    # }
-
-    # test {GEORADIUSBYMEMBER withdist (sorted)} {
-    #     r georadiusbymember nyc "wtc one" 7 km withdist
-    # } {{{wtc one} 0.0000} {{union square} 3.2544} {{central park n/q/r} 6.7000} {4545 6.1975} {{lic market} 6.8969}}
-
-    test {GEOHASH is able to return geohash strings} {
-        # Example from Wikipedia.
-        r del points
-        r geoadd points -5.6 42.6 test
-        lindex [r geohash points test] 0
-    } {ezs42e44yx0}
-
-    test {GEOPOS simple} {
-        r del points
-        r geoadd points 10 20 a 30 40 b
-        lassign [lindex [r geopos points a b] 0] x1 y1
-        lassign [lindex [r geopos points a b] 1] x2 y2
-        assert {abs($x1 - 10) < 0.001}
-        assert {abs($y1 - 20) < 0.001}
-        assert {abs($x2 - 30) < 0.001}
-        assert {abs($y2 - 40) < 0.001}
-    }
-
-    test {GEOPOS missing element} {
-        r del points
-        r geoadd points 10 20 a 30 40 b
-        lindex [r geopos points a x b] 1
-    } {}
-
-    test {GEODIST simple & unit} {
-        r del points
-        r geoadd points 13.361389 38.115556 "Palermo" \
-                        15.087269 37.502669 "Catania"
-        set m [r geodist points Palermo Catania]
-        assert {$m > 166274 && $m < 166275}
-        set km [r geodist points Palermo Catania km]
-        assert {$km > 166.2 && $km < 166.3}
-    }
-
-    test {GEODIST missing elements} {
-        r del points
-        r geoadd points 13.361389 38.115556 "Palermo" \
-                        15.087269 37.502669 "Catania"
-        set m [r geodist points Palermo Agrigento]
-        assert {$m eq {}}
-        set m [r geodist points Ragusa Agrigento]
-        assert {$m eq {}}
-        set m [r geodist empty_key Palermo Catania]
-        assert {$m eq {}}
-    }
-
-    test {GEORADIUS STORE option: syntax error} {
-        r del points
-        r geoadd points 13.361389 38.115556 "Palermo" \
-                        15.087269 37.502669 "Catania"
-        catch {r georadius points 13.361389 38.115556 50 km store} e
-        set e
-    } {*syntax*}
-
-    test {GEORADIUS missing key} {
-        r del points
-        assert_equal {} [r georadius points 13.361389 38.115556 50 km]
-    }
-    
-    # test {GEOSEARCHSTORE STORE option: syntax error} {
-    #     catch {r geosearchstore abc points fromlonlat 13.361389 38.115556 byradius 50 km store abc} e
-    #     set e
-    # } {*syntax*}
-
-    # test {GEORANGE STORE option: incompatible options} {
-    #     r del points
-    #     r geoadd points 13.361389 38.115556 "Palermo" \
-    #                     15.087269 37.502669 "Catania"
-    #     catch {r georadius points 13.361389 38.115556 50 km store points2 withdist} e
-    #     assert_match {*ERR*} $e
-    #     catch {r georadius points 13.361389 38.115556 50 km store points2 withhash} e
-    #     assert_match {*ERR*} $e
-    #     catch {r georadius points 13.361389 38.115556 50 km store points2 withcoords} e
-    #     assert_match {*ERR*} $e
-    # }
-
-    test {GEORANGE STORE option: plain usage} {
-        r del points
-        r geoadd points 13.361389 38.115556 "Palermo" \
-                        15.087269 37.502669 "Catania"
-        r georadius points 13.361389 38.115556 500 km store points2
-        assert_equal [r zrange points 0 -1] [r zrange points2 0 -1]
-    }
-
-    # test {GEOSEARCHSTORE STORE option: plain usage} {
-    #     r geosearchstore points2 points fromlonlat 13.361389 38.115556 byradius 500 km
-    #     assert_equal [r zrange points 0 -1] [r zrange points2 0 -1]
-    # }
-
-    # test {GEORANGE STOREDIST option: plain usage} {
-    #     r del points
-    #     r geoadd points 13.361389 38.115556 "Palermo" \
-    #                     15.087269 37.502669 "Catania"
-    #     r georadius points 13.361389 38.115556 500 km storedist points2
-    #     set res [r zrange points2 0 -1 withscores]
-    #     assert {[lindex $res 1] < 1}
-    #     assert {[lindex $res 3] > 166}
-    #     assert {[lindex $res 3] < 167}
-    # }
-
-    # test {GEOSEARCHSTORE STOREDIST option: plain usage} {
-    #     r geosearchstore points2 points fromlonlat 13.361389 38.115556 byradius 500 km storedist
-    #     set res [r zrange points2 0 -1 withscores]
-    #     assert {[lindex $res 1] < 1}
-    #     assert {[lindex $res 3] > 166}
-    #     assert {[lindex $res 3] < 167}
-    # }
-
-    # test {GEORANGE STOREDIST option: COUNT ASC and DESC} {
-    #     r del points
-    #     r geoadd points 13.361389 38.115556 "Palermo" \
-    #                     15.087269 37.502669 "Catania"
-    #     r georadius points 13.361389 38.115556 500 km storedist points2 asc count 1
-    #     assert {[r zcard points2] == 1}
-    #     set res [r zrange points2 0 -1 withscores]
-    #     assert {[lindex $res 0] eq "Palermo"}
-
-    #     r georadius points 13.361389 38.115556 500 km storedist points2 desc count 1
-    #     assert {[r zcard points2] == 1}
-    #     set res [r zrange points2 0 -1 withscores]
-    #     assert {[lindex $res 0] eq "Catania"}
-    # }
-
-    test {GEOADD + GEORANGE randomized test} {
-        set attempt 30
-        while {[incr attempt -1]} {
-            set rv [lindex $regression_vectors $rv_idx]
-            incr rv_idx
-
-            unset -nocomplain debuginfo
-            set srand_seed [clock milliseconds]
-            if {$rv ne {}} {set srand_seed [lindex $rv 0]}
-            lappend debuginfo "srand_seed is $srand_seed"
-            expr {srand($srand_seed)} ; # If you need a reproducible run
-            r del mypoints
-
-            if {[randomInt 10] == 0} {
-                # From time to time use very big radiuses
-                set radius_km [expr {[randomInt 50000]+10}]
-            } else {
-                # Normally use a few - ~200km radiuses to stress
-                # test the code the most in edge cases.
-                set radius_km [expr {[randomInt 200]+10}]
-            }
-            if {$rv ne {}} {set radius_km [lindex $rv 1]}
-            set radius_m [expr {$radius_km*1000}]
-            geo_random_point search_lon search_lat
-            if {$rv ne {}} {
-                set search_lon [lindex $rv 2]
-                set search_lat [lindex $rv 3]
-            }
-            lappend debuginfo "Search area: $search_lon,$search_lat $radius_km km"
-            set tcl_result {}
-            set argv {}
-            for {set j 0} {$j < 20000} {incr j} {
-                geo_random_point lon lat
-                lappend argv $lon $lat "place:$j"
-                set distance [geo_distance $lon $lat $search_lon $search_lat]
-                if {$distance < $radius_m} {
-                    lappend tcl_result "place:$j"
-                }
-                lappend debuginfo "place:$j $lon $lat [expr {$distance/1000}] km"
-            }
-            r geoadd mypoints {*}$argv
-            set res [lsort [r georadius mypoints $search_lon $search_lat $radius_km km]]
-            set res2 [lsort $tcl_result]
-            set test_result OK
-
-            if {$res != $res2} {
-                set rounding_errors 0
-                set diff [compare_lists $res $res2]
-                foreach place $diff {
-                    set mydist [geo_distance $lon $lat $search_lon $search_lat]
-                    set mydist [expr $mydist/1000]
-                    if {($mydist / $radius_km) > 0.999} {
-                        incr rounding_errors
-                        continue
-                    }
-                    if {$mydist < $radius_m} {
-                        # This is a false positive for redis since given the 
-                        # same points the higher precision calculation provided 
-                        # by TCL shows the point within range
-                        incr rounding_errors
-                        continue
-                    }
-                }
-
-                # Make sure this is a real error and not a rounidng issue.
-                if {[llength $diff] == $rounding_errors} {
-                    set res $res2; # Error silenced
-                }
-            }
-
-            if {$res != $res2} {
-                set diff [compare_lists $res $res2]
-                puts "*** Possible problem in GEO radius query ***"
-                puts "Redis: $res"
-                puts "Tcl  : $res2"
-                puts "Diff : $diff"
-                puts [join $debuginfo "\n"]
-                foreach place $diff {
-                    if {[lsearch -exact $res2 $place] != -1} {
-                        set where "(only in Tcl)"
-                    } else {
-                        set where "(only in Redis)"
-                    }
-                    lassign [lindex [r geopos mypoints $place] 0] lon lat
-                    set mydist [geo_distance $lon $lat $search_lon $search_lat]
-                    set mydist [expr $mydist/1000]
-                    puts "$place -> [r geopos mypoints $place] $mydist $where"
-                    if {($mydist / $radius_km) > 0.999} {incr rounding_errors}
-                }
-                set test_result FAIL
-            }
-            unset -nocomplain debuginfo
-            if {$test_result ne {OK}} break
-        }
-        set test_result
-    } {OK}
-}