diff --git a/addons/gut/error_tracker.gd b/addons/gut/error_tracker.gd index 99ff7317..b5a5dc39 100644 --- a/addons/gut/error_tracker.gd +++ b/addons/gut/error_tracker.gd @@ -75,6 +75,36 @@ func _is_error_failable(error : GutTrackedError): is_it = treat_engine_errors_as == GutUtils.TREAT_AS.FAILURE return is_it + +# Returns the line number in the test script where the error occurred. This +# is only usable for engine and push errors. +func _get_line_number_in_test(test_id, error): + if(error.is_gut_error()): + return -1 + + # This is bad. It has to know too much. gut.gd is making up the test_id. + # This script just expects it to be unique. Now this is desconstructing + # it. test_id construction should be moved into this class. + var parts = test_id.split(":") + if(parts.size() != 2): + return -1 + var test_name = test_id.split(":")[1] + + # There might be some assumptions being made here that could return + # misleading line numbers. Maybe. + var to_return = -1 + var i = 0 + while(i < error.backtrace.size() and to_return == -1): + var bt = error.backtrace[i] + var y = 0 + while(y < bt.get_frame_count() and to_return == -1): + if(bt.get_frame_function(y) == test_name): + to_return = bt.get_frame_line(y) + y += 1 + i += 1 + return to_return + + # ---------------- #endregion #region Godot's Logger Overrides @@ -88,6 +118,7 @@ func _log_error(function: String, file: String, line: int, code, rationale, editor_notify, error_type, script_backtraces) + # Godot's Logger virtual method for any output? # func _log_message(message: String, error: bool) -> void: # pass @@ -113,8 +144,8 @@ func get_current_test_errors(): return errors.items.get(_current_test_id, []) -# This should look through all the errors for a test and see if a failure -# should happen based off of flags. +# This looks through all the errors for a test and returns if it should fail +# based off of flags. func should_test_fail_from_errors(test_id = _current_test_id): var to_return = false if(errors.items.has(test_id)): @@ -135,15 +166,24 @@ func get_errors_for_test(test_id=_current_test_id): return to_return -# Returns emtpy string or text for errors that occurred during the test that -# should cause failure based on this class' flags. +# Returns empty string or text for errors that occurred during the test that +# should cause failure based on this class' flags. It might be better to put +# most of this logic int gut_tracked_error. func get_fail_text_for_errors(test_id=_current_test_id) -> String: var error_texts = [] if(errors.items.has(test_id)): for error in errors.items[test_id]: if(_is_error_failable(error)): - error_texts.append(str('<', error.get_error_type_name(), '>', error.code)) + var msg = str('<', error.get_error_type_name(), '>', error.rationale if error.rationale else error.code) + if error.file and error.file != GutUtils.NO_TEST and error.line >= 0: + if(error.is_engine_error()): + msg += str('\n at: ', error.function, ' (', error.file, ':', error.line, ')') + + var test_line_number = _get_line_number_in_test(test_id, error) + if(test_line_number != -1): + msg += str('\n at line: ', test_line_number) + error_texts.append(msg) var to_return = "" for i in error_texts.size(): diff --git a/addons/gut/gut.gd b/addons/gut/gut.gd index cd8f1980..7e4b11fa 100644 --- a/addons/gut/gut.gd +++ b/addons/gut/gut.gd @@ -918,8 +918,10 @@ func get_call_count_text(): func _fail(text=''): if(_current_test != null): var line_number = _extract_line_number(_current_test) - var line_text = ' at line ' + str(line_number) - p(line_text, LOG_LEVEL_FAIL_ONLY) + var line_text = '' + if(line_number != -1): + line_text = ' at line ' + str(line_number) + p(line_text, LOG_LEVEL_FAIL_ONLY) # format for summary line_text = "\n " + line_text var call_count_text = get_call_count_text() @@ -935,11 +937,11 @@ func _pending(text=''): # ------------------------------------------------------------------------------ -# Extracts the line number from curren stacktrace by matching the test case name +# Extracts the line number from current stacktrace by matching the test case name # ------------------------------------------------------------------------------ func _extract_line_number(current_test): var line_number = -1 - # if stack trace available than extraxt the test case line number + # if stack trace available than extract the test case line number var stackTrace = get_stack() if(stackTrace!=null): for index in stackTrace.size(): diff --git a/addons/gut/gut_tracked_error.gd b/addons/gut/gut_tracked_error.gd index 23725ee4..f6e932d8 100644 --- a/addons/gut/gut_tracked_error.gd +++ b/addons/gut/gut_tracked_error.gd @@ -5,7 +5,7 @@ class_name GutTrackedError ## for GUT errors. ## This will be an [code]Array[ScriptBacktrace][/code] for engine/push errors. -## This will the result of [code]get_stack[/code] for GUT errors. +## This will be the result of [code]get_stack[/code] for GUT errors. var backtrace = [] ## Usually the description var code = GutUtils.NO_TEST @@ -85,8 +85,10 @@ func get_error_type_name(): return to_return -# this might not work in other languages, and feels falkey, but might be -# useful at some point. +# This might not work in other languages? Do they change words like "Assertion +# failed" when translating Godot? It feels flakey even if they don't, but might +# be useful at some point, or at least I know I thought about this already and +# didn't do it. # func is_assert(): # return error_type == Logger.ERROR_TYPE_SCRIPT and \ # (code.find("Assertion failed.") == 0 or \ diff --git a/test/output_tests/test_with_errors.gd b/test/output_tests/test_with_errors.gd index 4c7753d1..f9f95a92 100644 --- a/test/output_tests/test_with_errors.gd +++ b/test/output_tests/test_with_errors.gd @@ -1,6 +1,63 @@ extends GutTest +func _generate_a_push_error(): + push_error("hello push_error") func test_with_push_error(): push_error('this is a push error') - assert_true(true, 'passing assert') \ No newline at end of file + pass_test('has push_error') + + +func test_forced_engine_error() -> void: + OS.delay_usec(-10) + pass_test('has forced engine error') + + +func test_gut_error(): + gut.get_logger().error("manual error") + pass_test('has a gut error') + + +func test_failing(): + assert_false(true) + + +func test_with_two_push_error(): + push_error('this is a push error') + _generate_a_push_error() + pass_test('has push_error') + + +func test_with_two_forced_engine_error() -> void: + OS.delay_usec(-10) + OS.delay_usec(-11) + pass_test('has forced engine error') + + +func test_with_two_gut_error(): + gut.get_logger().error("manual error") + gut.get_logger().error("error again") + pass_test('has a gut error') + + + +class TestInnerClassOutput: + extends GutTest + + func test_with_push_error(): + push_error('this is a push error') + pass_test('has push_error') + + + func test_forced_engine_error() -> void: + OS.delay_usec(-10) + pass_test('has forced engine error') + + + func test_gut_error(): + gut.get_logger().error("manual error") + pass_test('has a gut error') + + + func test_failing(): + assert_false(true)