#!/bin/bash
#
# SPDX-FileCopyrightText: 2012 Christian Babeux <christian.babeux@efficios.com>
# SPDX-FileCopyrightText: 2025 Michael Jeanson <mjeanson@efficios.com>
#
# SPDX-License-Identifier: GPL-2.0-only
#

TEST_DESC="Filtering - Invalid filters"

CURDIR=$(dirname "$0")
TESTDIR=$CURDIR/../../..

EVENT_NAME="bogus"

NUM_GLOBAL_TESTS=2
NUM_UST_TESTS=138
NUM_KERNEL_TESTS=138
NUM_TESTS=$(( NUM_UST_TESTS + NUM_KERNEL_TESTS + NUM_GLOBAL_TESTS ))

# shellcheck source-path=SCRIPTDIR/../../..
source "$TESTDIR/utils/utils.sh"

function test_ust_invalid_filter
{
	local test_invalid_filter="$1"

	local session_name="ust-invalid-filter"
	local trace_path

	diag "Test UST filter expression with invalid filter: $test_invalid_filter"

	trace_path=$(mktemp -d -t "tmp.${FUNCNAME[0]}_trace_path.XXXXXX")

	# Create session
	create_lttng_session_ok $session_name "$trace_path"

	# Apply filter
	enable_ust_lttng_event_fail $session_name $EVENT_NAME "" --filter "$test_invalid_filter"

	# Destroy session
	destroy_lttng_session_ok $session_name

	rm -rf "$trace_path"
}

function test_kernel_invalid_filter
{
	local test_invalid_filter="$1"

	local session_name="kernel-invalid-filter"
	local trace_path

	diag "Test kernel filter expression with invalid filter: $test_invalid_filter"

	trace_path=$(mktemp -d -t "tmp.${FUNCNAME[0]}_trace_path.XXXXXX")

	# Create session
	create_lttng_session_ok $session_name "$trace_path"

	# Apply filter
	enable_kernel_lttng_event_fail $session_name $EVENT_NAME "" --filter "$test_invalid_filter"

	# Destroy session
	destroy_lttng_session_ok $session_name

	rm -rf "$trace_path"
}

function test_ust_bytecode_size_limit
{
	local session_name="ust-bytecode-size-limit"
	local trace_path

	diag "Test UST filter bytecode size limits (64KiB)"

	trace_path=$(mktemp -d -t "tmp.${FUNCNAME[0]}_trace_path.XXXXXX")

	# Create session
	create_lttng_session_ok $session_name "$trace_path"

	# Apply filter
	enable_ust_lttng_event_fail $session_name $EVENT_NAME "" --filter "$BYTECODE_LIMIT"

	# Destroy session
	destroy_lttng_session_ok $session_name

	rm -rf "$trace_path"
}

function test_kernel_bytecode_size_limit
{
	local session_name="kernel-bytecode-size-limit"
	local trace_path

	diag "Test kernel filter bytecode size limits (64KiB)"

	trace_path=$(mktemp -d -t "tmp.${FUNCNAME[0]}_trace_path.XXXXXX")

	# Create session
	create_lttng_session_ok $session_name "$trace_path"

	# Apply filter
	enable_kernel_lttng_event_fail $session_name $EVENT_NAME "" --filter "$BYTECODE_LIMIT"

	# Destroy session
	destroy_lttng_session_ok $session_name

	rm -rf "$trace_path"
}


plan_tests $NUM_TESTS

print_test_banner "$TEST_DESC"

bail_out_if_no_babeltrace

# Current bytecode limitation is 65536 bytes long.
# Generate a huge bytecode
BYTECODE_LIMIT=$(echo -n "intfield"; for i in {0..5460}; do echo -n ' && 1'; done)

OLDIFS="$IFS"
IFS=$'\n'
INVALID_FILTERS=(
		# Unsupported ops
		"intfield*1"
		"intfield/1"
		"intfield+1"
		"intfield-1"
		"1+11111-3333+1"
		"(1+2)*(55*666)"
		"1+2*55*666"
		"asdf + 1 > 1"
		"asdfas < 2332 || asdf + 1 > 1"
		"!+-+++-------+++++++++++-----!!--!44+1"
		"aaa||(gg)+(333----1)"
		"1+1"
		# Unmatched parenthesis
		"((((((((((((((intfield)))))))))))))"
		'0 || ("abc" != "def")) && (3 < 4)'
		"a->"
		"a-->a"
		"a.b.c->d.e.f+1"
		# String can\'t be root node
		"\"somestring\""
		# Unary op on string not allowed
		"!\"somestring\""
		# Comparison with string type not allowed
		"\"somestring\" > 42"
		"\"somestring\" > 42.0"
		"42 > \"somestring\""
		"42.0 > \"somestring\""
		# Logical operator with string type not allowed
		"\"somestring\" || 1"
		"1 || \"somestring\""
		"\$ctx == 0"
		"0 == \$ctx"
		# Only \$ctx is supported for now
		"\$global.value == 0"
		"0 == \$global.value"
		# Cannot compare two full star globbing patterns
		'"hello*world" == "yes*man"'
		'"hello*world" == "yesman*"'
		'"helloworld*" == "yes*man"'
		# May only use != and == operators when one of them is a full
		# star globbing pattern
		'"hello*world" < field'
		'"hello*world" <= field'
		'"hello*world" >= field'
		'"hello*world" > field'
		'"hello*world" || field'
		'"hello*world" && field'
		'field < "hello*world"'
		'field <= "hello*world"'
		'field >= "hello*world"'
		'field > "hello*world"'
		'field && "hello*world"'
		'field || "hello*world"'
		# Array expression must contain constant index
		'field[abc] == 1'
)
IFS="$OLDIFS"

start_lttng_sessiond

diag "Test UST filters"

for filter in "${INVALID_FILTERS[@]}"
do
	test_ust_invalid_filter "$filter"
done

test_ust_bytecode_size_limit

check_skip_kernel_test "$NUM_KERNEL_TESTS" "Skipping kernel invalid filter tests." ||
{
	diag "Test kernel filters"

	for filter in "${INVALID_FILTERS[@]}"
	do
		test_kernel_invalid_filter "$filter"
	done

	test_kernel_bytecode_size_limit
}

stop_lttng_sessiond
