| 1 | #!/usr/bin/env bash
 | 
| 2 | #
 | 
| 3 | # Usage:
 | 
| 4 | #   benchmarks/time-test.sh <function name>
 | 
| 5 | 
 | 
| 6 | set -o nounset
 | 
| 7 | set -o pipefail
 | 
| 8 | set -o errexit
 | 
| 9 | 
 | 
| 10 | REPO_ROOT=$(cd "$(dirname $0)/.."; pwd)
 | 
| 11 | 
 | 
| 12 | source test/common.sh
 | 
| 13 | source test/tsv-lib.sh
 | 
| 14 | 
 | 
| 15 | # TODO: This would be a nice little program for Oil
 | 
| 16 | count-lines-and-cols() {
 | 
| 17 |   python2 -c '
 | 
| 18 | import sys
 | 
| 19 | 
 | 
| 20 | expected_num_lines = int(sys.argv[1])
 | 
| 21 | expected_num_cols = int(sys.argv[2])
 | 
| 22 | try:
 | 
| 23 |   sep = sys.argv[3]
 | 
| 24 | except IndexError:
 | 
| 25 |   sep = "\t"
 | 
| 26 | 
 | 
| 27 | num_lines = 0
 | 
| 28 | tab_counts = []
 | 
| 29 | for line in sys.stdin:
 | 
| 30 |   tab_counts.append(line.count(sep))
 | 
| 31 |   num_lines += 1
 | 
| 32 |   # Show what we get
 | 
| 33 |   sys.stdout.write(line)
 | 
| 34 | 
 | 
| 35 | if any(tab_counts[0] != n for n in tab_counts):
 | 
| 36 |   raise AssertionError(tab_counts)
 | 
| 37 | 
 | 
| 38 | num_tabs = tab_counts[0]
 | 
| 39 | 
 | 
| 40 | assert expected_num_lines == num_lines, \
 | 
| 41 |   "expected %d lines, got %d" % (expected_num_lines, num_lines)
 | 
| 42 | assert expected_num_cols == num_tabs + 1, \
 | 
| 43 |   "expected %d cols, got %d" % (expected_num_cols, num_tabs + 1)
 | 
| 44 | ' "$@"
 | 
| 45 | }
 | 
| 46 | 
 | 
| 47 | time-tool() {
 | 
| 48 |   $(dirname $0)/time_.py "$@"
 | 
| 49 | }
 | 
| 50 | 
 | 
| 51 | test-csv() {
 | 
| 52 |   local out=_tmp/time.csv
 | 
| 53 | 
 | 
| 54 |   time-tool -o $out -- echo hi
 | 
| 55 |   cat $out | count-lines-and-cols 1 2 ,
 | 
| 56 | 
 | 
| 57 |   time-tool -o $out --field a --field b -- echo hi
 | 
| 58 |   cat $out | count-lines-and-cols 1 4 ,
 | 
| 59 |   echo csv fields=$?
 | 
| 60 | 
 | 
| 61 |   time-tool -o $out --rusage -- echo hi
 | 
| 62 |   cat $out | count-lines-and-cols 1 5 ,
 | 
| 63 |   echo csv rusage=$?
 | 
| 64 | 
 | 
| 65 |   time-tool -o $out --rusage --field a --field b -- echo hi
 | 
| 66 |   cat $out | count-lines-and-cols 1 7 ,
 | 
| 67 |   echo csv rusage fields=$?
 | 
| 68 | }
 | 
| 69 | 
 | 
