master
sh 603 lines 15 KB
Raw
1 # Test framework for go-ipfs
2 #
3 # Copyright (c) 2014 Christian Couder
4 # MIT Licensed; see the LICENSE file in this repository.
5 #
6 # We are using sharness (https://github.com/pl-strflt/sharness/tree/feat/junit)
7 # which was extracted from the Git test framework.
8
9 # use the ipfs tool to test against
10
11 # add current directory to path, for ipfs tool.
12 if test "$MAKE_SKIP_PATH" != "1"; then
13 BIN=$(cd .. && echo `pwd`/bin)
14 BIN2=$(cd ../.. && echo `pwd`/cmd/ipfs)
15 PATH=${BIN2}:${BIN}:${PATH}
16
17 # assert the `ipfs` we're using is the right one.
18 if test `which ipfs` != ${BIN2}/ipfs; then
19 echo >&2 "Cannot find the tests' local ipfs tool."
20 echo >&2 "Please check test and ipfs tool installation."
21 exit 1
22 fi
23 fi
24
25 # set sharness verbosity. we set the env var directly as
26 # it's too late to pass in --verbose, and --verbose is harder
27 # to pass through in some cases.
28 test "$TEST_VERBOSE" = 1 && verbose=t
29 test "$TEST_IMMEDIATE" = 1 && immediate=t
30 test "$TEST_JUNIT" = 1 && junit=t
31 test "$TEST_NO_COLOR" = 1 && no_color=t
32 # source the common hashes first.
33 . lib/test-lib-hashes.sh
34
35
36 ln -sf lib/sharness/sharness.sh .
37 ln -sf lib/sharness/lib-sharness .
38
39 . "sharness.sh" || {
40 echo >&2 "Cannot source: sharness.sh"
41 echo >&2 "Please check Sharness installation."
42 exit 1
43 }
44
45 # Please put go-ipfs specific shell functions below
46
47 ###
48 # BEGIN Check for pre-existing daemon being stuck
49 ###
50 wait_prev_cleanup_tick_secs=1
51 wait_prev_cleanup_max_secs=5
52 cur_test_pwd="$(pwd)"
53
54 while true ; do
55 echo -n > stuck_cwd_list
56
57 timeout 5 lsof -c ipfs -Ffn 2>/dev/null | grep -A1 '^fcwd$' | grep '^n' | cut -b 2- | while read -r pwd_of_stuck ; do
58 case "$pwd_of_stuck" in
59 "$cur_test_pwd"*)
60 echo "$pwd_of_stuck" >> stuck_cwd_list
61 ;;
62 *)
63 ;;
64 esac
65 done
66
67 test -s stuck_cwd_list || break
68
69 test "$wait_prev_cleanup_max_secs" -le 0 && break
70
71 echo "Daemons still running, waiting for ${wait_prev_cleanup_max_secs}s"
72 sleep $wait_prev_cleanup_tick_secs
73
74 wait_prev_cleanup_max_secs="$(( $wait_prev_cleanup_max_secs - $wait_prev_cleanup_tick_secs ))"
75 done
76
77 if test -s stuck_cwd_list ; then
78 test_expect_success "ipfs daemon (s)seems to be running with CWDs of
79 $(cat stuck_cwd_list)
80 Almost certainly a leftover from a prior test, ABORTING" 'false'
81
82 test_done
83 fi
84 ###
85 # END Check for pre-existing daemon being stuck
86 ###
87
88 # Make sure the ipfs path is set, also set in test_init_ipfs but that
89 # is not always used.
90 export IPFS_PATH="$(pwd)/.ipfs"
91 # Ask programs to please not print ANSI codes
92 export TERM=dumb
93
94 TEST_OS="$(uname -s | tr '[a-z]' '[A-Z]')"
95
96 # grab + output options
97 test "$TEST_FUSE" = 1 && test_set_prereq FUSE
98 test "$TEST_EXPENSIVE" = 1 && test_set_prereq EXPENSIVE
99 test "$TEST_DOCKER" = 1 && type docker >/dev/null 2>&1 && groups | egrep "\bdocker\b" && test_set_prereq DOCKER
100 test "$TEST_PLUGIN" = 1 && test "$TEST_OS" = "LINUX" && test_set_prereq PLUGIN
101
102 # this may not be available, skip a few dependent tests
103 type socat >/dev/null 2>&1 && test_set_prereq SOCAT
104 type unzip >/dev/null 2>&1 && test_set_prereq UNZIP
105
106
107 # Set a prereq as error messages are often different on Windows/Cygwin
108 expr "$TEST_OS" : "CYGWIN_NT" >/dev/null || test_set_prereq STD_ERR_MSG
109
110 if test "$TEST_VERBOSE" = 1; then
111 echo '# TEST_VERBOSE='"$TEST_VERBOSE"
112 echo '# TEST_IMMEDIATE='"$TEST_IMMEDIATE"
113 echo '# TEST_FUSE='"$TEST_FUSE"
114 echo '# TEST_DOCKER='"$TEST_DOCKER"
115 echo '# TEST_PLUGIN='"$TEST_PLUGIN"
116 echo '# TEST_EXPENSIVE='"$TEST_EXPENSIVE"
117 echo '# TEST_OS='"$TEST_OS"
118 echo '# TEST_JUNIT='"$TEST_JUNIT"
119 echo '# TEST_NO_COLOR='"$TEST_NO_COLOR"
120 echo '# TEST_ULIMIT_PRESET='"$TEST_ULIMIT_PRESET"
121 fi
122
123 # source our generic test lib
124 . ../../ipfs-test-lib.sh
125
126 # source iptb lib
127 . ../lib/iptb-lib.sh
128
129 test_cmp_repeat_10_sec() {
130 for i in $(test_seq 1 100)
131 do
132 test_cmp "$1" "$2" >/dev/null && return
133 go-sleep 100ms
134 done
135 test_cmp "$1" "$2"
136 }
137
138 test_run_repeat_60_sec() {
139 for i in $(test_seq 1 600)
140 do
141 (test_eval_ "$1") && return
142 go-sleep 100ms
143 done
144 return 1 # failed
145 }
146
147 test_wait_output_n_lines() {
148 for i in $(test_seq 1 3600)
149 do
150 test $(cat "$1" | wc -l | tr -d " ") -ge $2 && return
151 go-sleep 100ms
152 done
153 actual=$(cat "$1" | wc -l | tr -d " ")
154 test_fsh "expected $2 lines of output. got $actual"
155 }
156
157 test_wait_open_tcp_port_10_sec() {
158 for i in $(test_seq 1 100)
159 do
160 # this is not a perfect check, but it's portable.
161 # can't count on ss. not installed everywhere.
162 # can't count on netstat using : or . as port delim. differ across platforms.
163 echo $(netstat -aln | egrep "^tcp.*LISTEN" | egrep "[.:]$1" | wc -l) -gt 0
164 if [ $(netstat -aln | egrep "^tcp.*LISTEN" | egrep "[.:]$1" | wc -l) -gt 0 ]; then
165 return 0
166 fi
167 go-sleep 100ms
168 done
169 return 1
170 }
171
172
173 # test_config_set helps us make sure _we really did set_ a config value.
174 # it sets it and then tests it. This became elaborate because ipfs config
175 # was setting really weird things and am not sure why.
176 test_config_set() {
177
178 # grab flags (like --bool in "ipfs config --bool")
179 test_cfg_flags="" # unset in case.
180 test "$#" = 3 && { test_cfg_flags=$1; shift; }
181
182 test_cfg_key=$1
183 test_cfg_val=$2
184
185 # when verbose, tell the user what config values are being set
186 test_cfg_cmd="ipfs config $test_cfg_flags \"$test_cfg_key\" \"$test_cfg_val\""
187 test "$TEST_VERBOSE" = 1 && echo "$test_cfg_cmd"
188
189 # ok try setting the config key/val pair.
190 ipfs config $test_cfg_flags "$test_cfg_key" "$test_cfg_val"
191 echo "$test_cfg_val" >cfg_set_expected
192 ipfs config "$test_cfg_key" >cfg_set_actual
193 test_cmp cfg_set_expected cfg_set_actual
194 }
195
196 test_init_ipfs() {
197 args=("$@")
198
199 # we set the Addresses.API config variable.
200 # the cli client knows to use it, so only need to set.
201 # todo: in the future, use env?
202
203 test_expect_success "ipfs init succeeds" '
204 export IPFS_PATH="$(pwd)/.ipfs" &&
205 ipfs init "${args[@]}" --profile=test > /dev/null
206 '
207
208 test_expect_success "disable telemetry" '
209 test_config_set --bool Plugins.Plugins.telemetry.Disabled "true"
210 '
211
212 test_expect_success "prepare config -- mounting" '
213 mkdir mountdir ipfs ipns mfs &&
214 test_config_set Mounts.IPFS "$(pwd)/ipfs" &&
215 test_config_set Mounts.IPNS "$(pwd)/ipns" &&
216 test_config_set Mounts.MFS "$(pwd)/mfs" ||
217 test_fsh cat "\"$IPFS_PATH/config\""
218 '
219
220 }
221
222 test_init_ipfs_measure() {
223 args=("$@")
224
225 # we set the Addresses.API config variable.
226 # the cli client knows to use it, so only need to set.
227 # todo: in the future, use env?
228
229 test_expect_success "ipfs init succeeds" '
230 export IPFS_PATH="$(pwd)/.ipfs" &&
231 ipfs init "${args[@]}" --profile=test,flatfs-measure > /dev/null
232 '
233
234 test_expect_success "disable telemetry" '
235 test_config_set --bool Plugins.Plugins.telemetry.Disabled "true"
236 '
237
238 test_expect_success "prepare config -- mounting" '
239 mkdir mountdir ipfs ipns &&
240 test_config_set Mounts.IPFS "$(pwd)/ipfs" &&
241 test_config_set Mounts.IPNS "$(pwd)/ipns" ||
242 test_fsh cat "\"$IPFS_PATH/config\""
243 '
244
245 }
246
247 test_wait_for_file() {
248 loops=$1
249 delay=$2
250 file=$3
251 fwaitc=0
252 while ! test -f "$file"
253 do
254 if test $fwaitc -ge $loops
255 then
256 echo "Error: timed out waiting for file: $file"
257 return 1
258 fi
259
260 go-sleep $delay
261 fwaitc=`expr $fwaitc + 1`
262 done
263 }
264
265 test_set_address_vars() {
266 daemon_output="$1"
267
268 test_expect_success "set up address variables" '
269 API_MADDR=$(cat "$IPFS_PATH/api") &&
270 API_ADDR=$(convert_tcp_maddr $API_MADDR) &&
271 API_PORT=$(port_from_maddr $API_MADDR) &&
272
273 GWAY_MADDR=$(sed -n "s/^Gateway server listening on //p" "$daemon_output") &&
274 GWAY_ADDR=$(convert_tcp_maddr $GWAY_MADDR) &&
275 GWAY_PORT=$(port_from_maddr $GWAY_MADDR)
276 '
277
278 if ipfs swarm addrs local >/dev/null 2>&1; then
279 test_expect_success "get swarm addresses" '
280 ipfs swarm addrs local > addrs_out
281 '
282
283 test_expect_success "set swarm address vars" '
284 SWARM_MADDR=$(grep "127.0.0.1" addrs_out) &&
285 SWARM_PORT=$(port_from_maddr $SWARM_MADDR)
286 '
287 fi
288 }
289
290 test_launch_ipfs_daemon() {
291
292 args=("$@")
293
294 test "$TEST_ULIMIT_PRESET" != 1 && ulimit -n 2048
295
296 test_expect_success "'ipfs daemon' succeeds" '
297 ipfs daemon "${args[@]}" >actual_daemon 2>daemon_err &
298 IPFS_PID=$!
299 '
300
301 # wait for api file to show up
302 test_expect_success "api file shows up" '
303 test_wait_for_file 50 200ms "$IPFS_PATH/api"
304 '
305
306 test_set_address_vars actual_daemon
307
308 # we say the daemon is ready when the API server is ready.
309 test_expect_success "'ipfs daemon' is ready" '
310 pollEndpoint -host=$API_MADDR -v -tout=1s -tries=60 2>poll_apierr > poll_apiout ||
311 test_fsh cat actual_daemon || test_fsh cat daemon_err || test_fsh cat poll_apierr || test_fsh cat poll_apiout
312 '
313 }
314
315 test_launch_ipfs_daemon_without_network() {
316 test_launch_ipfs_daemon --offline "$@"
317 }
318
319 do_umount() {
320 local mount_point="$1"
321 local max_retries=3
322 local retry_delay=0.5
323
324 # Try normal unmount first (without lazy flag)
325 for i in $(seq 1 $max_retries); do
326 if [ "$(uname -s)" = "Linux" ]; then
327 # First attempt: standard unmount
328 if fusermount -u "$mount_point" 2>/dev/null; then
329 return 0
330 fi
331 else
332 if umount "$mount_point" 2>/dev/null; then
333 return 0
334 fi
335 fi
336
337 # If not last attempt, wait before retry
338 if [ $i -lt $max_retries ]; then
339 go-sleep "${retry_delay}s"
340 fi
341 done
342
343 # If normal unmount failed, try lazy unmount as last resort (Linux only)
344 if [ "$(uname -s)" = "Linux" ]; then
345 # Log that we're falling back to lazy unmount
346 test "$TEST_VERBOSE" = 1 && echo "# Warning: falling back to lazy unmount for $mount_point"
347 fusermount -z -u "$mount_point" 2>/dev/null
348 else
349 # On non-Linux, try force unmount
350 umount -f "$mount_point" 2>/dev/null || true
351 fi
352 }
353
354 test_mount_ipfs() {
355
356 # make sure stuff is unmounted first.
357 test_expect_success FUSE "'ipfs mount' succeeds" '
358 do_umount "$(pwd)/ipfs" || true &&
359 do_umount "$(pwd)/ipns" || true &&
360 do_umount "$(pwd)/mfs" || true &&
361 ipfs mount >actual
362 '
363
364 test_expect_success FUSE "'ipfs mount' output looks good" '
365 echo "IPFS mounted at: $(pwd)/ipfs" >expected &&
366 echo "IPNS mounted at: $(pwd)/ipns" >>expected &&
367 echo "MFS mounted at: $(pwd)/mfs" >>expected &&
368 test_cmp expected actual
369 '
370
371 }
372
373 test_launch_ipfs_daemon_and_mount() {
374
375 test_init_ipfs
376 test_launch_ipfs_daemon
377 test_mount_ipfs
378
379 }
380
381 test_kill_repeat_10_sec() {
382 # try to shut down once + wait for graceful exit
383 kill $1
384 for i in $(test_seq 1 100)
385 do
386 go-sleep 100ms
387 ! kill -0 $1 2>/dev/null && return
388 done
389
390 # if not, try once more, which will skip graceful exit
391 kill $1
392 go-sleep 1s
393 ! kill -0 $1 2>/dev/null && return
394
395 # ok, no hope. kill it to prevent it messing with other tests
396 kill -9 $1 2>/dev/null
397 return 1
398 }
399
400 test_kill_ipfs_daemon() {
401
402 test_expect_success "'ipfs daemon' is still running" '
403 kill -0 $IPFS_PID
404 '
405
406 test_expect_success "'ipfs daemon' can be killed" '
407 test_kill_repeat_10_sec $IPFS_PID
408 '
409 }
410
411 test_curl_resp_http_code() {
412 curl -I "$1" >curl_output || {
413 echo "curl error with url: '$1'"
414 echo "curl output was:"
415 cat curl_output
416 return 1
417 }
418 shift &&
419 RESP=$(head -1 curl_output) &&
420 while test "$#" -gt 0
421 do
422 expr "$RESP" : "$1" >/dev/null && return
423 shift
424 done
425 echo "curl response didn't match!"
426 echo "curl response was: '$RESP'"
427 echo "curl output was:"
428 cat curl_output
429 return 1
430 }
431
432 test_must_be_empty() {
433 if test -s "$1"
434 then
435 echo "'$1' is not empty, it contains:"
436 cat "$1"
437 return 1
438 fi
439 }
440
441 test_should_contain() {
442 test "$#" = 2 || error "bug in the test script: not 2 parameters to test_should_contain"
443 if ! grep -q "$1" "$2"
444 then
445 echo "'$2' does not contain '$1', it contains:"
446 cat "$2"
447 return 1
448 fi
449 }
450
451 test_should_not_contain() {
452 test "$#" = 2 || error "bug in the test script: not 2 parameters to test_should_not_contain"
453 if grep -q "$1" "$2"
454 then
455 echo "'$2' contains undesired value '$1'"
456 return 1
457 fi
458 }
459
460 test_str_contains() {
461 find=$1
462 shift
463 echo "$@" | egrep "\b$find\b" >/dev/null
464 }
465
466 disk_usage() {
467 # normalize du across systems
468 case $(uname -s) in
469 Linux)
470 DU="du -sb"
471 M=1
472 ;;
473 FreeBSD)
474 DU="du -s -A -B 1"
475 M=512
476 ;;
477 Darwin | DragonFly | *)
478 DU="du -s"
479 M=512
480 ;;
481 esac
482 expr $($DU "$1" | awk "{print \$1}") "*" "$M"
483 }
484
485 # output a file's permission in human readable format
486 generic_stat() {
487 # normalize stat across systems
488 case $(uname -s) in
489 Linux)
490 _STAT="stat -c %A"
491 ;;
492 FreeBSD | Darwin | DragonFly)
493 _STAT="stat -f %Sp"
494 ;;
495 *)
496 echo "unsupported OS" >&2
497 exit 1
498 ;;
499 esac
500 $_STAT "$1" || echo "failed" # Avoid returning nothing.
501 }
502
503 # output a file's permission in human readable format
504 file_size() {
505 case $(uname -s) in
506 Linux)
507 _STAT="stat --format=%s"
508 ;;
509 FreeBSD | Darwin | DragonFly)
510 _STAT="stat -f%z"
511 ;;
512 *)
513 echo "unsupported OS" >&2
514 exit 1
515 ;;
516 esac
517 $_STAT "$1"
518 }
519
520 # len 46: 2048-bit RSA keys, b58mh-encoded
521 # len 52: ED25519 keys, b58mh-encoded
522 # len 56: 2048-bit RSA keys, base36-encoded
523 # len 62: ED25519 keys, base36-encoded
524 test_check_peerid() {
525 peeridlen=$(echo "$1" | tr -dC "[:alnum:]" | wc -c | tr -d " ") &&
526 test "$peeridlen" = "46" -o "$peeridlen" = "52" -o "$peeridlen" = "56" -o "$peeridlen" = "62" || {
527 echo "Bad peerid '$1' with len '$peeridlen'"
528 return 1
529 }
530 }
531
532 test_check_rsa2048_b58mh_peerid() {
533 peeridlen=$(echo "$1" | tr -dC "[:alnum:]" | wc -c | tr -d " ") &&
534 test "$peeridlen" = "46" || {
535 echo "Bad RSA2048 B58MH peerid '$1' with len '$peeridlen'"
536 return 1
537 }
538 }
539
540 test_check_ed25519_b58mh_peerid() {
541 peeridlen=$(echo "$1" | tr -dC "[:alnum:]" | wc -c | tr -d " ") &&
542 test "$peeridlen" = "52" || {
543 echo "Bad ED25519 B58MH peerid '$1' with len '$peeridlen'"
544 return 1
545 }
546 }
547
548 test_check_rsa2048_base36_peerid() {
549 peeridlen=$(echo "$1" | tr -dC "[:alnum:]" | wc -c | tr -d " ") &&
550 test "$peeridlen" = "56" || {
551 echo "Bad RSA2048 B36CID peerid '$1' with len '$peeridlen'"
552 return 1
553 }
554 }
555
556 test_check_ed25519_base36_peerid() {
557 peeridlen=$(echo "$1" | tr -dC "[:alnum:]" | wc -c | tr -d " ") &&
558 test "$peeridlen" = "62" || {
559 echo "Bad ED25519 B36CID peerid '$1' with len '$peeridlen'"
560 return 1
561 }
562 }
563
564 convert_tcp_maddr() {
565 echo $1 | awk -F'/' '{ printf "%s:%s", $3, $5 }'
566 }
567
568 port_from_maddr() {
569 echo $1 | awk -F'/' '{ print $NF }'
570 }
571
572 findprovs_empty() {
573 test_expect_success 'findprovs '$1' succeeds' '
574 ipfsi 1 routing findprovs -n 1 '$1' > findprovsOut
575 '
576
577 test_expect_success "findprovs $1 output is empty" '
578 test_must_be_empty findprovsOut
579 '
580 }
581
582 findprovs_expect() {
583 test_expect_success 'findprovs '$1' succeeds' '
584 ipfsi 1 routing findprovs -n 1 '$1' > findprovsOut &&
585 echo '$2' > expected
586 '
587
588 test_expect_success "findprovs $1 output looks good" '
589 test_cmp findprovsOut expected
590 '
591 }
592
593 purge_blockstore() {
594 ipfs pin ls --quiet --type=recursive | ipfs pin rm &>/dev/null
595 ipfs repo gc --silent &>/dev/null
596
597 test_expect_success "pinlist empty" '
598 [[ -z "$( ipfs pin ls )" ]]
599 '
600 test_expect_success "nothing left to gc" '
601 [[ -z "$( ipfs repo gc )" ]]
602 '
603 }