1set ::global_overrides {} 2set ::tags {} 3set ::valgrind_errors {} 4 5proc start_server_error {config_file error} { 6 set err {} 7 append err "Cant' start the Redis server\n" 8 append err "CONFIGURATION:" 9 append err [exec cat $config_file] 10 append err "\nERROR:" 11 append err [string trim $error] 12 send_data_packet $::test_server_fd err $err 13} 14 15proc check_valgrind_errors stderr { 16 set fd [open $stderr] 17 set buf [read $fd] 18 close $fd 19 20 if {[regexp -- { at 0x} $buf] || 21 (![regexp -- {definitely lost: 0 bytes} $buf] && 22 ![regexp -- {no leaks are possible} $buf])} { 23 send_data_packet $::test_server_fd err "Valgrind error: $buf\n" 24 } 25} 26 27proc kill_server config { 28 # nothing to kill when running against external server 29 if {$::external} return 30 31 # nevermind if its already dead 32 if {![is_alive $config]} { return } 33 set pid [dict get $config pid] 34 35 # check for leaks 36 if {![dict exists $config "skipleaks"]} { 37 catch { 38 if {[string match {*Darwin*} [exec uname -a]]} { 39 tags {"leaks"} { 40 test "Check for memory leaks (pid $pid)" { 41 set output {0 leaks} 42 catch {exec leaks $pid} output 43 set output 44 } {*0 leaks*} 45 } 46 } 47 } 48 } 49 50 # kill server and wait for the process to be totally exited 51 catch {exec kill $pid} 52 while {[is_alive $config]} { 53 incr wait 10 54 55 if {$wait >= 5000} { 56 puts "Forcing process $pid to exit..." 57 catch {exec kill -KILL $pid} 58 } elseif {$wait % 1000 == 0} { 59 puts "Waiting for process $pid to exit..." 60 } 61 after 10 62 } 63 64 # Check valgrind errors if needed 65 if {$::valgrind} { 66 check_valgrind_errors [dict get $config stderr] 67 } 68} 69 70proc is_alive config { 71 set pid [dict get $config pid] 72 if {[catch {exec ps -p $pid} err]} { 73 return 0 74 } else { 75 return 1 76 } 77} 78 79proc ping_server {host port} { 80 set retval 0 81 if {[catch { 82 set fd [socket $host $port] 83 fconfigure $fd -translation binary 84 puts $fd "PING\r\n" 85 flush $fd 86 set reply [gets $fd] 87 if {[string range $reply 0 0] eq {+} || 88 [string range $reply 0 0] eq {-}} { 89 set retval 1 90 } 91 close $fd 92 } e]} { 93 if {$::verbose} { 94 puts -nonewline "." 95 } 96 } else { 97 if {$::verbose} { 98 puts -nonewline "ok" 99 } 100 } 101 return $retval 102} 103 104# Return 1 if the server at the specified addr is reachable by PING, otherwise 105# returns 0. Performs a try every 50 milliseconds for the specified number 106# of retries. 107proc server_is_up {host port retrynum} { 108 after 10 ;# Use a small delay to make likely a first-try success. 109 set retval 0 110 while {[incr retrynum -1]} { 111 if {[catch {ping_server $host $port} ping]} { 112 set ping 0 113 } 114 if {$ping} {return 1} 115 after 50 116 } 117 return 0 118} 119 120# doesn't really belong here, but highly coupled to code in start_server 121proc tags {tags code} { 122 set ::tags [concat $::tags $tags] 123 uplevel 1 $code 124 set ::tags [lrange $::tags 0 end-[llength $tags]] 125} 126 127proc start_server {options {code undefined}} { 128 # If we are running against an external server, we just push the 129 # host/port pair in the stack the first time 130 if {$::external} { 131 if {[llength $::servers] == 0} { 132 set srv {} 133 dict set srv "host" $::host 134 dict set srv "port" $::port 135 set client [redis $::host $::port] 136 dict set srv "client" $client 137 $client select 9 138 139 # append the server to the stack 140 lappend ::servers $srv 141 } 142 uplevel 1 $code 143 return 144 } 145 146 # setup defaults 147 set baseconfig "default.conf" 148 set overrides {} 149 set tags {} 150 151 # parse options 152 foreach {option value} $options { 153 switch $option { 154 "config" { 155 set baseconfig $value } 156 "overrides" { 157 set overrides $value } 158 "tags" { 159 set tags $value 160 set ::tags [concat $::tags $value] } 161 default { 162 error "Unknown option $option" } 163 } 164 } 165 166 set data [split [exec cat "tests/assets/$baseconfig"] "\n"] 167 set config {} 168 foreach line $data { 169 if {[string length $line] > 0 && [string index $line 0] ne "#"} { 170 set elements [split $line " "] 171 set directive [lrange $elements 0 0] 172 set arguments [lrange $elements 1 end] 173 dict set config $directive $arguments 174 } 175 } 176 177 # use a different directory every time a server is started 178 dict set config dir [tmpdir server] 179 180 # start every server on a different port 181 set ::port [find_available_port [expr {$::port+1}]] 182 dict set config port $::port 183 184 # apply overrides from global space and arguments 185 foreach {directive arguments} [concat $::global_overrides $overrides] { 186 dict set config $directive $arguments 187 } 188 189 # write new configuration to temporary file 190 set config_file [tmpfile redis.conf] 191 set fp [open $config_file w+] 192 foreach directive [dict keys $config] { 193 puts -nonewline $fp "$directive " 194 puts $fp [dict get $config $directive] 195 } 196 close $fp 197 198 set stdout [format "%s/%s" [dict get $config "dir"] "stdout"] 199 set stderr [format "%s/%s" [dict get $config "dir"] "stderr"] 200 201 if {$::valgrind} { 202 exec valgrind --suppressions=src/valgrind.sup --show-reachable=no --show-possibly-lost=no --leak-check=full src/redis-server $config_file > $stdout 2> $stderr & 203 } else { 204 exec src/redis-server $config_file > $stdout 2> $stderr & 205 } 206 207 # check that the server actually started 208 # ugly but tries to be as fast as possible... 209 if {$::valgrind} {set retrynum 1000} else {set retrynum 100} 210 211 if {$::verbose} { 212 puts -nonewline "=== ($tags) Starting server ${::host}:${::port} " 213 } 214 215 if {$code ne "undefined"} { 216 set serverisup [server_is_up $::host $::port $retrynum] 217 } else { 218 set serverisup 1 219 } 220 221 if {$::verbose} { 222 puts "" 223 } 224 225 if {!$serverisup} { 226 set err {} 227 append err [exec cat $stdout] "\n" [exec cat $stderr] 228 start_server_error $config_file $err 229 return 230 } 231 232 # find out the pid 233 while {![info exists pid]} { 234 regexp {\[(\d+)\]} [exec cat $stdout] _ pid 235 after 100 236 } 237 238 # setup properties to be able to initialize a client object 239 set host $::host 240 set port $::port 241 if {[dict exists $config bind]} { set host [dict get $config bind] } 242 if {[dict exists $config port]} { set port [dict get $config port] } 243 244 # setup config dict 245 dict set srv "config_file" $config_file 246 dict set srv "config" $config 247 dict set srv "pid" $pid 248 dict set srv "host" $host 249 dict set srv "port" $port 250 dict set srv "stdout" $stdout 251 dict set srv "stderr" $stderr 252 253 # if a block of code is supplied, we wait for the server to become 254 # available, create a client object and kill the server afterwards 255 if {$code ne "undefined"} { 256 set line [exec head -n1 $stdout] 257 if {[string match {*already in use*} $line]} { 258 error_and_quit $config_file $line 259 } 260 261 while 1 { 262 # check that the server actually started and is ready for connections 263 if {[exec grep "ready to accept" | wc -l < $stdout] > 0} { 264 break 265 } 266 after 10 267 } 268 269 # append the server to the stack 270 lappend ::servers $srv 271 272 # connect client (after server dict is put on the stack) 273 reconnect 274 275 # execute provided block 276 set num_tests $::num_tests 277 if {[catch { uplevel 1 $code } error]} { 278 set backtrace $::errorInfo 279 280 # Kill the server without checking for leaks 281 dict set srv "skipleaks" 1 282 kill_server $srv 283 284 # Print warnings from log 285 puts [format "\nLogged warnings (pid %d):" [dict get $srv "pid"]] 286 set warnings [warnings_from_file [dict get $srv "stdout"]] 287 if {[string length $warnings] > 0} { 288 puts "$warnings" 289 } else { 290 puts "(none)" 291 } 292 puts "" 293 294 error $error $backtrace 295 } 296 297 # Don't do the leak check when no tests were run 298 if {$num_tests == $::num_tests} { 299 dict set srv "skipleaks" 1 300 } 301 302 # pop the server object 303 set ::servers [lrange $::servers 0 end-1] 304 305 set ::tags [lrange $::tags 0 end-[llength $tags]] 306 kill_server $srv 307 } else { 308 set ::tags [lrange $::tags 0 end-[llength $tags]] 309 set _ $srv 310 } 311} 312