1start_server {tags {"cli"}} { 2 proc open_cli {} { 3 set ::env(TERM) dumb 4 set fd [open [format "|src/redis-cli -p %d -n 9" [srv port]] "r+"] 5 fconfigure $fd -buffering none 6 fconfigure $fd -blocking false 7 fconfigure $fd -translation binary 8 assert_equal "redis> " [read_cli $fd] 9 set _ $fd 10 } 11 12 proc close_cli {fd} { 13 close $fd 14 } 15 16 proc read_cli {fd} { 17 set buf [read $fd] 18 while {[string length $buf] == 0} { 19 # wait some time and try again 20 after 10 21 set buf [read $fd] 22 } 23 set _ $buf 24 } 25 26 proc write_cli {fd buf} { 27 puts $fd $buf 28 flush $fd 29 } 30 31 # Helpers to run tests in interactive mode 32 proc run_command {fd cmd} { 33 write_cli $fd $cmd 34 set lines [split [read_cli $fd] "\n"] 35 assert_equal "redis> " [lindex $lines end] 36 join [lrange $lines 0 end-1] "\n" 37 } 38 39 proc test_interactive_cli {name code} { 40 set ::env(FAKETTY) 1 41 set fd [open_cli] 42 test "Interactive CLI: $name" $code 43 close_cli $fd 44 unset ::env(FAKETTY) 45 } 46 47 # Helpers to run tests where stdout is not a tty 48 proc write_tmpfile {contents} { 49 set tmp [tmpfile "cli"] 50 set tmpfd [open $tmp "w"] 51 puts -nonewline $tmpfd $contents 52 close $tmpfd 53 set _ $tmp 54 } 55 56 proc _run_cli {opts args} { 57 set cmd [format "src/redis-cli -p %d -n 9 $args" [srv port]] 58 foreach {key value} $opts { 59 if {$key eq "pipe"} { 60 set cmd "sh -c \"$value | $cmd\"" 61 } 62 if {$key eq "path"} { 63 set cmd "$cmd < $value" 64 } 65 } 66 67 set fd [open "|$cmd" "r"] 68 fconfigure $fd -buffering none 69 fconfigure $fd -translation binary 70 set resp [read $fd 1048576] 71 close $fd 72 set _ $resp 73 } 74 75 proc run_cli {args} { 76 _run_cli {} {*}$args 77 } 78 79 proc run_cli_with_input_pipe {cmd args} { 80 _run_cli [list pipe $cmd] {*}$args 81 } 82 83 proc run_cli_with_input_file {path args} { 84 _run_cli [list path $path] {*}$args 85 } 86 87 proc test_nontty_cli {name code} { 88 test "Non-interactive non-TTY CLI: $name" $code 89 } 90 91 # Helpers to run tests where stdout is a tty (fake it) 92 proc test_tty_cli {name code} { 93 set ::env(FAKETTY) 1 94 test "Non-interactive TTY CLI: $name" $code 95 unset ::env(FAKETTY) 96 } 97 98 test_interactive_cli "INFO response should be printed raw" { 99 set lines [split [run_command $fd info] "\n"] 100 foreach line $lines { 101 assert [regexp {^[a-z0-9_]+:[a-z0-9_]+} $line] 102 } 103 } 104 105 test_interactive_cli "Status reply" { 106 assert_equal "OK" [run_command $fd "set key foo"] 107 } 108 109 test_interactive_cli "Integer reply" { 110 assert_equal "(integer) 1" [run_command $fd "incr counter"] 111 } 112 113 test_interactive_cli "Bulk reply" { 114 r set key foo 115 assert_equal "\"foo\"" [run_command $fd "get key"] 116 } 117 118 test_interactive_cli "Multi-bulk reply" { 119 r rpush list foo 120 r rpush list bar 121 assert_equal "1. \"foo\"\n2. \"bar\"" [run_command $fd "lrange list 0 -1"] 122 } 123 124 test_interactive_cli "Parsing quotes" { 125 assert_equal "OK" [run_command $fd "set key \"bar\""] 126 assert_equal "bar" [r get key] 127 assert_equal "OK" [run_command $fd "set key \" bar \""] 128 assert_equal " bar " [r get key] 129 assert_equal "OK" [run_command $fd "set key \"\\\"bar\\\"\""] 130 assert_equal "\"bar\"" [r get key] 131 assert_equal "OK" [run_command $fd "set key \"\tbar\t\""] 132 assert_equal "\tbar\t" [r get key] 133 134 # invalid quotation 135 assert_equal "Invalid argument(s)" [run_command $fd "get \"\"key"] 136 assert_equal "Invalid argument(s)" [run_command $fd "get \"key\"x"] 137 138 # quotes after the argument are weird, but should be allowed 139 assert_equal "OK" [run_command $fd "set key\"\" bar"] 140 assert_equal "bar" [r get key] 141 } 142 143 test_tty_cli "Status reply" { 144 assert_equal "OK\n" [run_cli set key bar] 145 assert_equal "bar" [r get key] 146 } 147 148 test_tty_cli "Integer reply" { 149 r del counter 150 assert_equal "(integer) 1\n" [run_cli incr counter] 151 } 152 153 test_tty_cli "Bulk reply" { 154 r set key "tab\tnewline\n" 155 assert_equal "\"tab\\tnewline\\n\"\n" [run_cli get key] 156 } 157 158 test_tty_cli "Multi-bulk reply" { 159 r del list 160 r rpush list foo 161 r rpush list bar 162 assert_equal "1. \"foo\"\n2. \"bar\"\n" [run_cli lrange list 0 -1] 163 } 164 165 test_tty_cli "Read last argument from pipe" { 166 assert_equal "OK\n" [run_cli_with_input_pipe "echo foo" set key] 167 assert_equal "foo\n" [r get key] 168 } 169 170 test_tty_cli "Read last argument from file" { 171 set tmpfile [write_tmpfile "from file"] 172 assert_equal "OK\n" [run_cli_with_input_file $tmpfile set key] 173 assert_equal "from file" [r get key] 174 } 175 176 test_nontty_cli "Status reply" { 177 assert_equal "OK" [run_cli set key bar] 178 assert_equal "bar" [r get key] 179 } 180 181 test_nontty_cli "Integer reply" { 182 r del counter 183 assert_equal "1" [run_cli incr counter] 184 } 185 186 test_nontty_cli "Bulk reply" { 187 r set key "tab\tnewline\n" 188 assert_equal "tab\tnewline\n" [run_cli get key] 189 } 190 191 test_nontty_cli "Multi-bulk reply" { 192 r del list 193 r rpush list foo 194 r rpush list bar 195 assert_equal "foo\nbar" [run_cli lrange list 0 -1] 196 } 197 198 test_nontty_cli "Read last argument from pipe" { 199 assert_equal "OK" [run_cli_with_input_pipe "echo foo" set key] 200 assert_equal "foo\n" [r get key] 201 } 202 203 test_nontty_cli "Read last argument from file" { 204 set tmpfile [write_tmpfile "from file"] 205 assert_equal "OK" [run_cli_with_input_file $tmpfile set key] 206 assert_equal "from file" [r get key] 207 } 208} 209