| 70 | test-tsv() {
 | 
| 71 |   local out=_tmp/time.tsv
 | 
| 72 |   rm -f $out
 | 
| 73 | 
 | 
| 74 |   for i in 1 2 3; do
 | 
| 75 |     time-tool --tsv -o $out --append --time-fmt '%.2f' -- sleep 0.0${i}
 | 
| 76 |   done
 | 
| 77 |   cat $out | count-lines-and-cols 3 2
 | 
| 78 | 
 | 
| 79 |   time-tool --tsv -o $out --field a --field b -- echo hi
 | 
| 80 |   cat $out | count-lines-and-cols 1 4 
 | 
| 81 |   echo fields=$?
 | 
| 82 | 
 | 
| 83 |   time-tool --tsv -o $out --rusage --field a --field b -- echo hi
 | 
| 84 |   cat $out | count-lines-and-cols 1 7
 | 
| 85 |   echo rusage=$?
 | 
| 86 | 
 | 
| 87 |   time-tool --tsv -o $out --print-header \
 | 
| 88 |     --rusage-2
 | 
| 89 |   time-tool --tsv -o $out --append \
 | 
| 90 |     --rusage-2 -- echo hi
 | 
| 91 |   cat $out | count-lines-and-cols 2 10
 | 
| 92 |   echo rusage-2=$?
 | 
| 93 | }
 | 
| 94 | 
 | 
| 95 | test-append() {
 | 
| 96 |   local out=_tmp/overwrite.tsv
 | 
| 97 |   for i in 4 5; do
 | 
| 98 |     time-tool --tsv -o $out -- sleep 0.0${i}
 | 
| 99 |   done
 | 
| 100 |   cat $out | count-lines-and-cols 1 2
 | 
| 101 | 
 | 
| 102 |   echo ---
 | 
| 103 | 
 | 
| 104 |   local out=_tmp/append.tsv
 | 
| 105 |   rm -f $out
 | 
| 106 | 
 | 
| 107 |   for i in 4 5; do
 | 
| 108 |     time-tool --tsv -o $out --append -- sleep 0.0${i}
 | 
| 109 |   done
 | 
| 110 |   cat $out | count-lines-and-cols 2 2
 | 
| 111 | }
 | 
| 112 | 
 | 
| 113 | test-usage() {
 | 
| 114 |   # no args
 | 
| 115 |   set +o errexit
 | 
| 116 | 
 | 
| 117 |   time-tool; status=$?
 | 
| 118 |   assert $status -eq 2
 | 
| 119 | 
 | 
| 120 |   time-tool --output; status=$?
 | 
| 121 |   assert $status -eq 2
 | 
| 122 | 
 | 
| 123 |   time-tool sleep 0.1
 | 
| 124 |   time-tool --append sleep 0.1; status=$?
 | 
| 125 |   assert $status -eq 0
 | 
| 126 | 
 | 
| 127 |   set -o errexit
 | 
| 128 | }
 | 
| 129 | 
 | 
| 130 | test-bad-tsv-chars() {
 | 
| 131 |   local out=_tmp/time2.tsv
 | 
| 132 |   rm -f $out
 | 
| 133 | 
 | 
| 134 |   set +o errexit
 | 
| 135 | 
 | 
| 136 |   # Newline should fail
 | 
| 137 |   time-tool --tsv -o $out --field $'\n' -- sleep 0.001; status=$?
 | 
| 138 |   assert $status -eq 1
 | 
| 139 | 
 | 
| 140 |   # Tab should fail
 | 
| 141 |   time-tool --tsv -o $out --field $'\t' -- sleep 0.001; status=$?
 | 
| 142 |   assert $status -eq 1
 | 
| 143 | 
 | 
| 144 |   # Quote should fail
 | 
| 145 |   time-tool --tsv -o $out --field '"' -- sleep 0.001; status=$?
 | 
| 146 |   assert $status -eq 1
 | 
| 147 | 
 | 
| 148 |   # Backslash is OK
 | 
| 149 |   time-tool --tsv -o $out --field '\' -- sleep 0.001; status=$?
 | 
| 150 |   assert $status -eq 0
 | 
| 151 | 
 | 
| 152 |   # Space is OK, although canonical form would be " "
 | 
| 153 |   time-tool --tsv -o $out --field ' ' -- sleep 0.001; status=$?
 | 
| 154 |   assert $status -eq 0
 | 
| 155 | 
 | 
| 156 |   set -o errexit
 | 
| 157 | 
 | 
| 158 |   cat $out
 | 
| 159 | 
 | 
| 160 |   echo $'OK\ttest-bad-tsv-chars'
 | 
| 161 | }
 | 
