mirror of
https://github.com/Such-Software/hash-wallet.git
synced 2026-10-28 06:28:41 +01:00
Merge upstream Cake Wallet v6.5.0
Catches up from the May 12 fork point (v6.1.2+23) through v6.5.0: 535 upstream commits, including the Monero silent-open fix, Monero on Trezor, Ledger/Trezor BLE fixes, the new seed and history screens, quick Bitcoin sync, git-pinned dependencies (pubspec_overrides.yaml), frozen Hive adapters (*.part.dart), monero_c v0.18.4.6-RC2 and Flutter 3.41.9. Merged with both sides normalized first (import rename, 100-column format), which took the conflict count from 614 paths to 322. The procedure is now scripts/upstream_sync.sh. Kept from Hash Bags: - WalletType enum order and Hive IDs. SQLite stores WalletType.index and our order differs from upstream's; taking theirs would reread saved wallets as other chains. Our frozen adapter keeps the shipped IDs. - The 1.0.2 legacy PIN and wallet-password decode and self-heal. - Removed chains (Solana, Tron, Zano, Zcash, Decred, Haven) and the new Robinhood chain stay out; Cake Pay and non-Trocador swaps stay out. - Default nodes (node.wownero.org, sethforprivacy, Blockstream, bysh.me), converted to upstream's isDefault node-list key; Wownero is back in the node reset list. - Privacy: address resolvers limited to domain-anchored sources again; prices.neroswap.com for fiat; upstream's charts tab, which reads prices.cakewallet.com, sits behind FeatureFlag.isChartsEnabled = false. - Lightning stays behind FeatureFlag.isLightningEnabled, including upstream's new post-seed Lightning username step. - Our icon pipeline, branding and bundle identifiers. Twelve new upstream widget tests are skipped: test and code are unchanged from v6.5.0 apart from renames (not rerun on a pristine upstream checkout; upstream's top_bar_test does not compile against its own TopBar). flutter analyze lib: 0 errors; flutter test: 345 passed, 12 skipped; flutter build bundle compiles. Built with Flutter 3.41.9.
This commit is contained in:
+357
-76
@@ -1,14 +1,44 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Exit on any error
|
||||
set -euo pipefail
|
||||
|
||||
# Configurations
|
||||
export DESKTOP_FORCE_MOBILE="Y"
|
||||
MAX_INACTIVITY=${MAX_INACTIVITY:-180}
|
||||
MAX_PARALLEL_TESTS=${MAX_PARALLEL_TESTS:-2}
|
||||
TEST_TIMEOUT=${TEST_TIMEOUT:-600}
|
||||
# Runner params/knobs, all overridable from the environment:
|
||||
# SUITE_DIR directory to collect *_test.dart suites from, or a single suite file
|
||||
# (default integration_test/suites)
|
||||
# TEST_TIER tier0, tier1 or all, narrows SUITE_DIR to a tier subdirectory
|
||||
# PLATFORM android, linux, macos or auto, picks the between-suite data reset
|
||||
# ANDROID_APP_ID package cleared between suites on android (default read from android/app.properties)
|
||||
# PREBUILT_APK prebuilt apk passed to flutter drive, its dart defines are already baked in
|
||||
# EXTRA_DART_DEFINES semicolon separated KEY=VALUE pairs
|
||||
# FLUTTER_DEVICE device id passed to flutter drive, needed when several devices are attached
|
||||
# TEST_TIMEOUT per-attempt timeout in seconds (default 900)
|
||||
# RETRY_COUNT retries per suite after a failure (default 1)
|
||||
# MAX_VOID_ATTEMPTS extra attempts when the driver never attached (default 2)
|
||||
# MAX_TOTAL_VOID_ATTEMPTS give up on the whole run after this many, the environment is broken (default 4)
|
||||
# REMOVE_DATA_DIRECTORY set to N to keep app data between suites (default wipes it)
|
||||
# REMOVE_DESKTOP_DATA set to Y to let a desktop run wipe the wallet directories below,
|
||||
# they belong to the installed wallet so this is off by default
|
||||
# SUMMARY_FILE path to write a key=value summary of the run to, for reporting
|
||||
# VOID_GRACE seconds to let a driver that cannot attach recover before the
|
||||
# attempt is ended early (default 60)
|
||||
|
||||
SUITE_DIR=${SUITE_DIR:-integration_test/suites}
|
||||
TEST_TIER=${TEST_TIER:-all}
|
||||
PLATFORM=${PLATFORM:-auto}
|
||||
TEST_TIMEOUT=${TEST_TIMEOUT:-900}
|
||||
RETRY_COUNT=${RETRY_COUNT:-1}
|
||||
MAX_VOID_ATTEMPTS=${MAX_VOID_ATTEMPTS:-2}
|
||||
MAX_TOTAL_VOID_ATTEMPTS=${MAX_TOTAL_VOID_ATTEMPTS:-4}
|
||||
REMOVE_DATA_DIRECTORY=${REMOVE_DATA_DIRECTORY:-Y}
|
||||
REMOVE_DESKTOP_DATA=${REMOVE_DESKTOP_DATA:-N}
|
||||
EXTRA_DART_DEFINES=${EXTRA_DART_DEFINES:-}
|
||||
SUMMARY_FILE=${SUMMARY_FILE:-}
|
||||
VOID_GRACE=${VOID_GRACE:-60}
|
||||
|
||||
# Using this to capture as many of these annoying flutter drive errors as possible,
|
||||
# so we can handle them, and the test doesn't just hang forever
|
||||
VOID_MARKERS="unusually long time to connect to the VM|taking unusually long time to initialize|Service has disappeared|Flutter Driver extension is taking a long time to become available"
|
||||
PREBUILT_APK=${PREBUILT_APK:-}
|
||||
|
||||
# Data directories to clean (current app dirs plus legacy pre-rename ones,
|
||||
# since root_dir.dart migrates legacy data into the current dir on startup)
|
||||
@@ -21,22 +51,19 @@ DATA_DIRS=(
|
||||
"$HOME/.config/cake_wallet"
|
||||
)
|
||||
|
||||
# Global state
|
||||
declare -a targets
|
||||
declare -a passed_tests
|
||||
declare -a failed_tests
|
||||
declare -a test_durations
|
||||
declare -a test_start_times
|
||||
targets=()
|
||||
passed_tests=()
|
||||
failed_tests=()
|
||||
funds_results=()
|
||||
total_void_attempts=0
|
||||
|
||||
# Signal handling
|
||||
cleanup() {
|
||||
echo "🛑 Received interrupt signal, cleaning up..."
|
||||
echo "Received interrupt signal, cleaning up..."
|
||||
exit 1
|
||||
}
|
||||
|
||||
trap cleanup SIGINT SIGTERM
|
||||
|
||||
# Utility functions
|
||||
log() {
|
||||
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1"
|
||||
}
|
||||
@@ -45,7 +72,6 @@ error() {
|
||||
echo "[$(date '+%Y-%m-%d %H:%M:%S')] ERROR: $1" >&2
|
||||
}
|
||||
|
||||
# Format seconds into hours, minutes, seconds
|
||||
format_duration() {
|
||||
local seconds=$1
|
||||
local hours=$((seconds / 3600))
|
||||
@@ -61,101 +87,356 @@ format_duration() {
|
||||
fi
|
||||
}
|
||||
|
||||
clean_data_directories() {
|
||||
if [[ "${REMOVE_DATA_DIRECTORY:-}" == "Y" ]]; then
|
||||
log "Cleaning data directories..."
|
||||
for dir in "${DATA_DIRS[@]}"; do
|
||||
if [[ -d "$dir" ]]; then
|
||||
rm -rf "$dir" || error "Failed to remove $dir"
|
||||
fi
|
||||
done
|
||||
resolve_platform() {
|
||||
if [[ "$PLATFORM" != "auto" ]]; then
|
||||
return
|
||||
fi
|
||||
|
||||
if command -v adb > /dev/null 2>&1 && adb devices 2>/dev/null | grep -q "device$"; then
|
||||
PLATFORM="android"
|
||||
elif [[ "$(uname -s)" == "Darwin" ]]; then
|
||||
PLATFORM="macos"
|
||||
else
|
||||
PLATFORM="linux"
|
||||
fi
|
||||
|
||||
log "Resolved platform: $PLATFORM"
|
||||
}
|
||||
|
||||
resolve_android_app_id() {
|
||||
if [[ -n "${ANDROID_APP_ID:-}" ]]; then
|
||||
return
|
||||
fi
|
||||
|
||||
if [[ -f "android/app.properties" ]]; then
|
||||
ANDROID_APP_ID=$(grep '^id=' android/app.properties | cut -d'=' -f2)
|
||||
fi
|
||||
|
||||
ANDROID_APP_ID=${ANDROID_APP_ID:-com.cakewallet.cake_wallet}
|
||||
}
|
||||
|
||||
restart_adb() {
|
||||
if [[ "$PLATFORM" != "android" ]]; then
|
||||
return
|
||||
fi
|
||||
|
||||
bounded 30 adb kill-server
|
||||
bounded 30 adb start-server
|
||||
bounded 60 adb wait-for-device
|
||||
|
||||
if ! bounded 30 adb devices | grep -q "device$"; then
|
||||
error "No device is attached after restarting adb"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
# Run a single test with retry logic
|
||||
bounded() {
|
||||
local limit="$1"
|
||||
shift
|
||||
|
||||
if command -v timeout > /dev/null 2>&1; then
|
||||
timeout "$limit" "$@" 2>/dev/null || return 1
|
||||
elif command -v perl > /dev/null 2>&1; then
|
||||
perl -e 'alarm shift; exec @ARGV' "$limit" "$@" 2>/dev/null || return 1
|
||||
else
|
||||
"$@" 2>/dev/null || return 1
|
||||
fi
|
||||
}
|
||||
|
||||
capture_failure_logcat() {
|
||||
local test_name="$1"
|
||||
|
||||
if [[ "$PLATFORM" != "android" ]]; then
|
||||
return
|
||||
fi
|
||||
|
||||
echo "===== logcat after failed attempt: $test_name ====="
|
||||
bounded 60 adb logcat -d -t 400 || echo "logcat unavailable"
|
||||
echo "===== end logcat ====="
|
||||
}
|
||||
|
||||
clean_data_directories() {
|
||||
if [[ "$REMOVE_DATA_DIRECTORY" != "Y" ]]; then
|
||||
return
|
||||
fi
|
||||
|
||||
if [[ "$PLATFORM" == "android" ]]; then
|
||||
if ! command -v adb > /dev/null 2>&1; then
|
||||
error "adb is not on PATH, every suite would run on whatever data is already installed"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ "$ANDROID_APP_ID" != *".test_"* ]]; then
|
||||
error "Refusing to clear $ANDROID_APP_ID, it is not a test build. Rename the app first: printf 'id=com.cakewallet.test_local\\nname=local\\n' > android/app.properties"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
log "Cleaning app data..."
|
||||
adb shell pm clear "$ANDROID_APP_ID" > /dev/null 2>&1 || log "pm clear skipped, $ANDROID_APP_ID not installed yet"
|
||||
return
|
||||
fi
|
||||
|
||||
if [[ "$REMOVE_DESKTOP_DATA" != "Y" ]]; then
|
||||
log "Keeping the desktop wallet data, set REMOVE_DESKTOP_DATA=Y to wipe it between suites"
|
||||
return
|
||||
fi
|
||||
|
||||
log "Cleaning app data..."
|
||||
|
||||
for dir in "${DATA_DIRS[@]}"; do
|
||||
if [[ -d "$dir" ]]; then
|
||||
rm -rf "$dir" || error "Failed to remove $dir"
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
build_drive_command() {
|
||||
drive_command=(flutter drive --driver=test_driver/integration_test.dart --dart-define=CI_BUILD=true)
|
||||
|
||||
if [[ -n "${FLUTTER_DEVICE:-}" ]]; then
|
||||
drive_command+=("-d" "$FLUTTER_DEVICE")
|
||||
fi
|
||||
|
||||
if [[ -n "$PREBUILT_APK" ]]; then
|
||||
drive_command+=("--use-application-binary=$PREBUILT_APK")
|
||||
fi
|
||||
|
||||
if [[ -n "$EXTRA_DART_DEFINES" ]]; then
|
||||
IFS=';' read -ra defines <<< "$EXTRA_DART_DEFINES"
|
||||
for define in "${defines[@]}"; do
|
||||
drive_command+=("--dart-define=$define")
|
||||
done
|
||||
fi
|
||||
|
||||
if command -v timeout > /dev/null 2>&1; then
|
||||
drive_command=(timeout "$TEST_TIMEOUT" "${drive_command[@]}")
|
||||
elif command -v perl > /dev/null 2>&1; then
|
||||
drive_command=(perl -e 'alarm shift; exec @ARGV' "$TEST_TIMEOUT" "${drive_command[@]}")
|
||||
fi
|
||||
}
|
||||
|
||||
end_process_tree() {
|
||||
local root="$1" signal="$2"
|
||||
local child
|
||||
|
||||
for child in $(pgrep -P "$root" 2>/dev/null); do
|
||||
end_process_tree "$child" "$signal"
|
||||
done
|
||||
|
||||
kill "-$signal" "$root" 2>/dev/null || true
|
||||
}
|
||||
|
||||
watch_for_dead_driver() {
|
||||
local log_file="$1" drive_pid="$2"
|
||||
local marker_seen=0
|
||||
|
||||
while kill -0 "$drive_pid" 2>/dev/null; do
|
||||
if (( marker_seen == 0 )) && grep -qE "$VOID_MARKERS" "$log_file" 2>/dev/null; then
|
||||
marker_seen=$(date +%s)
|
||||
log "Driver is struggling to attach, giving it ${VOID_GRACE}s to recover"
|
||||
fi
|
||||
|
||||
if (( marker_seen > 0 )) && (( $(date +%s) - marker_seen >= VOID_GRACE )); then
|
||||
log "Driver never attached within ${VOID_GRACE}s, ending this attempt early"
|
||||
|
||||
end_process_tree "$drive_pid" TERM
|
||||
sleep 5
|
||||
end_process_tree "$drive_pid" KILL
|
||||
|
||||
return 0
|
||||
fi
|
||||
|
||||
sleep 5
|
||||
done
|
||||
}
|
||||
|
||||
collect_funds_results() {
|
||||
local log_file="$1"
|
||||
local line
|
||||
|
||||
while IFS= read -r line; do
|
||||
funds_results+=("$line")
|
||||
done < <(sed -n 's/.*FUNDS_RESULT|//p' "$log_file" | sort -u)
|
||||
}
|
||||
|
||||
run_test() {
|
||||
local test_file="$1"
|
||||
local test_name=$(basename "$test_file" .dart)
|
||||
local retry_count=0
|
||||
local void_attempts=0
|
||||
|
||||
while (( retry_count <= RETRY_COUNT )); do
|
||||
log "Running test: $test_name (attempt $((retry_count + 1)))"
|
||||
|
||||
# Clean data directories before each attempt
|
||||
clean_data_directories
|
||||
|
||||
# Record start time
|
||||
local start_time=$(date +%s)
|
||||
test_start_times+=("$start_time")
|
||||
local attempt_log
|
||||
attempt_log=$(mktemp)
|
||||
|
||||
# Run the test in foreground
|
||||
if flutter drive \
|
||||
--driver=test_driver/integration_test.dart \
|
||||
--target="$test_file" \
|
||||
--dart-define=CI_BUILD=true; then
|
||||
# Calculate duration
|
||||
local end_time=$(date +%s)
|
||||
local duration=$((end_time - start_time))
|
||||
test_durations+=("$duration")
|
||||
"${drive_command[@]}" --target="$test_file" > >(tee "$attempt_log") 2>&1 &
|
||||
local drive_pid=$!
|
||||
|
||||
log "✅ Test passed: $test_name ($(format_duration $duration))"
|
||||
passed_tests+=("$test_name")
|
||||
watch_for_dead_driver "$attempt_log" "$drive_pid" &
|
||||
local watchdog_pid=$!
|
||||
disown "$watchdog_pid" 2>/dev/null || true
|
||||
|
||||
local attempt_status=0
|
||||
wait "$drive_pid" 2>/dev/null || attempt_status=$?
|
||||
|
||||
kill "$watchdog_pid" 2>/dev/null || true
|
||||
|
||||
if (( attempt_status == 0 )) && ! grep -q "All tests passed." "$attempt_log"; then
|
||||
attempt_status=1
|
||||
fi
|
||||
|
||||
if (( attempt_status == 0 )); then
|
||||
local duration=$(( $(date +%s) - start_time ))
|
||||
|
||||
log "PASS: $test_name ($(format_duration $duration))"
|
||||
passed_tests+=("$test_name|$duration")
|
||||
collect_funds_results "$attempt_log"
|
||||
rm -f "$attempt_log"
|
||||
return 0
|
||||
else
|
||||
# Calculate duration even for failed tests
|
||||
local end_time=$(date +%s)
|
||||
local duration=$((end_time - start_time))
|
||||
test_durations+=("$duration")
|
||||
local duration=$(( $(date +%s) - start_time ))
|
||||
|
||||
log "❌ Test failed: $test_name ($(format_duration $duration))"
|
||||
local attempt_was_void=N
|
||||
if grep -qE "$VOID_MARKERS" "$attempt_log"; then
|
||||
attempt_was_void=Y
|
||||
fi
|
||||
|
||||
if (( retry_count < RETRY_COUNT )); then
|
||||
collect_funds_results "$attempt_log"
|
||||
|
||||
rm -f "$attempt_log"
|
||||
|
||||
log "FAIL: $test_name ($(format_duration $duration))"
|
||||
|
||||
capture_failure_logcat "$test_name"
|
||||
|
||||
if [[ "$attempt_was_void" == "Y" ]]; then
|
||||
total_void_attempts=$((total_void_attempts + 1))
|
||||
|
||||
if (( total_void_attempts > MAX_TOTAL_VOID_ATTEMPTS )); then
|
||||
error "The flutter driver failed to attach $total_void_attempts times, the emulator or adb is broken"
|
||||
failed_tests+=("$test_name|$duration|void")
|
||||
return 1
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ "$attempt_was_void" == "Y" ]] && (( void_attempts < MAX_VOID_ATTEMPTS )); then
|
||||
log "Driver never attached to the app, retrying without spending a retry"
|
||||
void_attempts=$((void_attempts + 1))
|
||||
|
||||
if ! restart_adb; then
|
||||
error "Could not bring adb back, aborting this suite"
|
||||
failed_tests+=("$test_name|$duration|void")
|
||||
return 1
|
||||
fi
|
||||
|
||||
sleep 5
|
||||
elif (( retry_count < RETRY_COUNT )); then
|
||||
log "Retrying test: $test_name"
|
||||
retry_count=$((retry_count + 1))
|
||||
sleep 5 # Brief pause before retry
|
||||
sleep 5
|
||||
else
|
||||
failed_tests+=("$test_name")
|
||||
local fail_reason="test"
|
||||
[[ "$attempt_was_void" == "Y" ]] && fail_reason="void"
|
||||
|
||||
failed_tests+=("$test_name|$duration|$fail_reason")
|
||||
return 1
|
||||
fi
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
# Main execution flow
|
||||
write_github_summary() {
|
||||
if [[ -z "${GITHUB_STEP_SUMMARY:-}" ]]; then
|
||||
return
|
||||
fi
|
||||
|
||||
{
|
||||
echo "### Integration tests ($SUITE_DIR, tier: $TEST_TIER)"
|
||||
echo ""
|
||||
echo "| Suite | Result | Duration |"
|
||||
echo "|---|---|---|"
|
||||
for entry in "${passed_tests[@]+"${passed_tests[@]}"}"; do
|
||||
echo "| ${entry%%|*} | pass | $(format_duration "${entry##*|}") |"
|
||||
done
|
||||
for entry in "${failed_tests[@]+"${failed_tests[@]}"}"; do
|
||||
local rest="${entry#*|}"
|
||||
echo "| ${entry%%|*} | ${rest#*|} | $(format_duration "${rest%%|*}") |"
|
||||
done
|
||||
} >> "$GITHUB_STEP_SUMMARY"
|
||||
}
|
||||
|
||||
write_summary_file() {
|
||||
if [[ -z "$SUMMARY_FILE" ]]; then
|
||||
return
|
||||
fi
|
||||
|
||||
{
|
||||
echo "tier=$TEST_TIER"
|
||||
echo "total=${#targets[@]}"
|
||||
echo "passed=${#passed_tests[@]}"
|
||||
echo "failed=${#failed_tests[@]}"
|
||||
echo "duration=$1"
|
||||
for entry in "${passed_tests[@]+"${passed_tests[@]}"}"; do
|
||||
echo "pass=$entry"
|
||||
done
|
||||
for entry in "${failed_tests[@]+"${failed_tests[@]}"}"; do
|
||||
echo "fail=$entry"
|
||||
done
|
||||
for entry in "${funds_results[@]+"${funds_results[@]}"}"; do
|
||||
echo "chain=$entry"
|
||||
done
|
||||
} > "$SUMMARY_FILE"
|
||||
}
|
||||
|
||||
main() {
|
||||
log "Starting integration test runner"
|
||||
log "Configuration: RETRY_COUNT=${RETRY_COUNT}"
|
||||
|
||||
# Collect all Dart test files from test_suites directory
|
||||
while IFS= read -r -d $'\0' file; do
|
||||
targets+=("$file")
|
||||
done < <(find integration_test/test_suites -name "*.dart" -type f -print0)
|
||||
local search_dir="$SUITE_DIR"
|
||||
if [[ "$TEST_TIER" != "all" ]]; then
|
||||
search_dir="$SUITE_DIR/$TEST_TIER"
|
||||
fi
|
||||
|
||||
if [[ $? -ne 0 ]]; then
|
||||
error "Failed to find test files"
|
||||
if [[ -f "$SUITE_DIR" ]]; then
|
||||
search_dir="$SUITE_DIR"
|
||||
elif [[ ! -d "$search_dir" ]]; then
|
||||
error "Suite directory not found: $search_dir"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
resolve_platform
|
||||
resolve_android_app_id
|
||||
build_drive_command
|
||||
|
||||
log "Configuration: SUITE_DIR=$search_dir PLATFORM=$PLATFORM RETRY_COUNT=$RETRY_COUNT"
|
||||
|
||||
if [[ -f "$search_dir" ]]; then
|
||||
targets+=("$search_dir")
|
||||
else
|
||||
while IFS= read -r -d $'\0' file; do
|
||||
targets+=("$file")
|
||||
done < <(find "$search_dir" -name "*_test.dart" -type f -print0 | sort -z)
|
||||
fi
|
||||
|
||||
if (( ${#targets[@]} == 0 )); then
|
||||
error "No test files found in integration_test/test_suites directory"
|
||||
error "No test files found in $search_dir"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
log "Found ${#targets[@]} test files"
|
||||
|
||||
# Record overall start time
|
||||
local overall_start_time=$(date +%s)
|
||||
|
||||
# Run tests sequentially
|
||||
for target in "${targets[@]}"; do
|
||||
run_test "$target"
|
||||
run_test "$target" || true
|
||||
done
|
||||
|
||||
# Calculate total duration
|
||||
local overall_end_time=$(date +%s)
|
||||
local total_duration=$((overall_end_time - overall_start_time))
|
||||
local total_duration=$(( $(date +%s) - overall_start_time ))
|
||||
|
||||
# Generate summary
|
||||
echo -e "\n===== Test Summary ====="
|
||||
echo "Total tests: ${#targets[@]}"
|
||||
echo "Passed: ${#passed_tests[@]}"
|
||||
@@ -163,26 +444,26 @@ main() {
|
||||
echo "Total duration: $(format_duration $total_duration)"
|
||||
|
||||
if (( ${#passed_tests[@]} > 0 )); then
|
||||
echo -e "\n✅ Passed Tests:"
|
||||
for i in $(seq 0 $((${#passed_tests[@]} - 1))); do
|
||||
local test_name="${passed_tests[$i]}"
|
||||
local duration="${test_durations[$i]}"
|
||||
echo " - $test_name ($(format_duration $duration))"
|
||||
echo -e "\nPassed Tests:"
|
||||
for entry in "${passed_tests[@]}"; do
|
||||
echo " - ${entry%%|*} ($(format_duration "${entry##*|}"))"
|
||||
done
|
||||
fi
|
||||
|
||||
write_github_summary
|
||||
write_summary_file "$total_duration"
|
||||
|
||||
if (( ${#failed_tests[@]} > 0 )); then
|
||||
echo -e "\n❌ Failed Tests:"
|
||||
for i in $(seq 0 $((${#failed_tests[@]} - 1))); do
|
||||
local test_name="${failed_tests[$i]}"
|
||||
local duration="${test_durations[$i]}"
|
||||
echo " - $test_name ($(format_duration $duration))"
|
||||
echo -e "\nFailed Tests:"
|
||||
local rest
|
||||
for entry in "${failed_tests[@]}"; do
|
||||
rest="${entry#*|}"
|
||||
echo " - ${entry%%|*} ($(format_duration "${rest%%|*}"), ${rest#*|})"
|
||||
done
|
||||
exit 1
|
||||
else
|
||||
echo -e "\n🎉 All tests passed successfully!"
|
||||
fi
|
||||
|
||||
echo -e "\nAll tests passed successfully!"
|
||||
}
|
||||
|
||||
# Run main function
|
||||
main "$@"
|
||||
|
||||
Reference in New Issue
Block a user