xref: /redis-3.2.3/tests/support/util.tcl (revision 175707e5)
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
94# Random integer between 0 and max (excluded).
95proc randomInt {max} {
96    expr {int(rand()*$max)}
97}
98
99# Random signed integer between -max and max (both extremes excluded).
100proc randomSignedInt {max} {
101    set i [randomInt $max]
102    if {rand() > 0.5} {
103        set i -$i
104    }
105    return $i
106}
107
108proc randpath args {
109    set path [expr {int(rand()*[llength $args])}]
110    uplevel 1 [lindex $args $path]
111}
112
113proc randomValue {} {
114    randpath {
115        # Small enough to likely collide
116        randomSignedInt 1000
117    } {
118        # 32 bit compressible signed/unsigned
119        randpath {randomSignedInt 2000000000} {randomSignedInt 4000000000}
120    } {
121        # 64 bit
122        randpath {randomSignedInt 1000000000000}
123    } {
124        # Random string
125        randpath {randstring 0 256 alpha} \
126                {randstring 0 256 compr} \
127                {randstring 0 256 binary}
128    }
129}
130
131proc randomKey {} {
132    randpath {
133        # Small enough to likely collide
134        randomInt 1000
135    } {
136        # 32 bit compressible signed/unsigned
137        randpath {randomInt 2000000000} {randomInt 4000000000}
138    } {
139        # 64 bit
140        randpath {randomInt 1000000000000}
141    } {
142        # Random string
143        randpath {randstring 1 256 alpha} \
144                {randstring 1 256 compr}
145    }
146}
147
148proc findKeyWithType {r type} {
149    for {set j 0} {$j < 20} {incr j} {
150        set k [{*}$r randomkey]
151        if {$k eq {}} {
152            return {}
153        }
154        if {[{*}$r type $k] eq $type} {
155            return $k
156        }
157    }
158    return {}
159}
160
161proc createComplexDataset {r ops {opt {}}} {
162    for {set j 0} {$j < $ops} {incr j} {
163        set k [randomKey]
164        set k2 [randomKey]
165        set f [randomValue]
166        set v [randomValue]
167
168        if {[lsearch -exact $opt useexpire] != -1} {
169            if {rand() < 0.1} {
170                {*}$r expire [randomKey] [randomInt 2]
171            }
172        }
173
174        randpath {
175            set d [expr {rand()}]
176        } {
177            set d [expr {rand()}]
178        } {
179            set d [expr {rand()}]
180        } {
181            set d [expr {rand()}]
182        } {
183            set d [expr {rand()}]
184        } {
185            randpath {set d +inf} {set d -inf}
186        }
187        set t [{*}$r type $k]
188
189        if {$t eq {none}} {
190            randpath {
191                {*}$r set $k $v
192            } {
193                {*}$r lpush $k $v
194            } {
195                {*}$r sadd $k $v
196            } {
197                {*}$r zadd $k $d $v
198            } {
199                {*}$r hset $k $f $v
200            } {
201                {*}$r del $k
202            }
203            set t [{*}$r type $k]
204        }
205
206        switch $t {
207            {string} {
208                # Nothing to do
209            }
210            {list} {
211                randpath {{*}$r lpush $k $v} \
212                        {{*}$r rpush $k $v} \
213                        {{*}$r lrem $k 0 $v} \
214                        {{*}$r rpop $k} \
215                        {{*}$r lpop $k}
216            }
217            {set} {
218                randpath {{*}$r sadd $k $v} \
219                        {{*}$r srem $k $v} \
220                        {
221                            set otherset [findKeyWithType {*}$r set]
222                            if {$otherset ne {}} {
223                                randpath {
224                                    {*}$r sunionstore $k2 $k $otherset
225                                } {
226                                    {*}$r sinterstore $k2 $k $otherset
227                                } {
228                                    {*}$r sdiffstore $k2 $k $otherset
229                                }
230                            }
231                        }
232            }
233            {zset} {
234                randpath {{*}$r zadd $k $d $v} \
235                        {{*}$r zrem $k $v} \
236                        {
237                            set otherzset [findKeyWithType {*}$r zset]
238                            if {$otherzset ne {}} {
239                                randpath {
240                                    {*}$r zunionstore $k2 2 $k $otherzset
241                                } {
242                                    {*}$r zinterstore $k2 2 $k $otherzset
243                                }
244                            }
245                        }
246            }
247            {hash} {
248                randpath {{*}$r hset $k $f $v} \
249                        {{*}$r hdel $k $f}
250            }
251        }
252    }
253}
254
255proc formatCommand {args} {
256    set cmd "*[llength $args]\r\n"
257    foreach a $args {
258        append cmd "$[string length $a]\r\n$a\r\n"
259    }
260    set _ $cmd
261}
262
263proc csvdump r {
264    set o {}
265    for {set db 0} {$db < 16} {incr db} {
266        {*}$r select $db
267        foreach k [lsort [{*}$r keys *]] {
268            set type [{*}$r type $k]
269            append o [csvstring $db] , [csvstring $k] , [csvstring $type] ,
270            switch $type {
271                string {
272                    append o [csvstring [{*}$r get $k]] "\n"
273                }
274                list {
275                    foreach e [{*}$r lrange $k 0 -1] {
276                        append o [csvstring $e] ,
277                    }
278                    append o "\n"
279                }
280                set {
281                    foreach e [lsort [{*}$r smembers $k]] {
282                        append o [csvstring $e] ,
283                    }
284                    append o "\n"
285                }
286                zset {
287                    foreach e [{*}$r zrange $k 0 -1 withscores] {
288                        append o [csvstring $e] ,
289                    }
290                    append o "\n"
291                }
292                hash {
293                    set fields [{*}$r hgetall $k]
294                    set newfields {}
295                    foreach {k v} $fields {
296                        lappend newfields [list $k $v]
297                    }
298                    set fields [lsort -index 0 $newfields]
299                    foreach kv $fields {
300                        append o [csvstring [lindex $kv 0]] ,
301                        append o [csvstring [lindex $kv 1]] ,
302                    }
303                    append o "\n"
304                }
305            }
306        }
307    }
308    {*}$r select 9
309    return $o
310}
311
312proc csvstring s {
313    return "\"$s\""
314}
315
316proc roundFloat f {
317    format "%.10g" $f
318}
319
320proc find_available_port start {
321    for {set j $start} {$j < $start+1024} {incr j} {
322        if {[catch {set fd1 [socket 127.0.0.1 $j]}] &&
323            [catch {set fd2 [socket 127.0.0.1 [expr $j+10000]]}]} {
324            return $j
325        } else {
326            catch {
327                close $fd1
328                close $fd2
329            }
330        }
331    }
332    if {$j == $start+1024} {
333        error "Can't find a non busy port in the $start-[expr {$start+1023}] range."
334    }
335}
336
337# Test if TERM looks like to support colors
338proc color_term {} {
339    expr {[info exists ::env(TERM)] && [string match *xterm* $::env(TERM)]}
340}
341
342proc colorstr {color str} {
343    if {[color_term]} {
344        set b 0
345        if {[string range $color 0 4] eq {bold-}} {
346            set b 1
347            set color [string range $color 5 end]
348        }
349        switch $color {
350            red {set colorcode {31}}
351            green {set colorcode {32}}
352            yellow {set colorcode {33}}
353            blue {set colorcode {34}}
354            magenta {set colorcode {35}}
355            cyan {set colorcode {36}}
356            white {set colorcode {37}}
357            default {set colorcode {37}}
358        }
359        if {$colorcode ne {}} {
360            return "\033\[$b;${colorcode};49m$str\033\[0m"
361        }
362    } else {
363        return $str
364    }
365}
366
367# Execute a background process writing random data for the specified number
368# of seconds to the specified Redis instance.
369proc start_write_load {host port seconds} {
370    set tclsh [info nameofexecutable]
371    exec $tclsh tests/helpers/gen_write_load.tcl $host $port $seconds &
372}
373
374# Stop a process generating write load executed with start_write_load.
375proc stop_write_load {handle} {
376    catch {exec /bin/kill -9 $handle}
377}
378