| 162 | 
 | 
| 163 | test-stdout() {
 | 
| 164 |   local out=_tmp/time-stdout.csv
 | 
| 165 |   time-tool -o $out --stdout _tmp/stdout.txt -- seq 3
 | 
| 166 | 
 | 
| 167 |   diff _tmp/stdout.txt - <<EOF
 | 
| 168 | 1
 | 
| 169 | 2
 | 
| 170 | 3
 | 
| 171 | EOF
 | 
| 172 | 
 | 
| 173 |   # No assertions here yet
 | 
| 174 |   md5sum _tmp/stdout.txt
 | 
| 175 |   cat $out | count-lines-and-cols 1 3 ,
 | 
| 176 | 
 | 
| 177 |   time-tool -o $out --rusage --stdout _tmp/stdout.txt -- seq 3
 | 
| 178 |   cat $out | count-lines-and-cols 1 6 ,
 | 
| 179 | }
 | 
| 180 | 
 | 
| 181 | test-rusage() {
 | 
| 182 |   local out=_tmp/time-rusage.csv
 | 
| 183 |   time-tool --tsv -o $out --rusage -- bash -c 'echo bash'
 | 
| 184 |   cat $out | count-lines-and-cols 1 5
 | 
| 185 | 
 | 
| 186 |   #time-tool --tsv -o $out --rusage -- dash -c 'echo dash'
 | 
| 187 |   #cat $out
 | 
| 188 | 
 | 
| 189 |   # Blow up memory size for testing
 | 
| 190 |   local py='a=[42]*500000; print "python"'
 | 
| 191 | 
 | 
| 192 |   time-tool --tsv -o $out --rusage -- python2 -c "$py"
 | 
| 193 |   cat $out | count-lines-and-cols 1 5
 | 
| 194 | 
 | 
| 195 |   #time-tool --tsv -o $out --rusage -- bin/osh -c 'echo osh'
 | 
| 196 |   #cat $out
 | 
| 197 | }
 | 
| 198 | 
 | 
| 199 | test-time-span() {
 | 
| 200 |   local out=_tmp/time-span.csv
 | 
| 201 | 
 | 
| 202 |   time-tool --tsv -o $out --time-span --print-header
 | 
| 203 |   cat $out | count-lines-and-cols 1 4
 | 
| 204 | 
 | 
| 205 |   time-tool --tsv -o $out --time-span -- bash -c 'echo bash'
 | 
| 206 |   cat $out | count-lines-and-cols 1 4
 | 
| 207 | }
 | 
| 208 | 
 | 
| 209 | # Compare vs. /usr/bin/time.
 | 
| 210 | test-maxrss() {
 | 
| 211 |   if which time; then  # Ignore this on continuous build
 | 
| 212 |     command time --format '%x %U %M' -- seq 1
 | 
| 213 |   fi
 | 
| 214 | 
 | 
| 215 |   # Showing a discrepancy.  FIXED!
 | 
| 216 |   time-tool -o _tmp/maxrss --tsv --rusage -- seq 1
 | 
| 217 |   cat _tmp/maxrss
 | 
| 218 | }
 | 
| 219 | 
 | 
| 220 | test-print-header() {
 | 
| 221 |   set +o errexit
 | 
| 222 | 
 | 
| 223 |   # no arguments allowed
 | 
| 224 |   time-tool --tsv --print-header foo bar
 | 
| 225 |   assert $? -eq 2
 | 
| 226 | 
 | 
| 227 |   time-tool --tsv --print-header --field name
 | 
| 228 |   assert $? -eq 0
 | 
| 229 | 
 | 
| 230 |   time-tool --tsv --print-header --rusage --field name
 | 
| 231 |   assert $? -eq 0
 | 
| 232 | 
 | 
| 233 |   time-tool --print-header --rusage --field foo --field bar
 | 
| 234 |   assert $? -eq 0
 | 
| 235 | 
 | 
| 236 |   time-tool -o _tmp/time-test-1 \
 | 
| 237 |     --print-header --rusage --stdout DUMMY --tsv --field a --field b
 | 
| 238 |   assert $? -eq 0
 | 
| 239 | 
 | 
| 240 |   #set -x
 | 
| 241 |   head _tmp/time-test-1
 | 
| 242 | }
 | 
