# Error Tracking Godot introduced the ability to detect errors in version 4.5. GUT records all the errors that occur during the execution of tests. At the end of each test GUT will check for any errors that were generated and not "handled". If any exist then the test will fail. Gut can detect the following * Internal GUT errors * Calls to `push_error` * Engine errors (Script/Shader/Godot errors) * Calls to `push_warning` (warnings do not cause failures) __Note about logging:__ All errors and warnings will always appear in the output. I don't know of a way to suppress them. If GUT's log level is 0 or 1 then GUT will print a message to the log for expected errors to make it more obvious when an error has been "handled". On log level 0, errors will appear before the test name (this can't be helped...or maybe it could but it would be ugly). ## "Handling" Expected Errors and Warnings Any error that is generated over the course of a test must be marked as "handled" to prevent GUT from failing the test. This can be done with any of the asserts below or manually via `get_errors()`. If you want to see all the errors that have been generated you can get detailed output by using print_tracked_errors. If you disable error tracking the asserts listed here will __always fail__. If you disable failing for specific error types, the asserts will still work but tests will not fail for any unhandled error type that is disabled. See below on how to disable things. Errors can only be asserted against once. If one error is generated and you assert that it has occurred multiple times or in different ways, it will not be found on subsequent asserts, causing those asserts to fail. Example: ```gdscript func test_fails_because_error_already_handled(): push_error("This is a push error") # This marks the error as handled. assert_push_error_count(1) # This will fail because GUT cannot find a matching unhandled error assert_push_error("is a push") ``` If you want to assert that no errors have been generated for a test, you can assert the count is zero. You could also assert on the size of the array returned by `get_errors`. Using `get_errors` means you wouldn't have to assert for each error/warning type. Asserting that no errors exist should only be used when you do not have another meaningful assert other than the fact that errors did not occur. ```gdscript func test_no_errors: # Ideally you would use just one of these. assert_engine_error_count(0, 'no engine errors') assert_push_error_count(0, 'no push errors') assert_push_warning_count(0, 'no push warnings') # Or you could assert no errors of any type happened, but that has # a funny smell to it...but you could do it. assert_eq(get_errors().size(), 0, 'no nuthin') ``` ## Engine Errors Engine errors are errors generated by Godot. These include Script errors, shader errors, engine warnings, and others. Your code should probably prevent these errors and generate an application specific `push_error` or some internal error mechanism. This might not always be possible, or you might not want to do that, so GUT has some asserts for these. Example engine error generation: ```gdscript func divide_recklessly(top, bottom): return top / bottom func assign_a_to_string_and_b_to_int(a, b): var string_a : String = a var int_b : int = b func test_demo_engine_type_error(): assign_a_to_string_and_b_to_int(Object.new(), 'asdf') divide_recklessly('some words', Node) ``` GUT error output ``` [Failed]: Unexpected Errors: [1] Trying to assign value of type 'Object' to a variable of type 'String'. [2] Invalid operands 'String' and 'Object' in operator '/'. at line -1 ``` You can use assert_engine_error_count to assert a specific number of engine errors have been genrated or you can use assert_engine_error to assert that an engine error with the passsed in text was generated. ```gdscript func test_demo_engine_type_error(): assign_a_to_string_and_b_to_int(Object.new(), 'asdf') divide_recklessly('some words', Node) assert_engine_error_count(2) func test_demo_engine_type_error_other_way(): assign_a_to_string_and_b_to_int(Object.new(), 'asdf') divide_recklessly('some words', Node) assert_engine_error("Invalid operands") assert_engine_error("to assign value") ``` ## push_error Errors A push_error error is any error generated from `push_error`. `push_error` errors are the closest thing we have to an application error. You can use `push_error` in your game and then assert that the error happened during at test. Example push error ```gdscript func test_demo_push_error(): push_error("This is a push error") ``` GUT error output ``` [Failed]: Unexpected Errors: [1] This is a push error at line -1 ``` You can use assert_push_error_count to assert a specific number of push errors have been genrated or you can use assert_push_error to assert that a push error with the passsed in text was generated. ```gdscript func test_demo_assert_push_error_count(): push_error("This is a push error") assert_push_error_count(1) func test_demo_assert_push_error(): push_error("This is a push error") assert_push_error("this is") ``` ## push_warning Warnings A push_warning warning is generated by calling `push_warning`. Warnings do not cause failures, but you can still assert that they were generated using assert_push_warning_count and assert_push_warning. The asserts work the same as the engine and push error asserts. ## Manually handle errors via get_errors You can also use get_errors to get all the errors and inspect them more closely and mark them as "handled". It is not recommended that mix how you "handle" errors in a single test. Setting the `handled` property of an error returned by `get_errors` and then asserting an error has occurred will give unexpected results. `get_errors` returns an array of GutTrackedError. To mark an error as handled, set the `handled` property to `true`. See get_errors and GutTrackedError for more information. ## Disabling Failing/Error Detection You can prevent GUT from failing when it encounters an error or prevent the detection all together. When failures are disabled, you can still assert that errors have occured. If you disable the error detection then error assertions will always fail. ### Via the Editor These options can be found in the GutPanel options. Track Errors disables the error tracking systm (same as `no_error_tracking` below). "Engine", "Push", and "GUT" enable/disable whether that error type causes a test to fail. ![Editor Error Options](_static/images/GutErrorOptions.png) ### Via .gutconfig.json File `failure_error_types` holds a list of the error types that will cause failures. An empty list means no error types will cause failures. Invalid values are ignored. Values are case sensitive. This is the default entry which contains all errors. This should only be specified in your file if you want to disable an error type. ```json failure_error_types = ["engine", "gut", "push_error"] ``` `no_error_tracking` can be used to disable the error detection system. GUT errors will still be detected (they are handled differently). ```json no_error_tracking = true, ``` ### Via the CLI There are also command line options for these, see the command line help for more information. ## Where is assert_gut_error_count and assert_gut_error? I didn't make those because your game shouldn't generate GUT errors. Your tests might, but it seems unlikely that you would test a test to ensure your test was doing something tests weren't meant to do. You could use `get_errors` if you really wanted...or just disable GUT errors causing errors. ## See Also This is my first pass at adding error detection/assertions to GUT. Please open an issue if you have additional ideas. * GutTrackedError