1# Redis test suite. Copyright (C) 2009 Salvatore Sanfilippo [email protected] 2# This software is released under the BSD License. See the COPYING file for 3# more information. 4 5package require Tcl 8.5 6 7set tcl_precision 17 8source tests/support/redis.tcl 9source tests/support/server.tcl 10source tests/support/tmpfile.tcl 11source tests/support/test.tcl 12source tests/support/util.tcl 13 14set ::all_tests { 15 unit/printver 16 unit/dump 17 unit/auth 18 unit/protocol 19 unit/keyspace 20 unit/scan 21 unit/type/string 22 unit/type/incr 23 unit/type/list 24 unit/type/list-2 25 unit/type/list-3 26 unit/type/set 27 unit/type/zset 28 unit/type/hash 29 unit/type/stream 30 unit/type/stream-cgroups 31 unit/sort 32 unit/expire 33 unit/other 34 unit/multi 35 unit/quit 36 unit/aofrw 37 integration/block-repl 38 integration/replication 39 integration/replication-2 40 integration/replication-3 41 integration/replication-4 42 integration/replication-psync 43 integration/aof 44 integration/rdb 45 integration/convert-zipmap-hash-on-load 46 integration/logging 47 integration/psync2 48 integration/psync2-reg 49 unit/pubsub 50 unit/slowlog 51 unit/scripting 52 unit/maxmemory 53 unit/introspection 54 unit/introspection-2 55 unit/limits 56 unit/obuf-limits 57 unit/bitops 58 unit/bitfield 59 unit/geo 60 unit/memefficiency 61 unit/hyperloglog 62 unit/lazyfree 63 unit/wait 64 unit/pendingquerybuf 65} 66# Index to the next test to run in the ::all_tests list. 67set ::next_test 0 68 69set ::host 127.0.0.1 70set ::port 21111 71set ::traceleaks 0 72set ::valgrind 0 73set ::stack_logging 0 74set ::verbose 0 75set ::quiet 0 76set ::denytags {} 77set ::skiptests {} 78set ::allowtags {} 79set ::only_tests {} 80set ::single_tests {} 81set ::skip_till "" 82set ::external 0; # If "1" this means, we are running against external instance 83set ::file ""; # If set, runs only the tests in this comma separated list 84set ::curfile ""; # Hold the filename of the current suite 85set ::accurate 0; # If true runs fuzz tests with more iterations 86set ::force_failure 0 87set ::timeout 600; # 10 minutes without progresses will quit the test. 88set ::last_progress [clock seconds] 89set ::active_servers {} ; # Pids of active Redis instances. 90set ::dont_clean 0 91set ::wait_server 0 92set ::stop_on_failure 0 93set ::loop 0 94 95# Set to 1 when we are running in client mode. The Redis test uses a 96# server-client model to run tests simultaneously. The server instance 97# runs the specified number of client instances that will actually run tests. 98# The server is responsible of showing the result to the user, and exit with 99# the appropriate exit code depending on the test outcome. 100set ::client 0 101set ::numclients 16 102 103proc execute_tests name { 104 set path "tests/$name.tcl" 105 set ::curfile $path 106 source $path 107 send_data_packet $::test_server_fd done "$name" 108} 109 110# Setup a list to hold a stack of server configs. When calls to start_server 111# are nested, use "srv 0 pid" to get the pid of the inner server. To access 112# outer servers, use "srv -1 pid" etcetera. 113set ::servers {} 114proc srv {args} { 115 set level 0 116 if {[string is integer [lindex $args 0]]} { 117 set level [lindex $args 0] 118 set property [lindex $args 1] 119 } else { 120 set property [lindex $args 0] 121 } 122 set srv [lindex $::servers end+$level] 123 dict get $srv $property 124} 125 126# Provide easy access to the client for the inner server. It's possible to 127# prepend the argument list with a negative level to access clients for 128# servers running in outer blocks. 129proc r {args} { 130 set level 0 131 if {[string is integer [lindex $args 0]]} { 132 set level [lindex $args 0] 133 set args [lrange $args 1 end] 134 } 135 [srv $level "client"] {*}$args 136} 137 138proc reconnect {args} { 139 set level [lindex $args 0] 140 if {[string length $level] == 0 || ![string is integer $level]} { 141 set level 0 142 } 143 144 set srv [lindex $::servers end+$level] 145 set host [dict get $srv "host"] 146 set port [dict get $srv "port"] 147 set config [dict get $srv "config"] 148 set client [redis $host $port] 149 dict set srv "client" $client 150 151 # select the right db when we don't have to authenticate 152 if {![dict exists $config "requirepass"]} { 153 $client select 9 154 } 155 156 # re-set $srv in the servers list 157 lset ::servers end+$level $srv 158} 159 160proc redis_deferring_client {args} { 161 set level 0 162 if {[llength $args] > 0 && [string is integer [lindex $args 0]]} { 163 set level [lindex $args 0] 164 set args [lrange $args 1 end] 165 } 166 167 # create client that defers reading reply 168 set client [redis [srv $level "host"] [srv $level "port"] 1] 169 170 # select the right db and read the response (OK) 171 $client select 9 172 $client read 173 return $client 174} 175 176# Provide easy access to INFO properties. Same semantic as "proc r". 177proc s {args} { 178 set level 0 179 if {[string is integer [lindex $args 0]]} { 180 set level [lindex $args 0] 181 set args [lrange $args 1 end] 182 } 183 status [srv $level "client"] [lindex $args 0] 184} 185 186proc cleanup {} { 187 if {$::dont_clean} { 188 return 189 } 190 if {!$::quiet} {puts -nonewline "Cleanup: may take some time... "} 191 flush stdout 192 catch {exec rm -rf {*}[glob tests/tmp/redis.conf.*]} 193 catch {exec rm -rf {*}[glob tests/tmp/server.*]} 194 if {!$::quiet} {puts "OK"} 195} 196 197proc test_server_main {} { 198 cleanup 199 set tclsh [info nameofexecutable] 200 # Open a listening socket, trying different ports in order to find a 201 # non busy one. 202 set port [find_available_port 11111] 203 if {!$::quiet} { 204 puts "Starting test server at port $port" 205 } 206 socket -server accept_test_clients -myaddr 127.0.0.1 $port 207 208 # Start the client instances 209 set ::clients_pids {} 210 set start_port [expr {$::port+100}] 211 for {set j 0} {$j < $::numclients} {incr j} { 212 set start_port [find_available_port $start_port] 213 set p [exec $tclsh [info script] {*}$::argv \ 214 --client $port --port $start_port &] 215 lappend ::clients_pids $p 216 incr start_port 10 217 } 218 219 # Setup global state for the test server 220 set ::idle_clients {} 221 set ::active_clients {} 222 array set ::active_clients_task {} 223 array set ::clients_start_time {} 224 set ::clients_time_history {} 225 set ::failed_tests {} 226 227 # Enter the event loop to handle clients I/O 228 after 100 test_server_cron 229 vwait forever 230} 231 232# This function gets called 10 times per second. 233proc test_server_cron {} { 234 set elapsed [expr {[clock seconds]-$::last_progress}] 235 236 if {$elapsed > $::timeout} { 237 set err "\[[colorstr red TIMEOUT]\]: clients state report follows." 238 puts $err 239 lappend ::failed_tests $err 240 show_clients_state 241 kill_clients 242 force_kill_all_servers 243 the_end 244 } 245 246 after 100 test_server_cron 247} 248 249proc accept_test_clients {fd addr port} { 250 fconfigure $fd -encoding binary 251 fileevent $fd readable [list read_from_test_client $fd] 252} 253 254# This is the readable handler of our test server. Clients send us messages 255# in the form of a status code such and additional data. Supported 256# status types are: 257# 258# ready: the client is ready to execute the command. Only sent at client 259# startup. The server will queue the client FD in the list of idle 260# clients. 261# testing: just used to signal that a given test started. 262# ok: a test was executed with success. 263# err: a test was executed with an error. 264# skip: a test was skipped by skipfile or individual test options. 265# ignore: a test was skipped by a group tag. 266# exception: there was a runtime exception while executing the test. 267# done: all the specified test file was processed, this test client is 268# ready to accept a new task. 269proc read_from_test_client fd { 270 set bytes [gets $fd] 271 set payload [read $fd $bytes] 272 foreach {status data} $payload break 273 set ::last_progress [clock seconds] 274 275 if {$status eq {ready}} { 276 if {!$::quiet} { 277 puts "\[$status\]: $data" 278 } 279 signal_idle_client $fd 280 } elseif {$status eq {done}} { 281 set elapsed [expr {[clock seconds]-$::clients_start_time($fd)}] 282 set all_tests_count [llength $::all_tests] 283 set running_tests_count [expr {[llength $::active_clients]-1}] 284 set completed_tests_count [expr {$::next_test-$running_tests_count}] 285 puts "\[$completed_tests_count/$all_tests_count [colorstr yellow $status]\]: $data ($elapsed seconds)" 286 lappend ::clients_time_history $elapsed $data 287 signal_idle_client $fd 288 set ::active_clients_task($fd) DONE 289 } elseif {$status eq {ok}} { 290 if {!$::quiet} { 291 puts "\[[colorstr green $status]\]: $data" 292 } 293 set ::active_clients_task($fd) "(OK) $data" 294 } elseif {$status eq {skip}} { 295 if {!$::quiet} { 296 puts "\[[colorstr yellow $status]\]: $data" 297 } 298 } elseif {$status eq {ignore}} { 299 if {!$::quiet} { 300 puts "\[[colorstr cyan $status]\]: $data" 301 } 302 } elseif {$status eq {err}} { 303 set err "\[[colorstr red $status]\]: $data" 304 puts $err 305 lappend ::failed_tests $err 306 set ::active_clients_task($fd) "(ERR) $data" 307 if {$::stop_on_failure} { 308 puts -nonewline "(Test stopped, press enter to continue)" 309 flush stdout 310 gets stdin 311 } 312 } elseif {$status eq {exception}} { 313 puts "\[[colorstr red $status]\]: $data" 314 kill_clients 315 force_kill_all_servers 316 exit 1 317 } elseif {$status eq {testing}} { 318 set ::active_clients_task($fd) "(IN PROGRESS) $data" 319 } elseif {$status eq {server-spawned}} { 320 lappend ::active_servers $data 321 } elseif {$status eq {server-killed}} { 322 set ::active_servers [lsearch -all -inline -not -exact $::active_servers $data] 323 } else { 324 if {!$::quiet} { 325 puts "\[$status\]: $data" 326 } 327 } 328} 329 330proc show_clients_state {} { 331 # The following loop is only useful for debugging tests that may 332 # enter an infinite loop. Commented out normally. 333 foreach x $::active_clients { 334 if {[info exist ::active_clients_task($x)]} { 335 puts "$x => $::active_clients_task($x)" 336 } else { 337 puts "$x => ???" 338 } 339 } 340} 341 342proc kill_clients {} { 343 foreach p $::clients_pids { 344 catch {exec kill $p} 345 } 346} 347 348proc force_kill_all_servers {} { 349 foreach p $::active_servers { 350 puts "Killing still running Redis server $p" 351 catch {exec kill -9 $p} 352 } 353} 354 355# A new client is idle. Remove it from the list of active clients and 356# if there are still test units to run, launch them. 357proc signal_idle_client fd { 358 # Remove this fd from the list of active clients. 359 set ::active_clients \ 360 [lsearch -all -inline -not -exact $::active_clients $fd] 361 362 if 0 {show_clients_state} 363 364 # New unit to process? 365 if {$::next_test != [llength $::all_tests]} { 366 if {!$::quiet} { 367 puts [colorstr bold-white "Testing [lindex $::all_tests $::next_test]"] 368 set ::active_clients_task($fd) "ASSIGNED: $fd ([lindex $::all_tests $::next_test])" 369 } 370 set ::clients_start_time($fd) [clock seconds] 371 send_data_packet $fd run [lindex $::all_tests $::next_test] 372 lappend ::active_clients $fd 373 incr ::next_test 374 if {$::loop && $::next_test == [llength $::all_tests]} { 375 set ::next_test 0 376 } 377 } else { 378 lappend ::idle_clients $fd 379 if {[llength $::active_clients] == 0} { 380 the_end 381 } 382 } 383} 384 385# The the_end function gets called when all the test units were already 386# executed, so the test finished. 387proc the_end {} { 388 # TODO: print the status, exit with the rigth exit code. 389 puts "\n The End\n" 390 puts "Execution time of different units:" 391 foreach {time name} $::clients_time_history { 392 puts " $time seconds - $name" 393 } 394 if {[llength $::failed_tests]} { 395 puts "\n[colorstr bold-red {!!! WARNING}] The following tests failed:\n" 396 foreach failed $::failed_tests { 397 puts "*** $failed" 398 } 399 cleanup 400 exit 1 401 } else { 402 puts "\n[colorstr bold-white {\o/}] [colorstr bold-green {All tests passed without errors!}]\n" 403 cleanup 404 exit 0 405 } 406} 407 408# The client is not even driven (the test server is instead) as we just need 409# to read the command, execute, reply... all this in a loop. 410proc test_client_main server_port { 411 set ::test_server_fd [socket localhost $server_port] 412 fconfigure $::test_server_fd -encoding binary 413 send_data_packet $::test_server_fd ready [pid] 414 while 1 { 415 set bytes [gets $::test_server_fd] 416 set payload [read $::test_server_fd $bytes] 417 foreach {cmd data} $payload break 418 if {$cmd eq {run}} { 419 execute_tests $data 420 } else { 421 error "Unknown test client command: $cmd" 422 } 423 } 424} 425 426proc send_data_packet {fd status data} { 427 set payload [list $status $data] 428 puts $fd [string length $payload] 429 puts -nonewline $fd $payload 430 flush $fd 431} 432 433proc print_help_screen {} { 434 puts [join { 435 "--valgrind Run the test over valgrind." 436 "--stack-logging Enable OSX leaks/malloc stack logging." 437 "--accurate Run slow randomized tests for more iterations." 438 "--quiet Don't show individual tests." 439 "--single <unit> Just execute the specified unit (see next option). this option can be repeated." 440 "--list-tests List all the available test units." 441 "--only <test> Just execute the specified test by test name. this option can be repeated." 442 "--skip-till <unit> Skip all units until (and including) the specified one." 443 "--clients <num> Number of test clients (default 16)." 444 "--timeout <sec> Test timeout in seconds (default 10 min)." 445 "--force-failure Force the execution of a test that always fails." 446 "--config <k> <v> Extra config file argument." 447 "--skipfile <file> Name of a file containing test names that should be skipped (one per line)." 448 "--dont-clean Don't delete redis log files after the run." 449 "--stop Blocks once the first test fails." 450 "--loop Execute the specified set of tests forever." 451 "--wait-server Wait after server is started (so that you can attach a debugger)." 452 "--help Print this help screen." 453 } "\n"] 454} 455 456# parse arguments 457for {set j 0} {$j < [llength $argv]} {incr j} { 458 set opt [lindex $argv $j] 459 set arg [lindex $argv [expr $j+1]] 460 if {$opt eq {--tags}} { 461 foreach tag $arg { 462 if {[string index $tag 0] eq "-"} { 463 lappend ::denytags [string range $tag 1 end] 464 } else { 465 lappend ::allowtags $tag 466 } 467 } 468 incr j 469 } elseif {$opt eq {--config}} { 470 set arg2 [lindex $argv [expr $j+2]] 471 lappend ::global_overrides $arg 472 lappend ::global_overrides $arg2 473 incr j 2 474 } elseif {$opt eq {--skipfile}} { 475 incr j 476 set fp [open $arg r] 477 set file_data [read $fp] 478 close $fp 479 set ::skiptests [split $file_data "\n"] 480 } elseif {$opt eq {--valgrind}} { 481 set ::valgrind 1 482 } elseif {$opt eq {--stack-logging}} { 483 if {[string match {*Darwin*} [exec uname -a]]} { 484 set ::stack_logging 1 485 } 486 } elseif {$opt eq {--quiet}} { 487 set ::quiet 1 488 } elseif {$opt eq {--host}} { 489 set ::external 1 490 set ::host $arg 491 incr j 492 } elseif {$opt eq {--port}} { 493 set ::port $arg 494 incr j 495 } elseif {$opt eq {--accurate}} { 496 set ::accurate 1 497 } elseif {$opt eq {--force-failure}} { 498 set ::force_failure 1 499 } elseif {$opt eq {--single}} { 500 lappend ::single_tests $arg 501 incr j 502 } elseif {$opt eq {--only}} { 503 lappend ::only_tests $arg 504 incr j 505 } elseif {$opt eq {--skip-till}} { 506 set ::skip_till $arg 507 incr j 508 } elseif {$opt eq {--list-tests}} { 509 foreach t $::all_tests { 510 puts $t 511 } 512 exit 0 513 } elseif {$opt eq {--verbose}} { 514 set ::verbose 1 515 } elseif {$opt eq {--client}} { 516 set ::client 1 517 set ::test_server_port $arg 518 incr j 519 } elseif {$opt eq {--clients}} { 520 set ::numclients $arg 521 incr j 522 } elseif {$opt eq {--dont-clean}} { 523 set ::dont_clean 1 524 } elseif {$opt eq {--wait-server}} { 525 set ::wait_server 1 526 } elseif {$opt eq {--stop}} { 527 set ::stop_on_failure 1 528 } elseif {$opt eq {--loop}} { 529 set ::loop 1 530 } elseif {$opt eq {--timeout}} { 531 set ::timeout $arg 532 incr j 533 } elseif {$opt eq {--help}} { 534 print_help_screen 535 exit 0 536 } else { 537 puts "Wrong argument: $opt" 538 exit 1 539 } 540} 541 542# If --skil-till option was given, we populate the list of single tests 543# to run with everything *after* the specified unit. 544if {$::skip_till != ""} { 545 set skipping 1 546 foreach t $::all_tests { 547 if {$skipping == 0} { 548 lappend ::single_tests $t 549 } 550 if {$t == $::skip_till} { 551 set skipping 0 552 } 553 } 554 if {$skipping} { 555 puts "test $::skip_till not found" 556 exit 0 557 } 558} 559 560# Override the list of tests with the specific tests we want to run 561# in case there was some filter, that is --single or --skip-till options. 562if {[llength $::single_tests] > 0} { 563 set ::all_tests $::single_tests 564} 565 566proc attach_to_replication_stream {} { 567 set s [socket [srv 0 "host"] [srv 0 "port"]] 568 fconfigure $s -translation binary 569 puts -nonewline $s "SYNC\r\n" 570 flush $s 571 572 # Get the count 573 while 1 { 574 set count [gets $s] 575 set prefix [string range $count 0 0] 576 if {$prefix ne {}} break; # Newlines are allowed as PINGs. 577 } 578 if {$prefix ne {$}} { 579 error "attach_to_replication_stream error. Received '$count' as count." 580 } 581 set count [string range $count 1 end] 582 583 # Consume the bulk payload 584 while {$count} { 585 set buf [read $s $count] 586 set count [expr {$count-[string length $buf]}] 587 } 588 return $s 589} 590 591proc read_from_replication_stream {s} { 592 fconfigure $s -blocking 0 593 set attempt 0 594 while {[gets $s count] == -1} { 595 if {[incr attempt] == 10} return "" 596 after 100 597 } 598 fconfigure $s -blocking 1 599 set count [string range $count 1 end] 600 601 # Return a list of arguments for the command. 602 set res {} 603 for {set j 0} {$j < $count} {incr j} { 604 read $s 1 605 set arg [::redis::redis_bulk_read $s] 606 if {$j == 0} {set arg [string tolower $arg]} 607 lappend res $arg 608 } 609 return $res 610} 611 612proc assert_replication_stream {s patterns} { 613 for {set j 0} {$j < [llength $patterns]} {incr j} { 614 assert_match [lindex $patterns $j] [read_from_replication_stream $s] 615 } 616} 617 618proc close_replication_stream {s} { 619 close $s 620} 621 622# With the parallel test running multiple Redis instances at the same time 623# we need a fast enough computer, otherwise a lot of tests may generate 624# false positives. 625# If the computer is too slow we revert the sequential test without any 626# parallelism, that is, clients == 1. 627proc is_a_slow_computer {} { 628 set start [clock milliseconds] 629 for {set j 0} {$j < 1000000} {incr j} {} 630 set elapsed [expr [clock milliseconds]-$start] 631 expr {$elapsed > 200} 632} 633 634if {$::client} { 635 if {[catch { test_client_main $::test_server_port } err]} { 636 set estr "Executing test client: $err.\n$::errorInfo" 637 if {[catch {send_data_packet $::test_server_fd exception $estr}]} { 638 puts $estr 639 } 640 exit 1 641 } 642} else { 643 if {[is_a_slow_computer]} { 644 puts "** SLOW COMPUTER ** Using a single client to avoid false positives." 645 set ::numclients 1 646 } 647 648 if {[catch { test_server_main } err]} { 649 if {[string length $err] > 0} { 650 # only display error when not generated by the test suite 651 if {$err ne "exception"} { 652 puts $::errorInfo 653 } 654 exit 1 655 } 656 } 657} 658