| 243 | 
 | 
| 244 | test-time-helper() {
 | 
| 245 |   set +o errexit
 | 
| 246 | 
 | 
| 247 |   local tmp=_tmp/time-helper.txt
 | 
| 248 | 
 | 
| 249 |   local th=_devbuild/bin/time-helper
 | 
| 250 | 
 | 
| 251 |   # Make some work show up
 | 
| 252 |   local cmd='{ md5sum */*.md; sleep 0.15; exit 42; } > /dev/null'
 | 
| 253 | 
 | 
| 254 |   echo 'will be overwritten' > $tmp
 | 
| 255 |   cat $tmp
 | 
| 256 | 
 | 
| 257 |   $th
 | 
| 258 |   assert $? -ne 0  # it's 1, but could be 2
 | 
| 259 | 
 | 
| 260 |   $th /bad
 | 
| 261 |   assert $? -eq 1
 | 
| 262 | 
 | 
| 263 |   $th -o $tmp -d $'\t' -x -e -- sh -c "$cmd"
 | 
| 264 |   assert $? -eq 42
 | 
| 265 |   cat $tmp
 | 
| 266 |   echo
 | 
| 267 | 
 | 
| 268 |   # Now append
 | 
| 269 |   $th -o $tmp -a -d , -x -e -U -S -M -- sh -c "$cmd"
 | 
| 270 |   assert $? -eq 42
 | 
| 271 |   cat $tmp
 | 
| 272 |   echo
 | 
| 273 |   
 | 
| 274 |   # Error case
 | 
| 275 |   $th -q
 | 
| 276 |   assert $? -eq 2
 | 
| 277 | }
 | 
| 278 | 
 | 
| 279 | test-time-tsv() {
 | 
| 280 |   local status
 | 
| 281 | 
 | 
| 282 |   local out=_tmp/time-test-zz
 | 
| 283 |   rm -f -v $out
 | 
| 284 | 
 | 
| 285 |   # Similar to what soil/worker.sh does
 | 
| 286 |   set +o errexit
 | 
| 287 |   time-tsv -o $out --append -- zz
 | 
| 288 |   status=$?
 | 
| 289 |   set -o errexit
 | 
| 290 | 
 | 
| 291 |   echo status=$status
 | 
| 292 |   assert $status -eq 1
 | 
| 293 | 
 | 
| 294 |   cat $out
 | 
| 295 |   echo
 | 
| 296 | }
 | 
| 297 | 
 | 
| 298 | test-grandchild-memory() {
 | 
| 299 |   local -a use_mem=( python2 -c 'import sys; ["X" * int(sys.argv[1])]' 10000000 )
 | 
| 300 | 
 | 
| 301 |   time-tsv -o /dev/stdout --rusage -- "${use_mem[@]}"
 | 
| 302 | 
 | 
| 303 |   # RUSAGE_CHILDREN includes grandchildren!
 | 
| 304 |   time-tsv -o /dev/stdout --rusage -- sh -c 'echo; "$@"' dummy "${use_mem[@]}"
 | 
| 305 | 
 | 
| 306 |   # 'exec' doesn't make a consistent difference, because /bin/sh doesn't use
 | 
| 307 |   # much memory
 | 
| 308 |   time-tsv -o /dev/stdout --rusage -- sh -c 'echo; exec "$@"' dummy "${use_mem[@]}"
 | 
| 309 | }
 | 
| 310 | 
 | 
| 311 | soil-run() {
 | 
| 312 |   run-test-funcs
 | 
| 313 | }
 | 
| 314 | 
 | 
| 315 | "$@"
 |