xref: /redis-3.2.3/tests/support/util.tcl (revision 40f822da)
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 randomInt {max} {
95    expr {int(rand()*$max)}
96}
97
98proc randomSignedInt {max} {
99    set i [randomInt $max]
100    if {rand() > 0.5} {
101        set i -$i
102    }
103    return $i
104}
105
106proc randpath args {
107    set path [expr {int(rand()*[llength $args])}]
108    uplevel 1 [lindex $args $path]
109}
110
111proc randomValue {} {
112    randpath {
113        # Small enough to likely collide
114        randomSignedInt 1000
115    } {
116        # 32 bit compressible signed/unsigned
117        randpath {randomSignedInt 2000000000} {randomSignedInt 4000000000}
118    } {
119        # 64 bit
120        randpath {randomSignedInt 1000000000000}
121    } {
122        # Random string
123        randpath {randstring 0 256 alpha} \
124                {randstring 0 256 compr} \
125                {randstring 0 256 binary}
126    }
127}
128
129proc randomKey {} {
130    randpath {
131        # Small enough to likely collide
132        randomInt 1000
133    } {
134        # 32 bit compressible signed/unsigned
135        randpath {randomInt 2000000000} {randomInt 4000000000}
136    } {
137        # 64 bit
138        randpath {randomInt 1000000000000}
139    } {
140        # Random string
141        randpath {randstring 1 256 alpha} \
142                {randstring 1 256 compr}
143    }
144}
145
146proc findKeyWithType {r type} {
147    for {set j 0} {$j < 20} {incr j} {
148        set k [{*}$r randomkey]
149        if {$k eq {}} {
150            return {}
151        }
152        if {[{*}$r type $k] eq $type} {
153            return $k
154        }
155    }
156    return {}
157}
158
159proc createComplexDataset {r ops {opt {}}} {
160    for {set j 0} {$j < $ops} {incr j} {
161        set k [randomKey]
162        set k2 [randomKey]
163        set f [randomValue]
164        set v [randomValue]
165
166        if {[lsearch -exact $opt useexpire] != -1} {
167            if {rand() < 0.1} {
168                {*}$r expire [randomKey] [randomInt 2]
169            }
170        }
171
172        randpath {
173            set d [expr {rand()}]
174        } {
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            randpath {set d +inf} {set d -inf}
184        }
185        set t [{*}$r type $k]
186
187        if {$t eq {none}} {
188            randpath {
189                {*}$r set $k $v
190            } {
191                {*}$r lpush $k $v
192            } {
193                {*}$r sadd $k $v
194            } {
195                {*}$r zadd $k $d $v
196            } {
197                {*}$r hset $k $f $v
198            } {
199                {*}$r del $k
200            }
201            set t [{*}$r type $k]
202        }
203
204        switch $t {
205            {string} {
206                # Nothing to do
207            }
208            {list} {
209                randpath {{*}$r lpush $k $v} \
210                        {{*}$r rpush $k $v} \
211                        {{*}$r lrem $k 0 $v} \
212                        {{*}$r rpop $k} \
213                        {{*}$r lpop $k}
214            }
215            {set} {
216                randpath {{*}$r sadd $k $v} \
217                        {{*}$r srem $k $v} \
218                        {
219                            set otherset [findKeyWithType {*}$r set]
220                            if {$otherset ne {}} {
221                                randpath {
222                                    {*}$r sunionstore $k2 $k $otherset
223                                } {
224                                    {*}$r sinterstore $k2 $k $otherset
225                                } {
226                                    {*}$r sdiffstore $k2 $k $otherset
227                                }
228                            }
229                        }
230            }
231            {zset} {
232                randpath {{*}$r zadd $k $d $v} \
233                        {{*}$r zrem $k $v} \
234                        {
235                            set otherzset [findKeyWithType {*}$r zset]
236                            if {$otherzset ne {}} {
237                                randpath {
238                                    {*}$r zunionstore $k2 2 $k $otherzset
239                                } {
240                                    {*}$r zinterstore $k2 2 $k $otherzset
241                                }
242                            }
243                        }
244            }
245            {hash} {
246                randpath {{*}$r hset $k $f $v} \
247                        {{*}$r hdel $k $f}
248            }
249        }
250    }
251}
252
253proc formatCommand {args} {
254    set cmd "*[llength $args]\r\n"
255    foreach a $args {
256        append cmd "$[string length $a]\r\n$a\r\n"
257    }
258    set _ $cmd
259}
260
261proc csvdump r {
262    set o {}
263    foreach k [lsort [{*}$r keys *]] {
264        set type [{*}$r type $k]
265        append o [csvstring $k] , [csvstring $type] ,
266        switch $type {
267            string {
268                append o [csvstring [{*}$r get $k]] "\n"
269            }
270            list {
271                foreach e [{*}$r lrange $k 0 -1] {
272                    append o [csvstring $e] ,
273                }
274                append o "\n"
275            }
276            set {
277                foreach e [lsort [{*}$r smembers $k]] {
278                    append o [csvstring $e] ,
279                }
280                append o "\n"
281            }
282            zset {
283                foreach e [{*}$r zrange $k 0 -1 withscores] {
284                    append o [csvstring $e] ,
285                }
286                append o "\n"
287            }
288            hash {
289                set fields [{*}$r hgetall $k]
290                set newfields {}
291                foreach {k v} $fields {
292                    lappend newfields [list $k $v]
293                }
294                set fields [lsort -index 0 $newfields]
295                foreach kv $fields {
296                    append o [csvstring [lindex $kv 0]] ,
297                    append o [csvstring [lindex $kv 1]] ,
298                }
299                append o "\n"
300            }
301        }
302    }
303    return $o
304}
305
306proc csvstring s {
307    return "\"$s\""
308}
309
310proc roundFloat f {
311    format "%.10g" $f
312}
313