#!/bin/bash
# SPDX-License-Identifier: GPL-2.0
# Test YNL ethtool functionality

# Load KTAP test helpers
KSELFTEST_KTAP_HELPERS="$(dirname "$(realpath "$0")")/../../../testing/selftests/kselftest/ktap_helpers.sh"
# shellcheck source=../../../testing/selftests/kselftest/ktap_helpers.sh
source "$KSELFTEST_KTAP_HELPERS"

# Default ynl-ethtool path for direct execution, can be overridden by make install
ynl_ethtool="./ethtool.py"

readonly NSIM_ID="1337"
readonly NSIM_DEV_NAME="nsim${NSIM_ID}"
readonly VETH_A="veth_a"
readonly VETH_B="veth_b"

testns="ynl-ethtool-$(mktemp -u XXXXXX)"
TESTS_NO=0

# Uses veth device as netdevsim doesn't support basic ethtool device info
ethtool_device_info()
{
	local info_output

	info_output=$(ip netns exec "$testns" $ynl_ethtool "$VETH_A" 2>/dev/null)

	if ! echo "$info_output" | grep -q "Settings for"; then
		ktap_test_fail "YNL ethtool device info (device info output missing expected content)"
		return
	fi

	ktap_test_pass "YNL ethtool device info"
}
TESTS_NO=$((TESTS_NO + 1))

ethtool_statistics()
{
	local stats_output

	stats_output=$(ip netns exec "$testns" $ynl_ethtool --statistics "$NSIM_DEV_NAME" 2>/dev/null)

	if ! echo "$stats_output" | grep -q -E "(NIC statistics|packets|bytes)"; then
		ktap_test_fail "YNL ethtool statistics (statistics output missing expected content)"
		return
	fi

	ktap_test_pass "YNL ethtool statistics"
}
TESTS_NO=$((TESTS_NO + 1))

ethtool_ring_params()
{
	local ring_output

	ring_output=$(ip netns exec "$testns" $ynl_ethtool --show-ring "$NSIM_DEV_NAME" 2>/dev/null)

	if ! echo "$ring_output" | grep -q -E "(Ring parameters|RX|TX)"; then
		ktap_test_fail "YNL ethtool ring parameters (ring parameters output missing expected content)"
		return
	fi

	if ! ip netns exec "$testns" $ynl_ethtool --set-ring "$NSIM_DEV_NAME" rx 64 2>/dev/null; then
		ktap_test_fail "YNL ethtool ring parameters (set-ring command failed unexpectedly)"
		return
	fi

	ktap_test_pass "YNL ethtool ring parameters (show/set)"
}
TESTS_NO=$((TESTS_NO + 1))

ethtool_coalesce_params()
{
	if ! ip netns exec "$testns" $ynl_ethtool --show-coalesce "$NSIM_DEV_NAME" &>/dev/null; then
		ktap_test_fail "YNL ethtool coalesce parameters (failed to get coalesce parameters)"
		return
	fi

	if ! ip netns exec "$testns" $ynl_ethtool --set-coalesce "$NSIM_DEV_NAME" rx-usecs 50 2>/dev/null; then
		ktap_test_fail "YNL ethtool coalesce parameters (set-coalesce command failed unexpectedly)"
		return
	fi

	ktap_test_pass "YNL ethtool coalesce parameters (show/set)"
}
TESTS_NO=$((TESTS_NO + 1))

ethtool_pause_params()
{
	if ! ip netns exec "$testns" $ynl_ethtool --show-pause "$NSIM_DEV_NAME" &>/dev/null; then
		ktap_test_fail "YNL ethtool pause parameters (failed to get pause parameters)"
		return
	fi

	if ! ip netns exec "$testns" $ynl_ethtool --set-pause "$NSIM_DEV_NAME" tx 1 rx 1 2>/dev/null; then
		ktap_test_fail "YNL ethtool pause parameters (set-pause command failed unexpectedly)"
		return
	fi

	ktap_test_pass "YNL ethtool pause parameters (show/set)"
}
TESTS_NO=$((TESTS_NO + 1))

