From ce075b056ff8b2b2728bd401bb92cb3c39f2f26d Mon Sep 17 00:00:00 2001 From: Jeremy <51220084+jeremy-rifkin@users.noreply.github.com> Date: Wed, 19 Jun 2024 17:11:24 -0500 Subject: [PATCH] Make unit testing more robust --- test/CMakeLists.txt | 7 +- test/unit/object_trace.cpp | 95 ++++++++++++ test/{unittest.cpp => unit/raw_trace.cpp} | 63 ++++---- test/unit/stacktrace.cpp | 171 ++++++++++++++++++++++ 4 files changed, 303 insertions(+), 33 deletions(-) create mode 100644 test/unit/object_trace.cpp rename test/{unittest.cpp => unit/raw_trace.cpp} (64%) create mode 100644 test/unit/stacktrace.cpp diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 785cf68..e1cf272 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -52,7 +52,12 @@ if(NOT CPPTRACE_IS_GH_ACTIONS) FetchContent_MakeAvailable(googletest) endif() - add_executable(unittest unittest.cpp) + add_executable( + unittest + unit/raw_trace.cpp + unit/object_trace.cpp + unit/stacktrace.cpp + ) target_compile_features(unittest PRIVATE cxx_std_20) target_link_libraries(unittest PRIVATE ${target_name} GTest::gtest_main GTest::gmock_main) target_compile_options(unittest PRIVATE ${warning_options} -Wno-pedantic) diff --git a/test/unit/object_trace.cpp b/test/unit/object_trace.cpp new file mode 100644 index 0000000..65a907e --- /dev/null +++ b/test/unit/object_trace.cpp @@ -0,0 +1,95 @@ +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +using namespace std::literals; + + + +TEST(ObjectTrace, Empty) { + cpptrace::object_trace empty; + EXPECT_TRUE(empty.empty()); + EXPECT_EQ(empty.resolve().to_string(), "Stack trace (most recent call first):\n\n"); +} + + + +CPPTRACE_FORCE_NO_INLINE void object_basic() { + auto trace = cpptrace::generate_object_trace(); + EXPECT_FALSE(trace.empty()); + EXPECT_NE(trace.frames[0].raw_address, 0); + EXPECT_NE(trace.frames[0].object_address, 0); + EXPECT_THAT(trace.frames[0].object_path, testing::HasSubstr("unittest")); +} + +TEST(ObjectTrace, Basic) { + object_basic(); +} + + + +CPPTRACE_FORCE_NO_INLINE void object_basic_resolution() { + auto line = __LINE__ + 1; + auto trace = cpptrace::generate_object_trace().resolve(); + EXPECT_THAT(trace.frames[0].filename, testing::EndsWith("object_trace.cpp")); + EXPECT_EQ(trace.frames[0].line.value(), line); + EXPECT_THAT(trace.frames[0].symbol, testing::HasSubstr("object_basic_resolution")); +} + +TEST(ObjectTrace, BasicResolution) { + object_basic(); +} + + + +CPPTRACE_FORCE_NO_INLINE void object_resolve_3(std::vector& line_numbers) { + line_numbers.insert(line_numbers.begin(), __LINE__ + 1); + auto dummy = cpptrace::generate_trace(); + auto dummy_otrace = cpptrace::generate_object_trace(); + cpptrace::object_trace otrace; + otrace.frames.push_back(cpptrace::object_frame{0, dummy.frames[0].object_address, dummy_otrace.frames[0].object_path}); + otrace.frames.push_back(cpptrace::object_frame{0, dummy.frames[1].object_address, dummy_otrace.frames[1].object_path}); + otrace.frames.push_back(cpptrace::object_frame{0, dummy.frames[2].object_address, dummy_otrace.frames[2].object_path}); + otrace.frames.push_back(cpptrace::object_frame{0, dummy.frames[3].object_address, dummy_otrace.frames[3].object_path}); + auto trace = otrace.resolve(); + int i = 0; + EXPECT_THAT(trace.frames[i].filename, testing::EndsWith("object_trace.cpp")); + EXPECT_EQ(trace.frames[i].line.value(), line_numbers[i]); + EXPECT_THAT(trace.frames[i].symbol, testing::HasSubstr("object_resolve_3")); + i++; + EXPECT_THAT(trace.frames[i].filename, testing::EndsWith("object_trace.cpp")); + EXPECT_EQ(trace.frames[i].line.value(), line_numbers[i]); + EXPECT_THAT(trace.frames[i].symbol, testing::HasSubstr("object_resolve_2")); + i++; + EXPECT_THAT(trace.frames[i].filename, testing::EndsWith("object_trace.cpp")); + EXPECT_EQ(trace.frames[i].line.value(), line_numbers[i]); + EXPECT_THAT(trace.frames[i].symbol, testing::HasSubstr("object_resolve_1")); + i++; + EXPECT_THAT(trace.frames[i].filename, testing::EndsWith("object_trace.cpp")); + EXPECT_EQ(trace.frames[i].line.value(), line_numbers[i]); + EXPECT_THAT(trace.frames[i].symbol, testing::HasSubstr("ObjectTrace_Resolution_Test::TestBody")); +} + +CPPTRACE_FORCE_NO_INLINE void object_resolve_2(std::vector& line_numbers) { + line_numbers.insert(line_numbers.begin(), __LINE__ + 1); + object_resolve_3(line_numbers); +} + +CPPTRACE_FORCE_NO_INLINE void object_resolve_1(std::vector& line_numbers) { + line_numbers.insert(line_numbers.begin(), __LINE__ + 1); + object_resolve_2(line_numbers); +} + +TEST(ObjectTrace, Resolution) { + std::vector line_numbers; + line_numbers.insert(line_numbers.begin(), __LINE__ + 1); + object_resolve_1(line_numbers); +} diff --git a/test/unittest.cpp b/test/unit/raw_trace.cpp similarity index 64% rename from test/unittest.cpp rename to test/unit/raw_trace.cpp index 74ca748..0e83ceb 100644 --- a/test/unittest.cpp +++ b/test/unit/raw_trace.cpp @@ -14,7 +14,7 @@ using namespace std::literals; // Raw trace tests -// This is fickle, however, it's the only way to do it really. It's a reliable test in practice. +// This is fickle, however, it's the only way to do it really. It's reasonably reliable test in practice. CPPTRACE_FORCE_NO_INLINE void raw_trace_basic() { auto raw_trace = cpptrace::generate_raw_trace(); @@ -41,23 +41,34 @@ TEST(RawTrace, Basic) { #endif } -CPPTRACE_FORCE_NO_INLINE void raw_trace_multi_1(std::pair parent) { + + +CPPTRACE_FORCE_NO_INLINE void raw_trace_multi_2( + cpptrace::frame_ptr parent_low_bound, + cpptrace::frame_ptr parent_high_bound +) { auto raw_trace = cpptrace::generate_raw_trace(); - EXPECT_GE(raw_trace.frames[0], reinterpret_cast(raw_trace_multi_1)); - EXPECT_LE(raw_trace.frames[0], reinterpret_cast(raw_trace_multi_1) + 90); - EXPECT_GE(raw_trace.frames[1], parent.first); - EXPECT_LE(raw_trace.frames[1], parent.second); + EXPECT_GE(raw_trace.frames[0], reinterpret_cast(raw_trace_multi_2)); + EXPECT_LE(raw_trace.frames[0], reinterpret_cast(raw_trace_multi_2) + 90); + EXPECT_GE(raw_trace.frames[1], parent_low_bound); + EXPECT_LE(raw_trace.frames[1], parent_high_bound); } -CPPTRACE_FORCE_NO_INLINE void raw_trace_multi_top() { +CPPTRACE_FORCE_NO_INLINE void raw_trace_multi_1() { auto raw_trace = cpptrace::generate_raw_trace(); - raw_trace_multi_1({reinterpret_cast(raw_trace_multi_top), reinterpret_cast(raw_trace_multi_top) + 300}); - EXPECT_GE(raw_trace.frames[0], reinterpret_cast(raw_trace_multi_top)); - EXPECT_LE(raw_trace.frames[0], reinterpret_cast(raw_trace_multi_top) + 90); + raw_trace_multi_2(reinterpret_cast(raw_trace_multi_1), reinterpret_cast(raw_trace_multi_1) + 300); + EXPECT_GE(raw_trace.frames[0], reinterpret_cast(raw_trace_multi_1)); + EXPECT_LE(raw_trace.frames[0], reinterpret_cast(raw_trace_multi_1) + 90); +} + +std::vector> parents; + +CPPTRACE_FORCE_NO_INLINE void record_parent(cpptrace::frame_ptr low_bound, cpptrace::frame_ptr high_bound) { + parents.insert(parents.begin(), {reinterpret_cast(low_bound), reinterpret_cast(high_bound)}); } #ifndef _MSC_VER -CPPTRACE_FORCE_NO_INLINE void raw_trace_multi_precise_2(std::vector>& parents) { +CPPTRACE_FORCE_NO_INLINE void raw_trace_multi_precise_3() { a: auto raw_trace = cpptrace::generate_raw_trace(); b: @@ -69,7 +80,7 @@ CPPTRACE_FORCE_NO_INLINE void raw_trace_multi_precise_2(std::vector>& parents) { +CPPTRACE_FORCE_NO_INLINE void raw_trace_multi_precise_2() { a: auto raw_trace = cpptrace::generate_raw_trace(); b: @@ -79,41 +90,29 @@ CPPTRACE_FORCE_NO_INLINE void raw_trace_multi_precise_1(std::vector(&&c), reinterpret_cast(&&d)}); + record_parent(reinterpret_cast(&&c), reinterpret_cast(&&d)); c: - raw_trace_multi_precise_2(parents); + raw_trace_multi_precise_3(); d:; } -CPPTRACE_FORCE_NO_INLINE void raw_trace_multi_precise_top() { +CPPTRACE_FORCE_NO_INLINE void raw_trace_multi_precise_1() { a: auto raw_trace = cpptrace::generate_raw_trace(); b: EXPECT_GE(raw_trace.frames[0], reinterpret_cast(&&a)); EXPECT_LE(raw_trace.frames[0], reinterpret_cast(&&b)); - std::vector> parents; - parents.insert(parents.begin(), {reinterpret_cast(&&c), reinterpret_cast(&&d)}); + record_parent(reinterpret_cast(&&c), reinterpret_cast(&&d)); c: - raw_trace_multi_precise_1(parents); + raw_trace_multi_precise_2(); d:; } #endif TEST(RawTrace, MultipleCalls) { - raw_trace_multi_top(); + parents.clear(); + raw_trace_multi_1(); #ifndef _MSC_VER - raw_trace_multi_precise_top(); + raw_trace_multi_precise_1(); #endif } - -CPPTRACE_FORCE_NO_INLINE void stacktrace_basic() { - auto line = __LINE__ + 1; - auto trace = cpptrace::generate_trace(); - EXPECT_THAT(trace.frames[0].filename, testing::EndsWith("unittest.cpp")); - EXPECT_EQ(trace.frames[0].line.value(), line); - EXPECT_THAT(trace.frames[0].symbol, testing::HasSubstr("stacktrace_basic")); -} - -TEST(Stacktrace, Basic) { - stacktrace_basic(); -} diff --git a/test/unit/stacktrace.cpp b/test/unit/stacktrace.cpp new file mode 100644 index 0000000..7160877 --- /dev/null +++ b/test/unit/stacktrace.cpp @@ -0,0 +1,171 @@ +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +using namespace std::literals; + +#ifdef _MSC_VER + #define CPPTRACE_FORCE_INLINE [[msvc::flatten]] +#else + #define CPPTRACE_FORCE_INLINE [[gnu::always_inline]] +#endif + + + +TEST(Stacktrace, Empty) { + cpptrace::stacktrace empty; + EXPECT_TRUE(empty.empty()); + EXPECT_EQ(empty.to_string(), "Stack trace (most recent call first):\n\n"); +} + + + +CPPTRACE_FORCE_NO_INLINE void stacktrace_basic() { + auto line = __LINE__ + 1; + auto trace = cpptrace::generate_trace(); + EXPECT_THAT(trace.frames[0].filename, testing::EndsWith("stacktrace.cpp")); + EXPECT_EQ(trace.frames[0].line.value(), line); + EXPECT_THAT(trace.frames[0].symbol, testing::HasSubstr("stacktrace_basic")); +} + +TEST(Stacktrace, Basic) { + stacktrace_basic(); +} + + + +CPPTRACE_FORCE_NO_INLINE void stacktrace_multi_3(std::vector& line_numbers) { + line_numbers.insert(line_numbers.begin(), __LINE__ + 1); + auto trace = cpptrace::generate_trace(); + int i = 0; + EXPECT_THAT(trace.frames[i].filename, testing::EndsWith("stacktrace.cpp")); + EXPECT_EQ(trace.frames[i].line.value(), line_numbers[i]); + EXPECT_THAT(trace.frames[i].symbol, testing::HasSubstr("stacktrace_multi_3")); + i++; + EXPECT_THAT(trace.frames[i].filename, testing::EndsWith("stacktrace.cpp")); + EXPECT_EQ(trace.frames[i].line.value(), line_numbers[i]); + EXPECT_THAT(trace.frames[i].symbol, testing::HasSubstr("stacktrace_multi_2")); + i++; + EXPECT_THAT(trace.frames[i].filename, testing::EndsWith("stacktrace.cpp")); + EXPECT_EQ(trace.frames[i].line.value(), line_numbers[i]); + EXPECT_THAT(trace.frames[i].symbol, testing::HasSubstr("stacktrace_multi_1")); + i++; + EXPECT_THAT(trace.frames[i].filename, testing::EndsWith("stacktrace.cpp")); + EXPECT_EQ(trace.frames[i].line.value(), line_numbers[i]); + EXPECT_THAT(trace.frames[i].symbol, testing::HasSubstr("Stacktrace_MultipleFrames_Test::TestBody")); +} + +CPPTRACE_FORCE_NO_INLINE void stacktrace_multi_2(std::vector& line_numbers) { + line_numbers.insert(line_numbers.begin(), __LINE__ + 1); + stacktrace_multi_3(line_numbers); +} + +CPPTRACE_FORCE_NO_INLINE void stacktrace_multi_1(std::vector& line_numbers) { + line_numbers.insert(line_numbers.begin(), __LINE__ + 1); + stacktrace_multi_2(line_numbers); +} + +TEST(Stacktrace, MultipleFrames) { + std::vector line_numbers; + line_numbers.insert(line_numbers.begin(), __LINE__ + 1); + stacktrace_multi_1(line_numbers); +} + + + +CPPTRACE_FORCE_NO_INLINE cpptrace::raw_trace stacktrace_raw_resolve_3(std::vector& line_numbers) { + line_numbers.insert(line_numbers.begin(), __LINE__ + 1); + return cpptrace::generate_raw_trace(); +} + +CPPTRACE_FORCE_NO_INLINE cpptrace::raw_trace stacktrace_raw_resolve_2(std::vector& line_numbers) { + line_numbers.insert(line_numbers.begin(), __LINE__ + 1); + return stacktrace_raw_resolve_3(line_numbers); +} + +CPPTRACE_FORCE_NO_INLINE cpptrace::raw_trace stacktrace_raw_resolve_1(std::vector& line_numbers) { + line_numbers.insert(line_numbers.begin(), __LINE__ + 1); + return stacktrace_raw_resolve_2(line_numbers); +} + +TEST(Stacktrace, RawTraceResolution) { + std::vector line_numbers; + line_numbers.insert(line_numbers.begin(), __LINE__ + 1); + auto raw = stacktrace_raw_resolve_1(line_numbers); + auto trace = raw.resolve(); + int i = 0; + EXPECT_THAT(trace.frames[i].filename, testing::EndsWith("stacktrace.cpp")); + EXPECT_EQ(trace.frames[i].line.value(), line_numbers[i]); + EXPECT_THAT(trace.frames[i].symbol, testing::HasSubstr("stacktrace_raw_resolve_3")); + i++; + EXPECT_THAT(trace.frames[i].filename, testing::EndsWith("stacktrace.cpp")); + EXPECT_EQ(trace.frames[i].line.value(), line_numbers[i]); + EXPECT_THAT(trace.frames[i].symbol, testing::HasSubstr("stacktrace_raw_resolve_2")); + i++; + EXPECT_THAT(trace.frames[i].filename, testing::EndsWith("stacktrace.cpp")); + EXPECT_EQ(trace.frames[i].line.value(), line_numbers[i]); + EXPECT_THAT(trace.frames[i].symbol, testing::HasSubstr("stacktrace_raw_resolve_1")); + i++; + EXPECT_THAT(trace.frames[i].filename, testing::EndsWith("stacktrace.cpp")); + EXPECT_EQ(trace.frames[i].line.value(), line_numbers[i]); + EXPECT_THAT(trace.frames[i].symbol, testing::HasSubstr("Stacktrace_RawTraceResolution_Test::TestBody")); +} + + + +CPPTRACE_FORCE_NO_INLINE void stacktrace_inline_resolution_3(std::vector& line_numbers) { + line_numbers.insert(line_numbers.begin(), __LINE__ + 1); + auto trace = cpptrace::generate_trace(); + int i = 0; + EXPECT_THAT(trace.frames[i].filename, testing::EndsWith("stacktrace.cpp")); + EXPECT_EQ(trace.frames[i].line.value(), line_numbers[i]); + EXPECT_THAT(trace.frames[i].symbol, testing::HasSubstr("stacktrace_inline_resolution_3")); + EXPECT_FALSE(trace.frames[i].is_inline); + EXPECT_NE(trace.frames[i].raw_address, 0); + EXPECT_NE(trace.frames[i].object_address, 0); + i++; + EXPECT_THAT(trace.frames[i].filename, testing::EndsWith("stacktrace.cpp")); + EXPECT_EQ(trace.frames[i].line.value(), line_numbers[i]); + EXPECT_THAT(trace.frames[i].symbol, testing::HasSubstr("stacktrace_inline_resolution_2")); + EXPECT_TRUE(trace.frames[i].is_inline); + EXPECT_EQ(trace.frames[i].raw_address, 0); + EXPECT_EQ(trace.frames[i].object_address, 0); + i++; + EXPECT_THAT(trace.frames[i].filename, testing::EndsWith("stacktrace.cpp")); + EXPECT_EQ(trace.frames[i].line.value(), line_numbers[i]); + EXPECT_THAT(trace.frames[i].symbol, testing::HasSubstr("stacktrace_inline_resolution_1")); + EXPECT_FALSE(trace.frames[i].is_inline); + EXPECT_NE(trace.frames[i].raw_address, 0); + EXPECT_NE(trace.frames[i].object_address, 0); + i++; + EXPECT_THAT(trace.frames[i].filename, testing::EndsWith("stacktrace.cpp")); + EXPECT_EQ(trace.frames[i].line.value(), line_numbers[i]); + EXPECT_THAT(trace.frames[i].symbol, testing::HasSubstr("Stacktrace_InlineResolution_Test::TestBody")); + EXPECT_FALSE(trace.frames[i].is_inline); + EXPECT_NE(trace.frames[i].raw_address, 0); + EXPECT_NE(trace.frames[i].object_address, 0); +} + +CPPTRACE_FORCE_INLINE void stacktrace_inline_resolution_2(std::vector& line_numbers) { + line_numbers.insert(line_numbers.begin(), __LINE__ + 1); + stacktrace_inline_resolution_3(line_numbers); +} + +CPPTRACE_FORCE_NO_INLINE void stacktrace_inline_resolution_1(std::vector& line_numbers) { + line_numbers.insert(line_numbers.begin(), __LINE__ + 1); + stacktrace_inline_resolution_2(line_numbers); +} + +TEST(Stacktrace, InlineResolution) { + std::vector line_numbers; + line_numbers.insert(line_numbers.begin(), __LINE__ + 1); + stacktrace_inline_resolution_1(line_numbers); +}