xref: /redis-3.2.3/tests/support/util.tcl (revision 44acd50a)
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    foreach k [lsort [{*}$r keys *]] {
266        set type [{*}$r type $k]
267        append o [csvstring $k] , [csvstring $type] ,
268        switch $type {
269            string {
270                append o [csvstring [{*}$r get $k]] "\n"
271            }
272            list {
273                foreach e [{*}$r lrange $k 0 -1] {
274                    append o [csvstring $e] ,
275                }
276                append o "\n"
277            }
278            set {
279                foreach e [lsort [{*}$r smembers $k]] {
280                    append o [csvstring $e] ,
281                }
282                append o "\n"
283            }
284            zset {
285                foreach e [{*}$r zrange $k 0 -1 withscores] {
286                    append o [csvstring $e] ,
287                }
288                append o "\n"
289            }
290            hash {
291                set fields [{*}$r hgetall $k]
292                set newfields {}
293                foreach {k v} $fields {
294                    lappend newfields [list $k $v]
295                }
296                set fields [lsort -index 0 $newfields]
297                foreach kv $fields {
298                    append o [csvstring [lindex $kv 0]] ,
299                    append o [csvstring [lindex $kv 1]] ,
300                }
301                append o "\n"
302            }
303        }
304    }
305    return $o
306}
307
308proc csvstring s {
309    return "\"$s\""
310}
311
312proc roundFloat f {
313    format "%.10g" $f
314}
315
316proc find_available_port start {
317    for {set j $start} {$j < $start+1024} {incr j} {
318        if {[catch {set fd1 [socket 127.0.0.1 $j]}] &&
319            [catch {set fd2 [socket 127.0.0.1 [expr $j+10000]]}]} {
320            return $j
321        } else {
322            catch {
323                close $fd1
324                close $fd2
325            }
326        }
327    }
328    if {$j == $start+1024} {
329        error "Can't find a non busy port in the $start-[expr {$start+1023}] range."
330    }
331}
332
333# Test if TERM looks like to support colors
334proc color_term {} {
335    expr {[info exists ::env(TERM)] && [string match *xterm* $::env(TERM)]}
336}
337
338proc colorstr {color str} {
339    if {[color_term]} {
340        set b 0
341        if {[string range $color 0 4] eq {bold-}} {
342            set b 1
343            set color [string range $color 5 end]
344        }
345        switch $color {
346            red {set colorcode {31}}
347            green {set colorcode {32}}
348            yellow {set colorcode {33}}
349            blue {set colorcode {34}}
350            magenta {set colorcode {35}}
351            cyan {set colorcode {36}}
352            white {set colorcode {37}}
353            default {set colorcode {37}}
354        }
355        if {$colorcode ne {}} {
356            return "\033\[$b;${colorcode};49m$str\033\[0m"
357        }
358    } else {
359        return $str
360    }
361}
362
363# Execute a background process writing random data for the specified number
364# of seconds to the specified Redis instance.
365proc start_write_load {host port seconds} {
366    set tclsh [info nameofexecutable]
367    exec $tclsh tests/helpers/gen_write_load.tcl $host $port $seconds &
368}
369
370# Stop a process generating write load executed with start_write_load.
371proc stop_write_load {handle} {
372    catch {exec /bin/kill -9 $handle}
373}
374