extends GdUnitTestSuite

var _current_iterations : Dictionary
var _expected_iterations: Dictionary

# a simple test fuzzer where provided a hard coded value set
class TestFuzzer extends Fuzzer:
	var _data := [0, 1, 2, 3, 4, 5, 6, 23, 8, 9]


	func next_value():
		return _data.pop_front()


func max_value() -> int:
	return 10


func min_value() -> int:
	return 1


func fuzzer() -> Fuzzer:
	return Fuzzers.rangei(min_value(), max_value())


func before():
	# define expected iteration count
	_expected_iterations = {
		"test_fuzzer_has_same_instance_peer_iteration" : 10,
		"test_multiple_fuzzers_inject_value_with_seed" : 10,
		"test_fuzzer_iterations_default" : Fuzzer.ITERATION_DEFAULT_COUNT,
		"test_fuzzer_iterations_custom_value" : 234,
		"test_fuzzer_inject_value" : 100,
		"test_multiline_fuzzer_args": 23,
	}
	# inital values
	_current_iterations = {
		"test_fuzzer_has_same_instance_peer_iteration" : 0,
		"test_multiple_fuzzers_inject_value_with_seed" : 0,
		"test_fuzzer_iterations_default" : 0,
		"test_fuzzer_iterations_custom_value" : 0,
		"test_fuzzer_inject_value" : 0,
		"test_multiline_fuzzer_args": 0,
	}


func after():
	for test_case in _expected_iterations.keys():
		var current = _current_iterations[test_case]
		var expected = _expected_iterations[test_case]

		assert_int(current).override_failure_message("Expecting %s itertions but is %s checked test case %s" % [expected, current, test_case]).is_equal(expected)

var _fuzzer_instance_before : Fuzzer = null


@warning_ignore("shadowed_variable", "unused_parameter")
func test_fuzzer_has_same_instance_peer_iteration(fuzzer=TestFuzzer.new(), fuzzer_iterations = 10):
	_current_iterations["test_fuzzer_has_same_instance_peer_iteration"] += 1
	assert_object(fuzzer).is_not_null()
	if _fuzzer_instance_before != null:
		assert_that(fuzzer).is_same(_fuzzer_instance_before)
	_fuzzer_instance_before = fuzzer


@warning_ignore("shadowed_variable", "unused_parameter")
func test_fuzzer_iterations_default(fuzzer := Fuzzers.rangei(-23, 22)):
	_current_iterations["test_fuzzer_iterations_default"] += 1
	assert_object(fuzzer).is_not_null()


@warning_ignore("shadowed_variable", "unused_parameter")
func test_fuzzer_iterations_custom_value(fuzzer := Fuzzers.rangei(-23, 22), fuzzer_iterations = 234, fuzzer_seed = 100):
	_current_iterations["test_fuzzer_iterations_custom_value"] += 1


@warning_ignore("shadowed_variable", "unused_parameter")
func test_fuzzer_inject_value(fuzzer := Fuzzers.rangei(-23, 22), fuzzer_iterations = 100):
	_current_iterations["test_fuzzer_inject_value"] += 1
	assert_object(fuzzer).is_not_null()
	assert_int(fuzzer.next_value()).is_between(-23, 22)


@warning_ignore("shadowed_variable", "unused_parameter")
func test_fuzzer_with_timeout(fuzzer := Fuzzers.rangei(-23, 22), fuzzer_iterations = 20, timeout = 100):
	discard_error_interupted_by_timeout()
	assert_int(fuzzer.next_value()).is_between(-23, 22)

	if fuzzer.iteration_index() == 10:
		await await_millis(100)
	# we not expect more than 10 iterations it should be interuptead by a timeout
	assert_int(fuzzer.iteration_index()).is_less_equal(10)

var expected_value := [22, 3, -14, -16, 21, 20, 4, -23, -19, -5]


@warning_ignore("shadowed_variable", "unused_parameter")
func test_fuzzer_inject_value_with_seed(fuzzer := Fuzzers.rangei(-23, 22), fuzzer_iterations = 10, fuzzer_seed = 187772):
	assert_object(fuzzer).is_not_null()
	var iteration_index =  fuzzer.iteration_index()-1
	var current = fuzzer.next_value()
	var expected = expected_value[iteration_index]
	assert_int(iteration_index).is_between(0, 9).is_less(10)
	assert_int(current)\
		.override_failure_message("Expect value %s checked test iteration %s\n but was %s" % [expected, iteration_index, current])\
		.is_equal(expected)

var expected_value_a := [22, -14, 21, 4, -19, -11, 5, 21, -6, -9]
var expected_value_b := [35, 38, 34, 39, 35, 41, 37, 35, 34, 39]


@warning_ignore("shadowed_variable", "unused_parameter")
func test_multiple_fuzzers_inject_value_with_seed(fuzzer_a := Fuzzers.rangei(-23, 22), fuzzer_b := Fuzzers.rangei(33, 44), fuzzer_iterations = 10, fuzzer_seed = 187772):
	_current_iterations["test_multiple_fuzzers_inject_value_with_seed"] += 1
	assert_object(fuzzer_a).is_not_null()
	assert_object(fuzzer_b).is_not_null()
	var iteration_index_a =  fuzzer_a.iteration_index()-1
	var current_a = fuzzer_a.next_value()
	var expected_a = expected_value_a[iteration_index_a]
	assert_int(iteration_index_a).is_between(0, 9).is_less(10)
	assert_int(current_a).is_between(-23, 22)
	assert_int(current_a)\
		.override_failure_message("Expect value %s checked test iteration %s\n but was %s" % [expected_a, iteration_index_a, current_a])\
		.is_equal(expected_a)
	var iteration_index_b =  fuzzer_b.iteration_index()-1
	var current_b = fuzzer_b.next_value()
	var expected_b = expected_value_b[iteration_index_b]
	assert_int(iteration_index_b).is_between(0, 9).is_less(10)
	assert_int(current_b).is_between(33, 44)
	assert_int(current_b)\
		.override_failure_message("Expect value %s checked test iteration %s\n but was %s" % [expected_b, iteration_index_b, current_b])\
		.is_equal(expected_b)


@warning_ignore("shadowed_variable", "unused_parameter")
func test_fuzzer_error_after_eight_iterations(fuzzer=TestFuzzer.new(), fuzzer_iterations = 10):
	assert_object(fuzzer).is_not_null()
	# should fail after 8 iterations
	if fuzzer.iteration_index() == 8:
		assert_failure(func(): assert_int(fuzzer.next_value()).is_between(0, 9)) \
			.is_failed() \
			.has_message("Expecting:\n '23'\n in range between\n '0' <> '9'")
	else:
		assert_int(fuzzer.next_value()).is_between(0, 9)


@warning_ignore("shadowed_variable", "unused_parameter")
func test_fuzzer_custom_func(fuzzer=fuzzer()):
	assert_object(fuzzer).is_not_null()
	assert_int(fuzzer.next_value()).is_between(1, 10)


@warning_ignore("shadowed_variable", "unused_parameter")
func test_multiline_fuzzer_args(
	fuzzer_a := Fuzzers.rangev2(Vector2(-47, -47), Vector2(47, 47)),
	fuzzer_b := Fuzzers.rangei(0, 9),
	fuzzer_iterations = 23):
		assert_object(fuzzer_a).is_not_null()
		assert_object(fuzzer_b).is_not_null()
		_current_iterations["test_multiline_fuzzer_args"] += 1
