1proc randstring {min max {type binary}} { 2 set len [expr {$min+int(rand()*($max-$min+1))}] 3 set output {} 4 if {$type eq {binary}} { 5 set minval 0 6 set maxval 255 7 } elseif {$type eq {alpha}} { 8 set minval 48 9 set maxval 122 10 } elseif {$type eq {compr}} { 11 set minval 48 12 set maxval 52 13 } 14 while {$len} { 15 append output [format "%c" [expr {$minval+int(rand()*($maxval-$minval+1))}]] 16 incr len -1 17 } 18 return $output 19} 20 21# Useful for some test 22proc zlistAlikeSort {a b} { 23 if {[lindex $a 0] > [lindex $b 0]} {return 1} 24 if {[lindex $a 0] < [lindex $b 0]} {return -1} 25 string compare [lindex $a 1] [lindex $b 1] 26} 27 28# Return all log lines starting with the first line that contains a warning. 29# Generally, this will be an assertion error with a stack trace. 30proc warnings_from_file {filename} { 31 set lines [split [exec cat $filename] "\n"] 32 set matched 0 33 set logall 0 34 set result {} 35 foreach line $lines { 36 if {[string match {*REDIS BUG REPORT START*} $line]} { 37 set logall 1 38 } 39 if {[regexp {^\[\d+\]\s+\d+\s+\w+\s+\d{2}:\d{2}:\d{2} \#} $line]} { 40 set matched 1 41 } 42 if {$logall || $matched} { 43 lappend result $line 44 } 45 } 46 join $result "\n" 47} 48 49# Return value for INFO property 50proc status {r property} { 51 if {[regexp "\r\n$property:(.*?)\r\n" [{*}$r info] _ value]} { 52 set _ $value 53 } 54} 55 56proc waitForBgsave r { 57 while 1 { 58 if {[status r rdb_bgsave_in_progress] eq 1} { 59 if {$::verbose} { 60 puts -nonewline "\nWaiting for background save to finish... " 61 flush stdout 62 } 63 after 1000 64 } else { 65 break 66 } 67 } 68} 69 70proc waitForBgrewriteaof r { 71 while 1 { 72 if {[status r aof_rewrite_in_progress] eq 1} { 73 if {$::verbose} { 74 puts -nonewline "\nWaiting for background AOF rewrite to finish... " 75 flush stdout 76 } 77 after 1000 78 } else { 79 break 80 } 81 } 82} 83 84proc wait_for_sync r { 85 while 1 { 86 if {[status $r master_link_status] eq "down"} { 87 after 10 88 } else { 89 break 90 } 91 } 92} 93 94proc wait_for_ofs_sync {r1 r2} { 95 wait_for_condition 50 100 { 96 [status $r1 master_repl_offset] eq [status $r2 master_repl_offset] 97 } else { 98 fail "replica didn't sync in time" 99 } 100} 101 102# Random integer between 0 and max (excluded). 103proc randomInt {max} { 104 expr {int(rand()*$max)} 105} 106 107# Random signed integer between -max and max (both extremes excluded). 108proc randomSignedInt {max} { 109 set i [randomInt $max] 110 if {rand() > 0.5} { 111 set i -$i 112 } 113 return $i 114} 115 116proc randpath args { 117 set path [expr {int(rand()*[llength $args])}] 118 uplevel 1 [lindex $args $path] 119} 120 121proc randomValue {} { 122 randpath { 123 # Small enough to likely collide 124 randomSignedInt 1000 125 } { 126 # 32 bit compressible signed/unsigned 127 randpath {randomSignedInt 2000000000} {randomSignedInt 4000000000} 128 } { 129 # 64 bit 130 randpath {randomSignedInt 1000000000000} 131 } { 132 # Random string 133 randpath {randstring 0 256 alpha} \ 134 {randstring 0 256 compr} \ 135 {randstring 0 256 binary} 136 } 137} 138 139proc randomKey {} { 140 randpath { 141 # Small enough to likely collide 142 randomInt 1000 143 } { 144 # 32 bit compressible signed/unsigned 145 randpath {randomInt 2000000000} {randomInt 4000000000} 146 } { 147 # 64 bit 148 randpath {randomInt 1000000000000} 149 } { 150 # Random string 151 randpath {randstring 1 256 alpha} \ 152 {randstring 1 256 compr} 153 } 154} 155 156proc findKeyWithType {r type} { 157 for {set j 0} {$j < 20} {incr j} { 158 set k [{*}$r randomkey] 159 if {$k eq {}} { 160 return {} 161 } 162 if {[{*}$r type $k] eq $type} { 163 return $k 164 } 165 } 166 return {} 167} 168 169proc createComplexDataset {r ops {opt {}}} { 170 for {set j 0} {$j < $ops} {incr j} { 171 set k [randomKey] 172 set k2 [randomKey] 173 set f [randomValue] 174 set v [randomValue] 175 176 if {[lsearch -exact $opt useexpire] != -1} { 177 if {rand() < 0.1} { 178 {*}$r expire [randomKey] [randomInt 2] 179 } 180 } 181 182 randpath { 183 set d [expr {rand()}] 184 } { 185 set d [expr {rand()}] 186 } { 187 set d [expr {rand()}] 188 } { 189 set d [expr {rand()}] 190 } { 191 set d [expr {rand()}] 192 } { 193 randpath {set d +inf} {set d -inf} 194 } 195 set t [{*}$r type $k] 196 197 if {$t eq {none}} { 198 randpath { 199 {*}$r set $k $v 200 } { 201 {*}$r lpush $k $v 202 } { 203 {*}$r sadd $k $v 204 } { 205 {*}$r zadd $k $d $v 206 } { 207 {*}$r hset $k $f $v 208 } { 209 {*}$r del $k 210 } 211 set t [{*}$r type $k] 212 } 213 214 switch $t { 215 {string} { 216 # Nothing to do 217 } 218 {list} { 219 randpath {{*}$r lpush $k $v} \ 220 {{*}$r rpush $k $v} \ 221 {{*}$r lrem $k 0 $v} \ 222 {{*}$r rpop $k} \ 223 {{*}$r lpop $k} 224 } 225 {set} { 226 randpath {{*}$r sadd $k $v} \ 227 {{*}$r srem $k $v} \ 228 { 229 set otherset [findKeyWithType {*}$r set] 230 if {$otherset ne {}} { 231 randpath { 232 {*}$r sunionstore $k2 $k $otherset 233 } { 234 {*}$r sinterstore $k2 $k $otherset 235 } { 236 {*}$r sdiffstore $k2 $k $otherset 237 } 238 } 239 } 240 } 241 {zset} { 242 randpath {{*}$r zadd $k $d $v} \ 243 {{*}$r zrem $k $v} \ 244 { 245 set otherzset [findKeyWithType {*}$r zset] 246 if {$otherzset ne {}} { 247 randpath { 248 {*}$r zunionstore $k2 2 $k $otherzset 249 } { 250 {*}$r zinterstore $k2 2 $k $otherzset 251 } 252 } 253 } 254 } 255 {hash} { 256 randpath {{*}$r hset $k $f $v} \ 257 {{*}$r hdel $k $f} 258 } 259 } 260 } 261} 262 263proc formatCommand {args} { 264 set cmd "*[llength $args]\r\n" 265 foreach a $args { 266 append cmd "$[string length $a]\r\n$a\r\n" 267 } 268 set _ $cmd 269} 270 271proc csvdump r { 272 set o {} 273 for {set db 0} {$db < 16} {incr db} { 274 {*}$r select $db 275 foreach k [lsort [{*}$r keys *]] { 276 set type [{*}$r type $k] 277 append o [csvstring $db] , [csvstring $k] , [csvstring $type] , 278 switch $type { 279 string { 280 append o [csvstring [{*}$r get $k]] "\n" 281 } 282 list { 283 foreach e [{*}$r lrange $k 0 -1] { 284 append o [csvstring $e] , 285 } 286 append o "\n" 287 } 288 set { 289 foreach e [lsort [{*}$r smembers $k]] { 290 append o [csvstring $e] , 291 } 292 append o "\n" 293 } 294 zset { 295 foreach e [{*}$r zrange $k 0 -1 withscores] { 296 append o [csvstring $e] , 297 } 298 append o "\n" 299 } 300 hash { 301 set fields [{*}$r hgetall $k] 302 set newfields {} 303 foreach {k v} $fields { 304 lappend newfields [list $k $v] 305 } 306 set fields [lsort -index 0 $newfields] 307 foreach kv $fields { 308 append o [csvstring [lindex $kv 0]] , 309 append o [csvstring [lindex $kv 1]] , 310 } 311 append o "\n" 312 } 313 } 314 } 315 } 316 {*}$r select 9 317 return $o 318} 319 320proc csvstring s { 321 return "\"$s\"" 322} 323 324proc roundFloat f { 325 format "%.10g" $f 326} 327 328proc find_available_port start { 329 for {set j $start} {$j < $start+1024} {incr j} { 330 if {[catch {set fd1 [socket 127.0.0.1 $j]}] && 331 [catch {set fd2 [socket 127.0.0.1 [expr $j+10000]]}]} { 332 return $j 333 } else { 334 catch { 335 close $fd1 336 close $fd2 337 } 338 } 339 } 340 if {$j == $start+1024} { 341 error "Can't find a non busy port in the $start-[expr {$start+1023}] range." 342 } 343} 344 345# Test if TERM looks like to support colors 346proc color_term {} { 347 expr {[info exists ::env(TERM)] && [string match *xterm* $::env(TERM)]} 348} 349 350proc colorstr {color str} { 351 if {[color_term]} { 352 set b 0 353 if {[string range $color 0 4] eq {bold-}} { 354 set b 1 355 set color [string range $color 5 end] 356 } 357 switch $color { 358 red {set colorcode {31}} 359 green {set colorcode {32}} 360 yellow {set colorcode {33}} 361 blue {set colorcode {34}} 362 magenta {set colorcode {35}} 363 cyan {set colorcode {36}} 364 white {set colorcode {37}} 365 default {set colorcode {37}} 366 } 367 if {$colorcode ne {}} { 368 return "\033\[$b;${colorcode};49m$str\033\[0m" 369 } 370 } else { 371 return $str 372 } 373} 374 375# Execute a background process writing random data for the specified number 376# of seconds to the specified Redis instance. 377proc start_write_load {host port seconds} { 378 set tclsh [info nameofexecutable] 379 exec $tclsh tests/helpers/gen_write_load.tcl $host $port $seconds & 380} 381 382# Stop a process generating write load executed with start_write_load. 383proc stop_write_load {handle} { 384 catch {exec /bin/kill -9 $handle} 385} 386 387proc K { x y } { set x } 388 389# Shuffle a list. From Tcl wiki. Originally from Steve Cohen that improved 390# other versions. Code should be under public domain. 391proc lshuffle {list} { 392 set n [llength $list] 393 while {$n>0} { 394 set j [expr {int(rand()*$n)}] 395 lappend slist [lindex $list $j] 396 incr n -1 397 set temp [lindex $list $n] 398 set list [lreplace [K $list [set list {}]] $j $j $temp] 399 } 400 return $slist 401} 402