summaryrefslogtreecommitdiff
path: root/tests/support
diff options
context:
space:
mode:
Diffstat (limited to 'tests/support')
-rw-r--r--tests/support/redis.tcl58
-rw-r--r--tests/support/response_transformers.tcl105
-rw-r--r--tests/support/server.tcl14
3 files changed, 172 insertions, 5 deletions
diff --git a/tests/support/redis.tcl b/tests/support/redis.tcl
index 861e8bc27..53fa9fe91 100644
--- a/tests/support/redis.tcl
+++ b/tests/support/redis.tcl
@@ -28,6 +28,8 @@
package require Tcl 8.5
package provide redis 0.1
+source [file join [file dirname [info script]] "response_transformers.tcl"]
+
namespace eval redis {}
set ::redis::id 0
array set ::redis::fd {}
@@ -41,6 +43,11 @@ array set ::redis::tls {}
array set ::redis::callback {}
array set ::redis::state {} ;# State in non-blocking reply reading
array set ::redis::statestack {} ;# Stack of states, for nested mbulks
+array set ::redis::curr_argv {} ;# Remember the current argv, to be used in response_transformers.tcl
+array set ::redis::testing_resp3 {} ;# Indicating if the current client is using RESP3 (only if the test is trying to test RESP3 specific behavior. It won't be on in case of force_resp3)
+
+set ::force_resp3 0
+set ::log_req_res 0
proc redis {{server 127.0.0.1} {port 6379} {defer 0} {tls 0} {tlsoptions {}} {readraw 0}} {
if {$tls} {
@@ -62,6 +69,8 @@ proc redis {{server 127.0.0.1} {port 6379} {defer 0} {tls 0} {tlsoptions {}} {re
set ::redis::deferred($id) $defer
set ::redis::readraw($id) $readraw
set ::redis::reconnect($id) 0
+ set ::redis::curr_argv($id) 0
+ set ::redis::testing_resp3($id) 0
set ::redis::tls($id) $tls
::redis::redis_reset_state $id
interp alias {} ::redis::redisHandle$id {} ::redis::__dispatch__ $id
@@ -123,6 +132,20 @@ proc ::redis::__dispatch__raw__ {id method argv} {
set fd $::redis::fd($id)
}
+ # Transform HELLO 2 to HELLO 3 if force_resp3
+ # All set the connection var testing_resp3 in case of HELLO 3
+ if {[llength $argv] > 0 && [string compare -nocase $method "HELLO"] == 0} {
+ if {[lindex $argv 0] == 3} {
+ set ::redis::testing_resp3($id) 1
+ } else {
+ set ::redis::testing_resp3($id) 0
+ if {$::force_resp3} {
+ # If we are in force_resp3 we run HELLO 3 instead of HELLO 2
+ lset argv 0 3
+ }
+ }
+ }
+
set blocking $::redis::blocking($id)
set deferred $::redis::deferred($id)
if {$blocking == 0} {
@@ -146,6 +169,7 @@ proc ::redis::__dispatch__raw__ {id method argv} {
return -code error "I/O error reading reply"
}
+ set ::redis::curr_argv($id) [concat $method $argv]
if {!$deferred} {
if {$blocking} {
::redis::redis_read_reply $id $fd
@@ -200,6 +224,8 @@ proc ::redis::__method__close {id fd} {
catch {unset ::redis::state($id)}
catch {unset ::redis::statestack($id)}
catch {unset ::redis::callback($id)}
+ catch {unset ::redis::curr_argv($id)}
+ catch {unset ::redis::testing_resp3($id)}
catch {interp alias {} ::redis::redisHandle$id {}}
}
@@ -253,7 +279,7 @@ proc ::redis::redis_multi_bulk_read {id fd} {
set err {}
for {set i 0} {$i < $count} {incr i} {
if {[catch {
- lappend l [redis_read_reply $id $fd]
+ lappend l [redis_read_reply_logic $id $fd]
} e] && $err eq {}} {
set err $e
}
@@ -269,8 +295,8 @@ proc ::redis::redis_read_map {id fd} {
set err {}
for {set i 0} {$i < $count} {incr i} {
if {[catch {
- set k [redis_read_reply $id $fd] ; # key
- set v [redis_read_reply $id $fd] ; # value
+ set k [redis_read_reply_logic $id $fd] ; # key
+ set v [redis_read_reply_logic $id $fd] ; # value
dict set d $k $v
} e] && $err eq {}} {
set err $e
@@ -296,13 +322,25 @@ proc ::redis::redis_read_bool fd {
return -code error "Bad protocol, '$v' as bool type"
}
+proc ::redis::redis_read_double {id fd} {
+ set v [redis_read_line $fd]
+ # unlike many other DTs, there is a textual difference between double and a string with the same value,
+ # so we need to transform to double if we are testing RESP3 (i.e. some tests check that a
+ # double reply is "1.0" and not "1")
+ if {[should_transform_to_resp2 $id]} {
+ return $v
+ } else {
+ return [expr {double($v)}]
+ }
+}
+
proc ::redis::redis_read_verbatim_str fd {
set v [redis_bulk_read $fd]
# strip the first 4 chars ("txt:")
return [string range $v 4 end]
}
-proc ::redis::redis_read_reply {id fd} {
+proc ::redis::redis_read_reply_logic {id fd} {
if {$::redis::readraw($id)} {
return [redis_read_line $fd]
}
@@ -314,7 +352,7 @@ proc ::redis::redis_read_reply {id fd} {
: -
( -
+ {return [redis_read_line $fd]}
- , {return [expr {double([redis_read_line $fd])}]}
+ , {return [redis_read_double $id $fd]}
# {return [redis_read_bool $fd]}
= {return [redis_read_verbatim_str $fd]}
- {return -code error [redis_read_line $fd]}
@@ -340,6 +378,11 @@ proc ::redis::redis_read_reply {id fd} {
}
}
+proc ::redis::redis_read_reply {id fd} {
+ set response [redis_read_reply_logic $id $fd]
+ ::response_transformers::transform_response_if_needed $id $::redis::curr_argv($id) $response
+}
+
proc ::redis::redis_reset_state id {
set ::redis::state($id) [dict create buf {} mbulk -1 bulk -1 reply {}]
set ::redis::statestack($id) {}
@@ -416,3 +459,8 @@ proc ::redis::redis_readable {fd id} {
}
}
}
+
+# when forcing resp3 some tests that rely on resp2 can fail, so we have to translate the resp3 response to resp2
+proc ::redis::should_transform_to_resp2 {id} {
+ return [expr {$::force_resp3 && !$::redis::testing_resp3($id)}]
+}
diff --git a/tests/support/response_transformers.tcl b/tests/support/response_transformers.tcl
new file mode 100644
index 000000000..45b3cf8f2
--- /dev/null
+++ b/tests/support/response_transformers.tcl
@@ -0,0 +1,105 @@
+# Tcl client library - used by the Redis test
+# Copyright (C) 2009-2023 Redis Ltd.
+# Released under the BSD license like Redis itself
+#
+# This file contains a bunch of commands whose purpose is to transform
+# a RESP3 response to RESP2
+# Why is it needed?
+# When writing the reply_schema part in COMMAND DOCS we decided to use
+# the existing tests in order to verify the schemas (see logreqres.c)
+# The problem was that many tests were relying on the RESP2 structure
+# of the response (e.g. HRANDFIELD WITHVALUES in RESP2: {f1 v1 f2 v2}
+# vs. RESP3: {{f1 v1} {f2 v2}}).
+# Instead of adjusting the tests to expect RESP3 responses (a lot of
+# changes in many files) we decided to transform the response to RESP2
+# when running with --force-resp3
+
+package require Tcl 8.5
+
+namespace eval response_transformers {}
+
+# Transform a map response into an array of tuples (tuple = array with 2 elements)
+# Used for XREAD[GROUP]
+proc transfrom_map_to_tupple_array {argv response} {
+ set tuparray {}
+ foreach {key val} $response {
+ set tmp {}
+ lappend tmp $key
+ lappend tmp $val
+ lappend tuparray $tmp
+ }
+ return $tuparray
+}
+
+# Transform an array of tuples to a flat array
+proc transfrom_tuple_array_to_flat_array {argv response} {
+ set flatarray {}
+ foreach pair $response {
+ lappend flatarray {*}$pair
+ }
+ return $flatarray
+}
+
+# With HRANDFIELD, we only need to transform the response if the request had WITHVALUES
+# (otherwise the returned response is a flat array in both RESPs)
+proc transfrom_hrandfield_command {argv response} {
+ foreach ele $argv {
+ if {[string compare -nocase $ele "WITHVALUES"] == 0} {
+ return [transfrom_tuple_array_to_flat_array $argv $response]
+ }
+ }
+ return $response
+}
+
+# With some zset commands, we only need to transform the response if the request had WITHSCORES
+# (otherwise the returned response is a flat array in both RESPs)
+proc transfrom_zset_withscores_command {argv response} {
+ foreach ele $argv {
+ if {[string compare -nocase $ele "WITHSCORES"] == 0} {
+ return [transfrom_tuple_array_to_flat_array $argv $response]
+ }
+ }
+ return $response
+}
+
+# With ZPOPMIN/ZPOPMAX, we only need to transform the response if the request had COUNT (3rd arg)
+# (otherwise the returned response is a flat array in both RESPs)
+proc transfrom_zpopmin_zpopmax {argv response} {
+ if {[llength $argv] == 3} {
+ return [transfrom_tuple_array_to_flat_array $argv $response]
+ }
+ return $response
+}
+
+set ::trasformer_funcs {
+ XREAD transfrom_map_to_tupple_array
+ XREADGROUP transfrom_map_to_tupple_array
+ HRANDFIELD transfrom_hrandfield_command
+ ZRANDMEMBER transfrom_zset_withscores_command
+ ZRANGE transfrom_zset_withscores_command
+ ZRANGEBYSCORE transfrom_zset_withscores_command
+ ZRANGEBYLEX transfrom_zset_withscores_command
+ ZREVRANGE transfrom_zset_withscores_command
+ ZREVRANGEBYSCORE transfrom_zset_withscores_command
+ ZREVRANGEBYLEX transfrom_zset_withscores_command
+ ZUNION transfrom_zset_withscores_command
+ ZDIFF transfrom_zset_withscores_command
+ ZINTER transfrom_zset_withscores_command
+ ZPOPMIN transfrom_zpopmin_zpopmax
+ ZPOPMAX transfrom_zpopmin_zpopmax
+}
+
+proc ::response_transformers::transform_response_if_needed {id argv response} {
+ if {![::redis::should_transform_to_resp2 $id] || $::redis::readraw($id)} {
+ return $response
+ }
+
+ set key [string toupper [lindex $argv 0]]
+ if {![dict exists $::trasformer_funcs $key]} {
+ return $response
+ }
+
+ set transform [dict get $::trasformer_funcs $key]
+
+ return [$transform $argv $response]
+}
diff --git a/tests/support/server.tcl b/tests/support/server.tcl
index a23224bd7..4c596290d 100644
--- a/tests/support/server.tcl
+++ b/tests/support/server.tcl
@@ -207,6 +207,12 @@ proc tags_acceptable {tags err_return} {
}
}
+ # some units mess with the client output buffer so we can't really use the req-res logging mechanism.
+ if {$::log_req_res && [lsearch $tags "logreqres:skip"] >= 0} {
+ set err "Not supported when running in log-req-res mode"
+ return 0
+ }
+
if {$::external && [lsearch $tags "external:skip"] >= 0} {
set err "Not supported on external server"
return 0
@@ -511,6 +517,14 @@ proc start_server {options {code undefined}} {
dict unset config $directive
}
+ if {$::log_req_res} {
+ dict set config "req-res-logfile" "stdout.reqres"
+ }
+
+ if {$::force_resp3} {
+ dict set config "client-default-resp" "3"
+ }
+
# write new configuration to temporary file
set config_file [tmpfile redis.conf]
create_server_config_file $config_file $config $config_lines