diff --git a/Runner/plans/meta-qcom_PreMerge.yaml b/Runner/plans/meta-qcom_PreMerge.yaml index e5c51e11..a2b3bd76 100755 --- a/Runner/plans/meta-qcom_PreMerge.yaml +++ b/Runner/plans/meta-qcom_PreMerge.yaml @@ -14,6 +14,8 @@ metadata: run: steps: - cd Runner + - $PWD/suites/Connectivity/Ethernet/run.sh || true + - $PWD/utils/send-to-lava.sh $PWD/suites/Connectivity/Ethernet/Ethernet.res || true - $PWD/suites/Kernel/FunctionalArea/baseport/adsp_remoteproc/run.sh || true - $PWD/utils/send-to-lava.sh $PWD/suites/Kernel/FunctionalArea/baseport/adsp_remoteproc/adsp_remoteproc.res || true - $PWD/suites/Kernel/FunctionalArea/baseport/cdsp_remoteproc/run.sh || true @@ -36,6 +38,4 @@ run: - $PWD/utils/send-to-lava.sh $PWD/suites/Multimedia/Audio/AudioRecord/AudioRecord.res || true - $PWD/suites/Connectivity/Bluetooth/run.sh || true - $PWD/utils/send-to-lava.sh $PWD/suites/Connectivity/Bluetooth/Bluetooth.res || true - - $PWD/suites/Connectivity/Ethernet/run.sh || true - - $PWD/utils/send-to-lava.sh $PWD/suites/Connectivity/Ethernet/Ethernet.res || true - $PWD/utils/result_parse.sh diff --git a/Runner/suites/Connectivity/Ethernet/README.md b/Runner/suites/Connectivity/Ethernet/README.md index 71a2d7e8..10abcc8c 100644 --- a/Runner/suites/Connectivity/Ethernet/README.md +++ b/Runner/suites/Connectivity/Ethernet/README.md @@ -28,6 +28,8 @@ cd scp -r common Runner user@target_device_ip: ssh user@target_device_ip cd /Runner && ./run-test.sh Ethernet +# Optional: specify preferred interface (e.g., eth1) +./run.sh [preferred-interface] ``` ## Prerequisites @@ -53,5 +55,14 @@ Test result will be saved in `Ethernet.res` as: ## Output A .res file is generated in the same directory: -`PASS Ethernet` OR `FAIL Ethernet` +`Ethernet PASS` OR `Ethernet FAIL` +## Sample Log +``` +Output + +[INFO] 2025-06-11 10:12:23 - Detected Ethernet interface: enP1p4s0u1u1 +[PASS] 2025-06-11 10:12:30 - enP1p4s0u1u1 is UP +[INFO] 2025-06-11 10:12:31 - Assigned IP: 10.0.0.55 +[PASS] 2025-06-11 10:12:35 - Ping successful on enP1p4s0u1u1 +``` diff --git a/Runner/suites/Connectivity/Ethernet/run.sh b/Runner/suites/Connectivity/Ethernet/run.sh index 63c658a9..1fe749fa 100755 --- a/Runner/suites/Connectivity/Ethernet/run.sh +++ b/Runner/suites/Connectivity/Ethernet/run.sh @@ -3,11 +3,10 @@ #Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. #SPDX-License-Identifier: BSD-3-Clause-Clear -# Source init_env and functestlib.sh +# Robustly find and source init_env SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" INIT_ENV="" SEARCH="$SCRIPT_DIR" - while [ "$SEARCH" != "/" ]; do if [ -f "$SEARCH/init_env" ]; then INIT_ENV="$SEARCH/init_env" @@ -15,78 +14,122 @@ while [ "$SEARCH" != "/" ]; do fi SEARCH=$(dirname "$SEARCH") done - + if [ -z "$INIT_ENV" ]; then echo "[ERROR] Could not find init_env (starting at $SCRIPT_DIR)" >&2 exit 1 fi - -# shellcheck disable=SC1090 -. "$INIT_ENV" + +if [ -z "$__INIT_ENV_LOADED" ]; then + # shellcheck disable=SC1090 + . "$INIT_ENV" +fi # shellcheck disable=SC1090,SC1091 . "$TOOLS/functestlib.sh" - + TESTNAME="Ethernet" -test_path=$(find_test_case_by_name "$TESTNAME") || { - log_fail "$TESTNAME : Test directory not found." - echo "FAIL $TESTNAME" > "./$TESTNAME.res" - exit 1 -} - +test_path=$(find_test_case_by_name "$TESTNAME") cd "$test_path" || exit 1 res_file="./$TESTNAME.res" -rm -f "$res_file" +summary_file="./$TESTNAME.summary" +rm -f "$res_file" "$summary_file" log_info "--------------------------------------------------------------------------" log_info "-------------------Starting $TESTNAME Testcase----------------------------" - + +# Check for dependencies check_dependencies ip ping - -IFACE="eth0" -RETRIES=3 -SLEEP_SEC=3 - -# Check interface existence -if ! ip link show "$IFACE" >/dev/null 2>&1; then - log_fail "Ethernet interface $IFACE not found" - echo "FAIL $TESTNAME" > "$res_file" - exit 1 + +# User-specified interface (argument) or all detected +user_iface="$1" +if [ -n "$user_iface" ]; then + ETH_IFACES="$user_iface" + log_info "User specified interface: $user_iface" +else + ETH_IFACES="$(get_ethernet_interfaces)" + log_info "Auto-detected Ethernet interfaces: $ETH_IFACES" fi - -# Bring up interface with retries -log_info "Ensuring $IFACE is UP..." -i=0 -while [ $i -lt $RETRIES ]; do - ip link set "$IFACE" up - sleep "$SLEEP_SEC" - if ip link show "$IFACE" | grep -q "state UP"; then - log_info "$IFACE is UP" - break + +if [ -z "$ETH_IFACES" ]; then + log_warn "No Ethernet interfaces detected." + echo "No Ethernet interfaces detected." >> "$summary_file" + echo "$TESTNAME SKIP" > "$res_file" + exit 0 +fi + +any_passed=0 +any_tested=0 + +for iface in $ETH_IFACES; do + log_info "---- Testing interface: $iface ----" + + if ! is_interface_up "$iface"; then + log_warn "$iface is DOWN, skipping" + echo "$iface: SKIP (down/no cable)" >> "$summary_file" + continue + fi + + ip_addr=$(get_ip_address "$iface") + if [ -z "$ip_addr" ]; then + if try_dhcp_client_safe "$iface" 10; then + ip_addr=$(get_ip_address "$iface") + log_info "$iface obtained IP after DHCP: $ip_addr" + fi + fi + + if [ -z "$ip_addr" ]; then + log_warn "$iface did not obtain an IP address after DHCP attempt, skipping" + echo "$iface: SKIP (no IP, DHCP failed)" >> "$summary_file" + continue + fi + + if echo "$ip_addr" | grep -q '^169\.254'; then + log_warn "$iface got only link-local IP ($ip_addr), skipping" + echo "$iface: SKIP (link-local only: $ip_addr)" >> "$summary_file" + continue + fi + + log_pass "$iface is UP" + log_info "$iface got IP: $ip_addr" + + any_tested=1 + retries=3 + pass=0 + for i in $(seq 1 $retries); do + if ping -I "$iface" -c 4 -W 2 8.8.8.8 >/dev/null 2>&1; then + log_pass "Ethernet connectivity verified via ping" + echo "$iface: PASS (IP: $ip_addr, ping OK)" >> "$summary_file" + pass=1 + any_passed=1 + break + else + log_warn "Ping failed for $iface (attempt $i/$retries)" + sleep 2 + fi + done + + if [ "$pass" -eq 0 ]; then + log_fail "Ping test failed for $iface" + echo "$iface: FAIL (IP: $ip_addr, ping failed)" >> "$summary_file" fi - log_warn "$IFACE is still DOWN (attempt $((i + 1))/$RETRIES)..." - i=$((i + 1)) done - -if [ $i -eq $RETRIES ]; then - log_fail "Failed to bring up $IFACE after $RETRIES attempts" - echo "FAIL $TESTNAME" > "$res_file" + +log_info "---- Ethernet Interface Test Summary ----" +if [ -f "$summary_file" ]; then + cat "$summary_file" +else + log_info "No summary information recorded." +fi + +if [ "$any_passed" -gt 0 ]; then + echo "$TESTNAME PASS" > "$res_file" + exit 0 +elif [ "$any_tested" -gt 0 ]; then + echo "$TESTNAME FAIL" > "$res_file" exit 1 +else + log_warn "No interfaces were tested (all were skipped)." + echo "No suitable Ethernet interfaces found. All were down, link-local, or failed to get IP." >> "$summary_file" + echo "$TESTNAME SKIP" > "$res_file" + exit 0 fi - -# Ping test with retries -log_info "Running ping test to 8.8.8.8 via $IFACE..." -i=0 -while [ $i -lt $RETRIES ]; do - if ping -I "$IFACE" -c 4 -W 2 8.8.8.8 >/dev/null 2>&1; then - log_pass "Ethernet connectivity verified via ping" - echo "PASS $TESTNAME" > "$res_file" - exit 0 - fi - log_warn "Ping failed (attempt $((i + 1))/$RETRIES)... retrying" - sleep "$SLEEP_SEC" - i=$((i + 1)) -done - -log_fail "Ping test failed after $RETRIES attempts" -echo "FAIL $TESTNAME" > "$res_file" -exit 1 diff --git a/Runner/utils/functestlib.sh b/Runner/utils/functestlib.sh index 68612bd7..a2ec407a 100755 --- a/Runner/utils/functestlib.sh +++ b/Runner/utils/functestlib.sh @@ -3,6 +3,9 @@ # Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. # SPDX-License-Identifier: BSD-3-Clause-Clear +# Suppress 'Broken pipe' errors globally in this shell (put this at the very top once) +trap '' PIPE + # --- Logging helpers --- log() { level=$1 @@ -391,3 +394,168 @@ weston_start() { fi } +# Returns true (0) if interface is administratively and physically up +is_interface_up() { + iface="$1" + if [ -f "/sys/class/net/$iface/operstate" ]; then + [ "$(cat "/sys/class/net/$iface/operstate")" = "up" ] + elif command -v ip >/dev/null 2>&1; then + ip link show "$iface" 2>/dev/null | grep -qw "state UP" + elif command -v ifconfig >/dev/null 2>&1; then + ifconfig "$iface" 2>/dev/null | grep -qw "UP" + else + return 1 + fi +} + +# Returns true (0) if physical link/carrier is detected (cable plugged in) +is_link_up() { + iface="$1" + [ -f "/sys/class/net/$iface/carrier" ] && [ "$(cat "/sys/class/net/$iface/carrier")" = "1" ] +} + +# Returns true (0) if interface is Ethernet type (type 1 in sysfs) +is_ethernet_interface() { + iface="$1" + [ -f "/sys/class/net/$iface/type" ] && [ "$(cat "/sys/class/net/$iface/type")" = "1" ] +} + +# Get all Ethernet interfaces (excluding common virtual types) +get_ethernet_interfaces() { + for path in /sys/class/net/*; do + iface=$(basename "$path") + case "$iface" in + lo|docker*|br-*|veth*|virbr*|tap*|tun*|wl*) continue ;; + esac + if is_ethernet_interface "$iface"; then + echo "$iface" + fi + done +} + +# Bring up interface with retries (down before up). +bringup_interface() { + iface="$1"; retries="${2:-3}"; sleep_sec="${3:-2}"; i=0 + while [ $i -lt "$retries" ]; do + if command -v ip >/dev/null 2>&1; then + ip link set "$iface" down + sleep 1 + ip link set "$iface" up + sleep "$sleep_sec" + ip link show "$iface" | grep -q "state UP" && return 0 + elif command -v ifconfig >/dev/null 2>&1; then + ifconfig "$iface" down + sleep 1 + ifconfig "$iface" up + sleep "$sleep_sec" + ifconfig "$iface" | grep -q "UP" && return 0 + fi + i=$((i + 1)) + done + return 1 +} + +# Wait for a valid IPv4 address on the given interface, up to a timeout (default 30s) +wait_for_ip_address() { + iface="$1" + timeout="${2:-30}" + elapsed=0 + while [ "$elapsed" -lt "$timeout" ]; do + ip_addr=$(get_ip_address "$iface") + if [ -n "$ip_addr" ]; then + if echo "$ip_addr" | grep -q '^169\.254'; then + echo "$ip_addr" + return 2 + fi + echo "$ip_addr" + return 0 + fi + sleep 1 + elapsed=$((elapsed + 1)) + done + return 1 +} + +# Get the IPv4 address for a given interface. +get_ip_address() { + iface="$1" + if command -v ip >/dev/null 2>&1; then + ip -4 -o addr show "$iface" | awk '{print $4}' | cut -d/ -f1 | head -n1 + elif command -v ifconfig >/dev/null 2>&1; then + ifconfig "$iface" 2>/dev/null | awk '/inet / {print $2}' | head -n1 + fi +} + +# Run a command with a timeout (in seconds) +run_with_timeout() { + timeout="$1"; shift + ( "$@" ) & + pid=$! + ( sleep "$timeout"; kill "$pid" 2>/dev/null ) & + watcher=$! + wait $pid 2>/dev/null + status=$? + kill $watcher 2>/dev/null + return $status +} + +# DHCP client logic (dhclient and udhcpc with timeouts) +run_dhcp_client() { + iface="$1" + timeout="${2:-10}" + ip_addr="" + log_info "Attempting DHCP on $iface (timeout ${timeout}s)..." + if command -v dhclient >/dev/null 2>&1; then + log_info "Trying dhclient for $iface" + run_with_timeout "$timeout" dhclient "$iface" + ip_addr=$(wait_for_ip_address "$iface" 5) + if [ -n "$ip_addr" ]; then + echo "$ip_addr" + return 0 + fi + fi + if command -v udhcpc >/dev/null 2>&1; then + log_info "Trying udhcpc for $iface" + run_with_timeout "$timeout" udhcpc -i "$iface" -T 3 -t 3 + ip_addr=$(wait_for_ip_address "$iface" 5) + if [ -n "$ip_addr" ]; then + echo "$ip_addr" + return 0 + fi + fi + log_warn "DHCP failed for $iface" + return 1 +} + +# Safely run DHCP client without disrupting existing config +try_dhcp_client_safe() { + iface="$1" + timeout="${2:-10}" + + current_ip=$(get_ip_address "$iface") + if [ -n "$current_ip" ] && ! echo "$current_ip" | grep -q '^169\.254'; then + log_info "$iface already has valid IP: $current_ip. Skipping DHCP." + return 0 + fi + + if ! command -v udhcpc >/dev/null 2>&1; then + log_warn "udhcpc not found, skipping DHCP attempt" + return 1 + fi + + # Use a no-op script to avoid flushing IPs + safe_dhcp_script="/tmp/dhcp-noop-$$.sh" + cat <<'EOF' > "$safe_dhcp_script" +#!/bin/sh +exit 0 +EOF + chmod +x "$safe_dhcp_script" + + log_info "Attempting DHCP on $iface safely..." + (udhcpc -i "$iface" -n -q -s "$safe_dhcp_script" >/dev/null 2>&1) & + dhcp_pid=$! + sleep "$timeout" + kill "$dhcp_pid" 2>/dev/null + rm -f "$safe_dhcp_script" + return 0 +}