kubo/test/sharness/lib/test-lib.sh
Hector Sanjuan 15f723a15e
Some checks failed
CodeQL / codeql (push) Has been cancelled
Docker Build / docker-build (push) Has been cancelled
Gateway Conformance / gateway-conformance (push) Has been cancelled
Gateway Conformance / gateway-conformance-libp2p-experiment (push) Has been cancelled
Go Build / go-build (push) Has been cancelled
Go Check / go-check (push) Has been cancelled
Go Lint / go-lint (push) Has been cancelled
Go Test / go-test (push) Has been cancelled
Interop / interop-prep (push) Has been cancelled
Sharness / sharness-test (push) Has been cancelled
Spell Check / spellcheck (push) Has been cancelled
Interop / helia-interop (push) Has been cancelled
Interop / ipfs-webui (push) Has been cancelled
fix: disable telemetry in test profile (#10931)
* Tests: disable telemetry in tests by default

Disable the plugin in cli tests and sharness by default. Enable only in
telemetry tests.

There are cases when tests get stuck or get killed and leave daemons hanging around. We don't want to be getting telemetry from those.

* sharness: attempt to fix

* sharness: add missing --bool flag

* fix(ci): add omitempty to Plugin.Config field

The sharness problem is that when the telemetry plugin is configured
initially with 'ipfs config --bool', it creates a structure without
the 'Config: null' field, but when the config is copied and replaced,
it expects the structure to be preserved.

Adding omitempty ensures the Config field is omitted from JSON when
nil, making the config structure consistent between initial creation
and replacement operations.

---------

Co-authored-by: Marcin Rataj <lidel@lidel.org>
2025-08-24 14:30:35 +02:00

604 lines
15 KiB
Bash

# Test framework for go-ipfs
#
# Copyright (c) 2014 Christian Couder
# MIT Licensed; see the LICENSE file in this repository.
#
# We are using sharness (https://github.com/pl-strflt/sharness/tree/feat/junit)
# which was extracted from the Git test framework.
# use the ipfs tool to test against
# add current directory to path, for ipfs tool.
if test "$MAKE_SKIP_PATH" != "1"; then
BIN=$(cd .. && echo `pwd`/bin)
BIN2=$(cd ../.. && echo `pwd`/cmd/ipfs)
PATH=${BIN2}:${BIN}:${PATH}
# assert the `ipfs` we're using is the right one.
if test `which ipfs` != ${BIN2}/ipfs; then
echo >&2 "Cannot find the tests' local ipfs tool."
echo >&2 "Please check test and ipfs tool installation."
exit 1
fi
fi
# set sharness verbosity. we set the env var directly as
# it's too late to pass in --verbose, and --verbose is harder
# to pass through in some cases.
test "$TEST_VERBOSE" = 1 && verbose=t
test "$TEST_IMMEDIATE" = 1 && immediate=t
test "$TEST_JUNIT" = 1 && junit=t
test "$TEST_NO_COLOR" = 1 && no_color=t
# source the common hashes first.
. lib/test-lib-hashes.sh
ln -sf lib/sharness/sharness.sh .
ln -sf lib/sharness/lib-sharness .
. "sharness.sh" || {
echo >&2 "Cannot source: sharness.sh"
echo >&2 "Please check Sharness installation."
exit 1
}
# Please put go-ipfs specific shell functions below
###
# BEGIN Check for pre-existing daemon being stuck
###
wait_prev_cleanup_tick_secs=1
wait_prev_cleanup_max_secs=5
cur_test_pwd="$(pwd)"
while true ; do
echo -n > stuck_cwd_list
timeout 5 lsof -c ipfs -Ffn 2>/dev/null | grep -A1 '^fcwd$' | grep '^n' | cut -b 2- | while read -r pwd_of_stuck ; do
case "$pwd_of_stuck" in
"$cur_test_pwd"*)
echo "$pwd_of_stuck" >> stuck_cwd_list
;;
*)
;;
esac
done
test -s stuck_cwd_list || break
test "$wait_prev_cleanup_max_secs" -le 0 && break
echo "Daemons still running, waiting for ${wait_prev_cleanup_max_secs}s"
sleep $wait_prev_cleanup_tick_secs
wait_prev_cleanup_max_secs="$(( $wait_prev_cleanup_max_secs - $wait_prev_cleanup_tick_secs ))"
done
if test -s stuck_cwd_list ; then
test_expect_success "ipfs daemon (s)seems to be running with CWDs of
$(cat stuck_cwd_list)
Almost certainly a leftover from a prior test, ABORTING" 'false'
test_done
fi
###
# END Check for pre-existing daemon being stuck
###
# Make sure the ipfs path is set, also set in test_init_ipfs but that
# is not always used.
export IPFS_PATH="$(pwd)/.ipfs"
# Ask programs to please not print ANSI codes
export TERM=dumb
TEST_OS="$(uname -s | tr '[a-z]' '[A-Z]')"
# grab + output options
test "$TEST_FUSE" = 1 && test_set_prereq FUSE
test "$TEST_EXPENSIVE" = 1 && test_set_prereq EXPENSIVE
test "$TEST_DOCKER" = 1 && type docker >/dev/null 2>&1 && groups | egrep "\bdocker\b" && test_set_prereq DOCKER
test "$TEST_PLUGIN" = 1 && test "$TEST_OS" = "LINUX" && test_set_prereq PLUGIN
# this may not be available, skip a few dependent tests
type socat >/dev/null 2>&1 && test_set_prereq SOCAT
type unzip >/dev/null 2>&1 && test_set_prereq UNZIP
# Set a prereq as error messages are often different on Windows/Cygwin
expr "$TEST_OS" : "CYGWIN_NT" >/dev/null || test_set_prereq STD_ERR_MSG
if test "$TEST_VERBOSE" = 1; then
echo '# TEST_VERBOSE='"$TEST_VERBOSE"
echo '# TEST_IMMEDIATE='"$TEST_IMMEDIATE"
echo '# TEST_FUSE='"$TEST_FUSE"
echo '# TEST_DOCKER='"$TEST_DOCKER"
echo '# TEST_PLUGIN='"$TEST_PLUGIN"
echo '# TEST_EXPENSIVE='"$TEST_EXPENSIVE"
echo '# TEST_OS='"$TEST_OS"
echo '# TEST_JUNIT='"$TEST_JUNIT"
echo '# TEST_NO_COLOR='"$TEST_NO_COLOR"
echo '# TEST_ULIMIT_PRESET='"$TEST_ULIMIT_PRESET"
fi
# source our generic test lib
. ../../ipfs-test-lib.sh
# source iptb lib
. ../lib/iptb-lib.sh
test_cmp_repeat_10_sec() {
for i in $(test_seq 1 100)
do
test_cmp "$1" "$2" >/dev/null && return
go-sleep 100ms
done
test_cmp "$1" "$2"
}
test_run_repeat_60_sec() {
for i in $(test_seq 1 600)
do
(test_eval_ "$1") && return
go-sleep 100ms
done
return 1 # failed
}
test_wait_output_n_lines() {
for i in $(test_seq 1 3600)
do
test $(cat "$1" | wc -l | tr -d " ") -ge $2 && return
go-sleep 100ms
done
actual=$(cat "$1" | wc -l | tr -d " ")
test_fsh "expected $2 lines of output. got $actual"
}
test_wait_open_tcp_port_10_sec() {
for i in $(test_seq 1 100)
do
# this is not a perfect check, but it's portable.
# can't count on ss. not installed everywhere.
# can't count on netstat using : or . as port delim. differ across platforms.
echo $(netstat -aln | egrep "^tcp.*LISTEN" | egrep "[.:]$1" | wc -l) -gt 0
if [ $(netstat -aln | egrep "^tcp.*LISTEN" | egrep "[.:]$1" | wc -l) -gt 0 ]; then
return 0
fi
go-sleep 100ms
done
return 1
}
# test_config_set helps us make sure _we really did set_ a config value.
# it sets it and then tests it. This became elaborate because ipfs config
# was setting really weird things and am not sure why.
test_config_set() {
# grab flags (like --bool in "ipfs config --bool")
test_cfg_flags="" # unset in case.
test "$#" = 3 && { test_cfg_flags=$1; shift; }
test_cfg_key=$1
test_cfg_val=$2
# when verbose, tell the user what config values are being set
test_cfg_cmd="ipfs config $test_cfg_flags \"$test_cfg_key\" \"$test_cfg_val\""
test "$TEST_VERBOSE" = 1 && echo "$test_cfg_cmd"
# ok try setting the config key/val pair.
ipfs config $test_cfg_flags "$test_cfg_key" "$test_cfg_val"
echo "$test_cfg_val" >cfg_set_expected
ipfs config "$test_cfg_key" >cfg_set_actual
test_cmp cfg_set_expected cfg_set_actual
}
test_init_ipfs() {
args=("$@")
# we set the Addresses.API config variable.
# the cli client knows to use it, so only need to set.
# todo: in the future, use env?
test_expect_success "ipfs init succeeds" '
export IPFS_PATH="$(pwd)/.ipfs" &&
ipfs init "${args[@]}" --profile=test > /dev/null
'
test_expect_success "disable telemetry" '
test_config_set --bool Plugins.Plugins.telemetry.Disabled "true"
'
test_expect_success "prepare config -- mounting" '
mkdir mountdir ipfs ipns mfs &&
test_config_set Mounts.IPFS "$(pwd)/ipfs" &&
test_config_set Mounts.IPNS "$(pwd)/ipns" &&
test_config_set Mounts.MFS "$(pwd)/mfs" ||
test_fsh cat "\"$IPFS_PATH/config\""
'
}
test_init_ipfs_measure() {
args=("$@")
# we set the Addresses.API config variable.
# the cli client knows to use it, so only need to set.
# todo: in the future, use env?
test_expect_success "ipfs init succeeds" '
export IPFS_PATH="$(pwd)/.ipfs" &&
ipfs init "${args[@]}" --profile=test,flatfs-measure > /dev/null
'
test_expect_success "disable telemetry" '
test_config_set --bool Plugins.Plugins.telemetry.Disabled "true"
'
test_expect_success "prepare config -- mounting" '
mkdir mountdir ipfs ipns &&
test_config_set Mounts.IPFS "$(pwd)/ipfs" &&
test_config_set Mounts.IPNS "$(pwd)/ipns" ||
test_fsh cat "\"$IPFS_PATH/config\""
'
}
test_wait_for_file() {
loops=$1
delay=$2
file=$3
fwaitc=0
while ! test -f "$file"
do
if test $fwaitc -ge $loops
then
echo "Error: timed out waiting for file: $file"
return 1
fi
go-sleep $delay
fwaitc=`expr $fwaitc + 1`
done
}
test_set_address_vars() {
daemon_output="$1"
test_expect_success "set up address variables" '
API_MADDR=$(cat "$IPFS_PATH/api") &&
API_ADDR=$(convert_tcp_maddr $API_MADDR) &&
API_PORT=$(port_from_maddr $API_MADDR) &&
GWAY_MADDR=$(sed -n "s/^Gateway server listening on //p" "$daemon_output") &&
GWAY_ADDR=$(convert_tcp_maddr $GWAY_MADDR) &&
GWAY_PORT=$(port_from_maddr $GWAY_MADDR)
'
if ipfs swarm addrs local >/dev/null 2>&1; then
test_expect_success "get swarm addresses" '
ipfs swarm addrs local > addrs_out
'
test_expect_success "set swarm address vars" '
SWARM_MADDR=$(grep "127.0.0.1" addrs_out) &&
SWARM_PORT=$(port_from_maddr $SWARM_MADDR)
'
fi
}
test_launch_ipfs_daemon() {
args=("$@")
test "$TEST_ULIMIT_PRESET" != 1 && ulimit -n 2048
test_expect_success "'ipfs daemon' succeeds" '
ipfs daemon "${args[@]}" >actual_daemon 2>daemon_err &
IPFS_PID=$!
'
# wait for api file to show up
test_expect_success "api file shows up" '
test_wait_for_file 50 200ms "$IPFS_PATH/api"
'
test_set_address_vars actual_daemon
# we say the daemon is ready when the API server is ready.
test_expect_success "'ipfs daemon' is ready" '
pollEndpoint -host=$API_MADDR -v -tout=1s -tries=60 2>poll_apierr > poll_apiout ||
test_fsh cat actual_daemon || test_fsh cat daemon_err || test_fsh cat poll_apierr || test_fsh cat poll_apiout
'
}
test_launch_ipfs_daemon_without_network() {
test_launch_ipfs_daemon --offline "$@"
}
do_umount() {
local mount_point="$1"
local max_retries=3
local retry_delay=0.5
# Try normal unmount first (without lazy flag)
for i in $(seq 1 $max_retries); do
if [ "$(uname -s)" = "Linux" ]; then
# First attempt: standard unmount
if fusermount -u "$mount_point" 2>/dev/null; then
return 0
fi
else
if umount "$mount_point" 2>/dev/null; then
return 0
fi
fi
# If not last attempt, wait before retry
if [ $i -lt $max_retries ]; then
go-sleep "${retry_delay}s"
fi
done
# If normal unmount failed, try lazy unmount as last resort (Linux only)
if [ "$(uname -s)" = "Linux" ]; then
# Log that we're falling back to lazy unmount
test "$TEST_VERBOSE" = 1 && echo "# Warning: falling back to lazy unmount for $mount_point"
fusermount -z -u "$mount_point" 2>/dev/null
else
# On non-Linux, try force unmount
umount -f "$mount_point" 2>/dev/null || true
fi
}
test_mount_ipfs() {
# make sure stuff is unmounted first.
test_expect_success FUSE "'ipfs mount' succeeds" '
do_umount "$(pwd)/ipfs" || true &&
do_umount "$(pwd)/ipns" || true &&
do_umount "$(pwd)/mfs" || true &&
ipfs mount >actual
'
test_expect_success FUSE "'ipfs mount' output looks good" '
echo "IPFS mounted at: $(pwd)/ipfs" >expected &&
echo "IPNS mounted at: $(pwd)/ipns" >>expected &&
echo "MFS mounted at: $(pwd)/mfs" >>expected &&
test_cmp expected actual
'
}
test_launch_ipfs_daemon_and_mount() {
test_init_ipfs
test_launch_ipfs_daemon
test_mount_ipfs
}
test_kill_repeat_10_sec() {
# try to shut down once + wait for graceful exit
kill $1
for i in $(test_seq 1 100)
do
go-sleep 100ms
! kill -0 $1 2>/dev/null && return
done
# if not, try once more, which will skip graceful exit
kill $1
go-sleep 1s
! kill -0 $1 2>/dev/null && return
# ok, no hope. kill it to prevent it messing with other tests
kill -9 $1 2>/dev/null
return 1
}
test_kill_ipfs_daemon() {
test_expect_success "'ipfs daemon' is still running" '
kill -0 $IPFS_PID
'
test_expect_success "'ipfs daemon' can be killed" '
test_kill_repeat_10_sec $IPFS_PID
'
}
test_curl_resp_http_code() {
curl -I "$1" >curl_output || {
echo "curl error with url: '$1'"
echo "curl output was:"
cat curl_output
return 1
}
shift &&
RESP=$(head -1 curl_output) &&
while test "$#" -gt 0
do
expr "$RESP" : "$1" >/dev/null && return
shift
done
echo "curl response didn't match!"
echo "curl response was: '$RESP'"
echo "curl output was:"
cat curl_output
return 1
}
test_must_be_empty() {
if test -s "$1"
then
echo "'$1' is not empty, it contains:"
cat "$1"
return 1
fi
}
test_should_contain() {
test "$#" = 2 || error "bug in the test script: not 2 parameters to test_should_contain"
if ! grep -q "$1" "$2"
then
echo "'$2' does not contain '$1', it contains:"
cat "$2"
return 1
fi
}
test_should_not_contain() {
test "$#" = 2 || error "bug in the test script: not 2 parameters to test_should_not_contain"
if grep -q "$1" "$2"
then
echo "'$2' contains undesired value '$1'"
return 1
fi
}
test_str_contains() {
find=$1
shift
echo "$@" | egrep "\b$find\b" >/dev/null
}
disk_usage() {
# normalize du across systems
case $(uname -s) in
Linux)
DU="du -sb"
M=1
;;
FreeBSD)
DU="du -s -A -B 1"
M=512
;;
Darwin | DragonFly | *)
DU="du -s"
M=512
;;
esac
expr $($DU "$1" | awk "{print \$1}") "*" "$M"
}
# output a file's permission in human readable format
generic_stat() {
# normalize stat across systems
case $(uname -s) in
Linux)
_STAT="stat -c %A"
;;
FreeBSD | Darwin | DragonFly)
_STAT="stat -f %Sp"
;;
*)
echo "unsupported OS" >&2
exit 1
;;
esac
$_STAT "$1" || echo "failed" # Avoid returning nothing.
}
# output a file's permission in human readable format
file_size() {
case $(uname -s) in
Linux)
_STAT="stat --format=%s"
;;
FreeBSD | Darwin | DragonFly)
_STAT="stat -f%z"
;;
*)
echo "unsupported OS" >&2
exit 1
;;
esac
$_STAT "$1"
}
# len 46: 2048-bit RSA keys, b58mh-encoded
# len 52: ED25519 keys, b58mh-encoded
# len 56: 2048-bit RSA keys, base36-encoded
# len 62: ED25519 keys, base36-encoded
test_check_peerid() {
peeridlen=$(echo "$1" | tr -dC "[:alnum:]" | wc -c | tr -d " ") &&
test "$peeridlen" = "46" -o "$peeridlen" = "52" -o "$peeridlen" = "56" -o "$peeridlen" = "62" || {
echo "Bad peerid '$1' with len '$peeridlen'"
return 1
}
}
test_check_rsa2048_b58mh_peerid() {
peeridlen=$(echo "$1" | tr -dC "[:alnum:]" | wc -c | tr -d " ") &&
test "$peeridlen" = "46" || {
echo "Bad RSA2048 B58MH peerid '$1' with len '$peeridlen'"
return 1
}
}
test_check_ed25519_b58mh_peerid() {
peeridlen=$(echo "$1" | tr -dC "[:alnum:]" | wc -c | tr -d " ") &&
test "$peeridlen" = "52" || {
echo "Bad ED25519 B58MH peerid '$1' with len '$peeridlen'"
return 1
}
}
test_check_rsa2048_base36_peerid() {
peeridlen=$(echo "$1" | tr -dC "[:alnum:]" | wc -c | tr -d " ") &&
test "$peeridlen" = "56" || {
echo "Bad RSA2048 B36CID peerid '$1' with len '$peeridlen'"
return 1
}
}
test_check_ed25519_base36_peerid() {
peeridlen=$(echo "$1" | tr -dC "[:alnum:]" | wc -c | tr -d " ") &&
test "$peeridlen" = "62" || {
echo "Bad ED25519 B36CID peerid '$1' with len '$peeridlen'"
return 1
}
}
convert_tcp_maddr() {
echo $1 | awk -F'/' '{ printf "%s:%s", $3, $5 }'
}
port_from_maddr() {
echo $1 | awk -F'/' '{ print $NF }'
}
findprovs_empty() {
test_expect_success 'findprovs '$1' succeeds' '
ipfsi 1 routing findprovs -n 1 '$1' > findprovsOut
'
test_expect_success "findprovs $1 output is empty" '
test_must_be_empty findprovsOut
'
}
findprovs_expect() {
test_expect_success 'findprovs '$1' succeeds' '
ipfsi 1 routing findprovs -n 1 '$1' > findprovsOut &&
echo '$2' > expected
'
test_expect_success "findprovs $1 output looks good" '
test_cmp findprovsOut expected
'
}
purge_blockstore() {
ipfs pin ls --quiet --type=recursive | ipfs pin rm &>/dev/null
ipfs repo gc --silent &>/dev/null
test_expect_success "pinlist empty" '
[[ -z "$( ipfs pin ls )" ]]
'
test_expect_success "nothing left to gc" '
[[ -z "$( ipfs repo gc )" ]]
'
}