ethtool_features_info()
{
	local features_output

	features_output=$(ip netns exec "$testns" $ynl_ethtool --show-features "$NSIM_DEV_NAME" 2>/dev/null)

	if ! echo "$features_output" | grep -q -E "(Features|offload)"; then
		ktap_test_fail "YNL ethtool features info (features output missing expected content)"
		return
	fi

	ktap_test_pass "YNL ethtool features info (show/set)"
}
TESTS_NO=$((TESTS_NO + 1))

ethtool_channels_info()
{
	local channels_output

	channels_output=$(ip netns exec "$testns" $ynl_ethtool --show-channels "$NSIM_DEV_NAME" 2>/dev/null)

	if ! echo "$channels_output" | grep -q -E "(Channel|Combined|RX|TX)"; then
		ktap_test_fail "YNL ethtool channels info (channels output missing expected content)"
		return
	fi

	if ! ip netns exec "$testns" $ynl_ethtool --set-channels "$NSIM_DEV_NAME" combined-count 1 2>/dev/null; then
		ktap_test_fail "YNL ethtool channels info (set-channels command failed unexpectedly)"
		return
	fi

	ktap_test_pass "YNL ethtool channels info (show/set)"
}
TESTS_NO=$((TESTS_NO + 1))

ethtool_time_stamping()
{
	local ts_output

	ts_output=$(ip netns exec "$testns" $ynl_ethtool --show-time-stamping "$NSIM_DEV_NAME" 2>/dev/null)

	if ! echo "$ts_output" | grep -q -E "(Time stamping|timestamping|SOF_TIMESTAMPING)"; then
		ktap_test_fail "YNL ethtool time stamping (time stamping output missing expected content)"
		return
	fi

	ktap_test_pass "YNL ethtool time stamping"
}
TESTS_NO=$((TESTS_NO + 1))

setup()
{
	modprobe netdevsim &> /dev/null
	if ! [ -f /sys/bus/netdevsim/new_device ]; then
		ktap_skip_all "netdevsim module not available"
		exit "$KSFT_SKIP"
	fi

	if ! ip netns add "$testns" 2>/dev/null; then
		ktap_skip_all "failed to create test namespace"
		exit "$KSFT_SKIP"
	fi

	echo "$NSIM_ID 1" | ip netns exec "$testns" tee /sys/bus/netdevsim/new_device >/dev/null 2>&1 || {
		ktap_skip_all "failed to create netdevsim device"
		exit "$KSFT_SKIP"
	}

	local dev
	dev=$(ip netns exec "$testns" ls /sys/bus/netdevsim/devices/netdevsim$NSIM_ID/net 2>/dev/null | head -1)
	if [[ -z "$dev" ]]; then
		ktap_skip_all "failed to find netdevsim device"
		exit "$KSFT_SKIP"
	fi

	ip -netns "$testns" link set dev "$dev" name "$NSIM_DEV_NAME" 2>/dev/null || {
		ktap_skip_all "failed to rename netdevsim device"
		exit "$KSFT_SKIP"
	}

	ip -netns "$testns" link set dev "$NSIM_DEV_NAME" up 2>/dev/null

	if ! ip -n "$testns" link add "$VETH_A" type veth peer name "$VETH_B" 2>/dev/null; then
		ktap_skip_all "failed to create veth pair"
		exit "$KSFT_SKIP"
	fi

	ip -n "$testns" link set "$VETH_A" up 2>/dev/null
	ip -n "$testns" link set "$VETH_B" up 2>/dev/null
}

cleanup()
{
	ip netns exec "$testns" bash -c "echo $NSIM_ID > /sys/bus/netdevsim/del_device" 2>/dev/null || true
	ip netns del "$testns" 2>/dev/null || true
}

# Check if ynl-ethtool command is available
if ! command -v $ynl_ethtool &>/dev/null && [[ ! -x $ynl_ethtool ]]; then
	ktap_skip_all "ynl-ethtool command not found: $ynl_ethtool"
	exit "$KSFT_SKIP"
fi

trap cleanup EXIT

ktap_print_header
setup
ktap_set_plan "${TESTS_NO}"

ethtool_device_info
ethtool_statistics
ethtool_ring_params
ethtool_coalesce_params
ethtool_pause_params
ethtool_features_info
ethtool_channels_info
ethtool_time_stamping

ktap_finished
