/* * Copyright 2010-2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. * A copy of the License is located at * * http://aws.amazon.com/apache2.0 * * or in the "license" file accompanying this file. This file is distributed * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either * express or implied. See the License for the specific language governing * permissions and limitations under the License. */ #include #include #include static struct aws_error_info s_errors[] = { AWS_DEFINE_ERROR_INFO(1024, "test error 1", "test lib"), AWS_DEFINE_ERROR_INFO(1025, "test error 2", "test lib"), }; static struct aws_error_info_list s_errors_list = { .error_list = s_errors, .count = AWS_ARRAY_SIZE(s_errors), }; static int s_setup_errors_test_fn(struct aws_allocator *allocator, void *ctx) { (void)allocator; (void)ctx; aws_reset_error(); aws_set_global_error_handler_fn(NULL, NULL); aws_set_thread_local_error_handler_fn(NULL, NULL); aws_register_error_info(&s_errors_list); return AWS_OP_SUCCESS; } static int s_teardown_errors_test_fn(struct aws_allocator *allocator, int setup_res, void *ctx) { (void)allocator; (void)setup_res; (void)ctx; aws_reset_error(); aws_set_global_error_handler_fn(NULL, NULL); aws_set_thread_local_error_handler_fn(NULL, NULL); return AWS_OP_SUCCESS; } static int s_raise_errors_test_fn(struct aws_allocator *allocator, void *ctx) { (void)allocator; (void)ctx; int error = aws_last_error(); ASSERT_NULL(error, "error should be initialized to NULL"); ASSERT_INT_EQUALS(0, aws_last_error(), "error code should be initialized to 0"); struct aws_error_info test_error_1 = s_errors[0]; struct aws_error_info test_error_2 = s_errors[1]; ASSERT_INT_EQUALS(-1, aws_raise_error(test_error_1.error_code), "Raise error should return failure code."); error = aws_last_error(); ASSERT_INT_EQUALS( test_error_1.error_code, error, "Expected error code %d, but was %d", test_error_1.error_code, error); ASSERT_STR_EQUALS( test_error_1.error_str, aws_error_str(error), "Expected error string %s, but got %s", test_error_1.error_str, aws_error_str(error)); ASSERT_STR_EQUALS( test_error_1.lib_name, aws_error_lib_name(error), "Expected error libname %s, but got %s", test_error_1.lib_name, aws_error_lib_name(error)); ASSERT_INT_EQUALS(-1, aws_raise_error(test_error_2.error_code), "Raise error should return failure code."); error = aws_last_error(); ASSERT_INT_EQUALS( test_error_2.error_code, error, "Expected error code %d, but was %d", test_error_2.error_code, error); error = aws_last_error(); ASSERT_NOT_NULL(error, "last error should not have been null"); ASSERT_STR_EQUALS( test_error_2.error_str, aws_error_str(error), "Expected error string %s, but got %s", test_error_2.error_str, aws_error_str(error)); ASSERT_STR_EQUALS( test_error_2.lib_name, aws_error_lib_name(error), "Expected error libname %s, but got %s", test_error_2.lib_name, aws_error_lib_name(error)); aws_reset_error(); error = aws_last_error(); ASSERT_NULL(error, "error should be reset to NULL"); ASSERT_INT_EQUALS(0, aws_last_error(), "error code should be reset to 0"); return 0; } static int s_reset_errors_test_fn(struct aws_allocator *allocator, void *ctx) { (void)allocator; (void)ctx; struct aws_error_info test_error_1 = s_errors[0]; struct aws_error_info test_error_2 = s_errors[1]; aws_raise_error(test_error_2.error_code); aws_restore_error(test_error_1.error_code); int error = aws_last_error(); ASSERT_NOT_NULL(error, "last error should not have been null"); ASSERT_INT_EQUALS( test_error_1.error_code, error, "Expected error code %d, but was %d", test_error_1.error_code, error); ASSERT_STR_EQUALS( test_error_1.error_str, aws_error_str(error), "Expected error string %s, but got %s", test_error_1.error_str, aws_error_str(error)); ASSERT_STR_EQUALS( test_error_1.lib_name, aws_error_lib_name(error), "Expected error libname %s, but got %s", test_error_1.lib_name, aws_error_lib_name(error)); return 0; } struct error_test_cb_data { int global_cb_called; int tl_cb_called; int last_seen; }; static void s_error_test_global_cb(int err, void *ctx) { struct error_test_cb_data *cb_data = (struct error_test_cb_data *)ctx; cb_data->global_cb_called = 1; cb_data->last_seen = err; } static void s_error_test_thread_local_cb(int err, void *ctx) { struct error_test_cb_data *cb_data = (struct error_test_cb_data *)ctx; cb_data->tl_cb_called = 1; cb_data->last_seen = err; } static int s_error_callback_test_fn(struct aws_allocator *allocator, void *ctx) { (void)allocator; (void)ctx; struct error_test_cb_data cb_data = {.last_seen = 0, .global_cb_called = 0, .tl_cb_called = 0}; struct aws_error_info test_error_1 = s_errors[0]; struct aws_error_info test_error_2 = s_errors[1]; aws_error_handler_fn *old_fn = aws_set_global_error_handler_fn(s_error_test_global_cb, &cb_data); ASSERT_NULL(old_fn, "setting the global error callback the first time should return null"); aws_raise_error(test_error_1.error_code); ASSERT_NOT_NULL(cb_data.last_seen, "last error should not have been null"); ASSERT_TRUE(cb_data.global_cb_called, "Global Callback should have been invoked"); ASSERT_FALSE(cb_data.tl_cb_called, "Thread Local Callback should not have been invoked"); ASSERT_INT_EQUALS( test_error_1.error_code, cb_data.last_seen, "Expected error code %d, but was %d", test_error_1.error_code, cb_data.last_seen); ASSERT_STR_EQUALS( test_error_1.error_str, aws_error_str(cb_data.last_seen), "Expected error string %s, but got %s", test_error_1.error_str, aws_error_str(cb_data.last_seen)); ASSERT_STR_EQUALS( test_error_1.lib_name, aws_error_lib_name(cb_data.last_seen), "Expected error libname %s, but got %s", test_error_1.lib_name, aws_error_lib_name(cb_data.last_seen)); cb_data.last_seen = 0; cb_data.global_cb_called = 0; old_fn = aws_set_thread_local_error_handler_fn(s_error_test_thread_local_cb, &cb_data); ASSERT_NULL(old_fn, "setting the global error callback the first time should return null"); aws_raise_error(test_error_2.error_code); ASSERT_INT_EQUALS( test_error_2.error_code, aws_last_error(), "Expected error code %d, but was %d", test_error_2.error_code, aws_last_error()); ASSERT_NOT_NULL(cb_data.last_seen, "last error should not have been null"); ASSERT_FALSE(cb_data.global_cb_called, "Global Callback should not have been invoked"); ASSERT_TRUE(cb_data.tl_cb_called, "Thread local Callback should have been invoked"); ASSERT_INT_EQUALS( test_error_2.error_code, cb_data.last_seen, "Expected error code %d, but was %d", test_error_2.error_code, cb_data.last_seen); ASSERT_STR_EQUALS( test_error_2.error_str, aws_error_str(cb_data.last_seen), "Expected error string %s, but got %s", test_error_2.error_str, aws_error_str(cb_data.last_seen)); ASSERT_STR_EQUALS( test_error_2.lib_name, aws_error_lib_name(cb_data.last_seen), "Expected error libname %s, but got %s", test_error_2.lib_name, aws_error_lib_name(cb_data.last_seen)); old_fn = aws_set_thread_local_error_handler_fn(NULL, NULL); ASSERT_PTR_EQUALS( s_error_test_thread_local_cb, old_fn, "Setting a new thread local error callback should have returned the most recent value"); old_fn = aws_set_global_error_handler_fn(NULL, NULL); ASSERT_PTR_EQUALS( s_error_test_global_cb, old_fn, "Setting a new global error callback should have returned the most recent value"); return 0; } static int s_unknown_error_code_in_slot_test_fn(struct aws_allocator *allocator, void *ctx) { (void)allocator; (void)ctx; int error = aws_last_error(); ASSERT_NULL(error, "error should be initialized to NULL"); ASSERT_INT_EQUALS(0, aws_last_error(), "error code should be initialized to 0"); struct aws_error_info test_error_2 = s_errors[1]; aws_raise_error(test_error_2.error_code + 1); error = aws_last_error(); /* error code should still propogate */ ASSERT_INT_EQUALS( test_error_2.error_code + 1, error, "Expected error code %d, but was %d", test_error_2.error_code + 1, error); /* string should be invalid though */ ASSERT_STR_EQUALS( "Unknown Error Code", aws_error_str(error), "Expected error string %s, but got %s", "Unknown Error Code", aws_error_str(error)); ASSERT_STR_EQUALS( "Unknown Error Code", aws_error_lib_name(error), "Expected error string %s, but got %s", "Unknown Error Code", aws_error_lib_name(error)); ASSERT_STR_EQUALS( "Unknown Error Code", aws_error_debug_str(error), "Expected error string %s, but got %s", "Unknown Error Code", aws_error_debug_str(error)); return 0; } static int s_unknown_error_code_no_slot_test_fn(struct aws_allocator *allocator, void *ctx) { (void)allocator; (void)ctx; int error = aws_last_error(); ASSERT_NULL(error, "error should be initialized to NULL"); ASSERT_INT_EQUALS(0, aws_last_error(), "error code should be initialized to 0"); int non_slotted_error_code = 3000; aws_raise_error(non_slotted_error_code); error = aws_last_error(); /* error code should still propogate */ ASSERT_INT_EQUALS( non_slotted_error_code, error, "Expected error code %d, but was %d", non_slotted_error_code, error); /* string should be invalid though */ ASSERT_STR_EQUALS( "Unknown Error Code", aws_error_str(error), "Expected error string %s, but got %s", "Unknown Error Code", aws_error_str(error)); ASSERT_STR_EQUALS( "Unknown Error Code", aws_error_lib_name(error), "Expected error string %s, but got %s", "Unknown Error Code", aws_error_lib_name(error)); ASSERT_STR_EQUALS( "Unknown Error Code", aws_error_debug_str(error), "Expected error string %s, but got %s", "Unknown Error Code", aws_error_debug_str(error)); return 0; } static int s_unknown_error_code_range_too_large_test_fn(struct aws_allocator *allocator, void *ctx) { (void)allocator; (void)ctx; int error = aws_last_error(); ASSERT_NULL(error, "error should be initialized to NULL"); ASSERT_INT_EQUALS(0, aws_last_error(), "error code should be initialized to 0"); int oor_error_code = 10001; aws_raise_error(oor_error_code); error = aws_last_error(); /* error code should still propogate */ ASSERT_INT_EQUALS(oor_error_code, error, "Expected error code %d, but was %d", oor_error_code, error); /* string should be invalid though */ ASSERT_STR_EQUALS( "Unknown Error Code", aws_error_str(error), "Expected error string %s, but got %s", "Unknown Error Code", aws_error_str(error)); ASSERT_STR_EQUALS( "Unknown Error Code", aws_error_lib_name(error), "Expected error string %s, but got %s", "Unknown Error Code", aws_error_lib_name(error)); ASSERT_STR_EQUALS( "Unknown Error Code", aws_error_debug_str(error), "Expected error string %s, but got %s", "Unknown Error Code", aws_error_debug_str(error)); return 0; } struct error_thread_test_data { int thread_1_code; int thread_1_get_last_code; aws_thread_id_t thread_1_id; int thread_1_encountered_count; int thread_2_code; int thread_2_get_last_code; int thread_2_encountered_count; aws_thread_id_t thread_2_id; }; static void s_error_thread_test_thread_local_cb(int err, void *ctx) { struct error_thread_test_data *cb_data = (struct error_thread_test_data *)ctx; aws_thread_id_t thread_id = aws_thread_current_thread_id(); if (aws_thread_thread_id_equal(thread_id, cb_data->thread_1_id)) { cb_data->thread_1_code = err; cb_data->thread_1_get_last_code = aws_last_error(); cb_data->thread_1_encountered_count += 1; return; } cb_data->thread_2_code = err; cb_data->thread_2_get_last_code = aws_last_error(); cb_data->thread_2_id = aws_thread_current_thread_id(); cb_data->thread_2_encountered_count += 1; } static void s_error_thread_fn(void *arg) { aws_set_thread_local_error_handler_fn(s_error_thread_test_thread_local_cb, arg); aws_raise_error(15); } static int s_error_code_cross_thread_test_fn(struct aws_allocator *allocator, void *ctx) { (void)ctx; struct error_thread_test_data test_data = {.thread_1_code = 0, .thread_1_get_last_code = 0, .thread_1_encountered_count = 0, .thread_2_code = 0, .thread_2_get_last_code = 0, .thread_2_encountered_count = 0, .thread_2_id = 0}; test_data.thread_1_id = aws_thread_current_thread_id(); aws_set_thread_local_error_handler_fn(s_error_thread_test_thread_local_cb, &test_data); int thread_1_error_code_expected = 5; aws_raise_error(thread_1_error_code_expected); struct aws_thread thread; aws_thread_init(&thread, allocator); ASSERT_SUCCESS( aws_thread_launch(&thread, s_error_thread_fn, &test_data, NULL), "Thread creation failed with error %d", aws_last_error()); ASSERT_SUCCESS(aws_thread_join(&thread), "Thread join failed with error %d", aws_last_error()); aws_thread_clean_up(&thread); ASSERT_INT_EQUALS( 1, test_data.thread_1_encountered_count, "The thread local CB should only have triggered for the first thread once."); ASSERT_INT_EQUALS( 1, test_data.thread_2_encountered_count, "The thread local CB should only have triggered for the second thread once."); ASSERT_FALSE(test_data.thread_2_id == 0, "thread 2 id should have been set to something other than 0"); ASSERT_FALSE(test_data.thread_2_id == test_data.thread_1_id, "threads 1 and 2 should be different ids"); ASSERT_INT_EQUALS( thread_1_error_code_expected, aws_last_error(), "Thread 1's error should not have changed when thread 2 raised an error."); ASSERT_INT_EQUALS( thread_1_error_code_expected, test_data.thread_1_code, "Thread 1 code should have matched the original error."); ASSERT_INT_EQUALS( thread_1_error_code_expected, test_data.thread_1_get_last_code, "Thread 1 get last error code should have matched the original error."); ASSERT_INT_EQUALS(15, test_data.thread_2_code, "Thread 2 code should have matched the thread 2 error."); ASSERT_INT_EQUALS( 15, test_data.thread_2_get_last_code, "Thread 2 get last error code should have matched the thread 2 error."); return 0; } static int s_aws_load_error_strings_test(struct aws_allocator *allocator, void *ctx) { (void)ctx; /* Load aws-c-common's actual error info. * This will fail if the error info list is out of sync with the error enums. */ aws_common_library_init(allocator); return AWS_OP_SUCCESS; } static int s_aws_assume_compiles_test(struct aws_allocator *allocator, void *ctx) { (void)allocator; (void)ctx; AWS_ASSUME(true); if (false) { AWS_UNREACHABLE(); } return AWS_OP_SUCCESS; } AWS_TEST_CASE_FIXTURE( raise_errors_test, s_setup_errors_test_fn, s_raise_errors_test_fn, s_teardown_errors_test_fn, NULL) AWS_TEST_CASE_FIXTURE( error_callback_test, s_setup_errors_test_fn, s_error_callback_test_fn, s_teardown_errors_test_fn, NULL) AWS_TEST_CASE_FIXTURE( reset_errors_test, s_setup_errors_test_fn, s_reset_errors_test_fn, s_teardown_errors_test_fn, NULL) AWS_TEST_CASE_FIXTURE( unknown_error_code_in_slot_test, s_setup_errors_test_fn, s_unknown_error_code_in_slot_test_fn, s_teardown_errors_test_fn, NULL) AWS_TEST_CASE_FIXTURE( unknown_error_code_no_slot_test, s_setup_errors_test_fn, s_unknown_error_code_no_slot_test_fn, s_teardown_errors_test_fn, NULL) AWS_TEST_CASE_FIXTURE( unknown_error_code_range_too_large_test, s_setup_errors_test_fn, s_unknown_error_code_range_too_large_test_fn, s_teardown_errors_test_fn, NULL) AWS_TEST_CASE_FIXTURE( error_code_cross_thread_test, s_setup_errors_test_fn, s_error_code_cross_thread_test_fn, s_teardown_errors_test_fn, NULL) AWS_TEST_CASE(aws_load_error_strings_test, s_aws_load_error_strings_test) AWS_TEST_CASE(aws_assume_compiles_test, s_aws_assume_compiles_test)