| // Copyright 2019 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "util/trace_logging.h" |
| |
| #include <chrono> |
| #include <thread> |
| |
| #include "gmock/gmock.h" |
| #include "gtest/gtest.h" |
| #include "platform/test/trace_logging_helpers.h" |
| |
| namespace openscreen { |
| namespace { |
| |
| #if defined(ENABLE_TRACE_LOGGING) |
| constexpr TraceHierarchyParts kAllParts = static_cast<TraceHierarchyParts>( |
| TraceHierarchyParts::kRoot | TraceHierarchyParts::kParent | |
| TraceHierarchyParts::kCurrent); |
| constexpr TraceHierarchyParts kParentAndRoot = static_cast<TraceHierarchyParts>( |
| TraceHierarchyParts::kRoot | TraceHierarchyParts::kParent); |
| constexpr TraceId kEmptyId = TraceId{0}; |
| #endif |
| |
| using ::testing::_; |
| using ::testing::AtLeast; |
| using ::testing::DoAll; |
| using ::testing::Invoke; |
| |
| MATCHER_P(EqAsyncEndEvent, expected, "") { |
| return arg.ids.current == expected.ids.current && |
| arg.result == expected.result; |
| } |
| |
| MATCHER_P(HasSameArguments, expected, "") { |
| return arg.arguments == expected.arguments; |
| } |
| |
| // All the unit tests below should use TestTraceLoggingPlatform for the |
| // library's tracing output mock. This is a StrictMock to ensure that, when not |
| // compiling with ENABLE_TRACE_LOGGING, the mock receives no method calls. |
| using StrictMockLoggingPlatform = ::testing::StrictMock<MockLoggingPlatform>; |
| |
| TEST(TraceLoggingTest, MacroCallScopedDoesNotSegFault) { |
| StrictMockLoggingPlatform platform; |
| #if defined(ENABLE_TRACE_LOGGING) |
| EXPECT_CALL(platform, IsTraceLoggingEnabled(TraceCategory::kAny)) |
| .Times(AtLeast(1)); |
| EXPECT_CALL(platform, LogTrace(_, _)); |
| #endif |
| { TRACE_SCOPED(TraceCategory::kAny, "test"); } |
| } |
| |
| TEST(TraceLoggingTest, MacroCallDefaultScopedDoesNotSegFault) { |
| StrictMockLoggingPlatform platform; |
| #if defined(ENABLE_TRACE_LOGGING) |
| EXPECT_CALL(platform, IsTraceLoggingEnabled(TraceCategory::kAny)) |
| .Times(AtLeast(1)); |
| EXPECT_CALL(platform, LogTrace(_, _)); |
| #endif |
| { TRACE_DEFAULT_SCOPED(TraceCategory::kAny); } |
| } |
| |
| TEST(TraceLoggingTest, MacroCallUnscopedDoesNotSegFault) { |
| StrictMockLoggingPlatform platform; |
| #if defined(ENABLE_TRACE_LOGGING) |
| EXPECT_CALL(platform, IsTraceLoggingEnabled(TraceCategory::kAny)) |
| .Times(AtLeast(1)); |
| EXPECT_CALL(platform, LogAsyncStart(_)); |
| #endif |
| { TRACE_ASYNC_START(TraceCategory::kAny, "test"); } |
| } |
| |
| TEST(TraceLoggingTest, MacroVariablesUniquelyNames) { |
| StrictMockLoggingPlatform platform; |
| #if defined(ENABLE_TRACE_LOGGING) |
| EXPECT_CALL(platform, IsTraceLoggingEnabled(TraceCategory::kAny)) |
| .Times(AtLeast(1)); |
| EXPECT_CALL(platform, LogTrace(_, _)).Times(3); |
| EXPECT_CALL(platform, LogAsyncStart(_)).Times(2); |
| #endif |
| |
| { |
| TRACE_SCOPED(TraceCategory::kAny, "test1"); |
| TRACE_SCOPED(TraceCategory::kAny, "test2"); |
| TRACE_ASYNC_START(TraceCategory::kAny, "test3"); |
| TRACE_ASYNC_START(TraceCategory::kAny, "test4"); |
| TRACE_DEFAULT_SCOPED(TraceCategory::kAny); |
| } |
| } |
| |
| TEST(TraceLoggingTest, ExpectTimestampsReflectDelay) { |
| constexpr uint32_t delay_in_ms = 50; |
| StrictMockLoggingPlatform platform; |
| #if defined(ENABLE_TRACE_LOGGING) |
| EXPECT_CALL(platform, IsTraceLoggingEnabled(TraceCategory::kAny)) |
| .Times(AtLeast(1)); |
| EXPECT_CALL(platform, LogTrace(_, _)) |
| .WillOnce(DoAll(Invoke(ValidateTraceTimestampDiff<delay_in_ms>), |
| Invoke(ValidateTraceErrorCode<Error::Code::kNone>))); |
| #endif |
| |
| { |
| TRACE_SCOPED(TraceCategory::kAny, "Name"); |
| std::this_thread::sleep_for(std::chrono::milliseconds(delay_in_ms)); |
| } |
| } |
| |
| TEST(TraceLoggingTest, ExpectErrorsPassedToResult) { |
| constexpr Error::Code result_code = Error::Code::kParseError; |
| StrictMockLoggingPlatform platform; |
| #if defined(ENABLE_TRACE_LOGGING) |
| EXPECT_CALL(platform, IsTraceLoggingEnabled(TraceCategory::kAny)) |
| .Times(AtLeast(1)); |
| EXPECT_CALL(platform, LogTrace(_, _)) |
| .WillOnce(Invoke(ValidateTraceErrorCode<result_code>)); |
| #endif |
| |
| { |
| TRACE_SCOPED(TraceCategory::kAny, "Name"); |
| TRACE_SET_RESULT(result_code); |
| } |
| } |
| |
| TEST(TraceLoggingTest, ExpectUnsetTraceIdNotSet) { |
| StrictMockLoggingPlatform platform; |
| #if defined(ENABLE_TRACE_LOGGING) |
| EXPECT_CALL(platform, IsTraceLoggingEnabled(TraceCategory::kAny)) |
| .Times(AtLeast(1)); |
| EXPECT_CALL(platform, LogTrace(_, _)); |
| #endif |
| |
| TraceIdHierarchy h = {kUnsetTraceId, kUnsetTraceId, kUnsetTraceId}; |
| { |
| TRACE_SCOPED(TraceCategory::kAny, "Name", h); |
| |
| auto ids = TRACE_HIERARCHY; |
| EXPECT_NE(ids.current, kUnsetTraceId); |
| EXPECT_NE(ids.parent, kUnsetTraceId); |
| EXPECT_NE(ids.root, kUnsetTraceId); |
| } |
| } |
| |
| TEST(TraceLoggingTest, ExpectCreationWithIdsToWork) { |
| constexpr TraceId current = 0x32; |
| constexpr TraceId parent = 0x47; |
| constexpr TraceId root = 0x84; |
| StrictMockLoggingPlatform platform; |
| #if defined(ENABLE_TRACE_LOGGING) |
| EXPECT_CALL(platform, IsTraceLoggingEnabled(TraceCategory::kAny)) |
| .Times(AtLeast(1)); |
| EXPECT_CALL(platform, LogTrace(_, _)) |
| .WillOnce( |
| DoAll(Invoke(ValidateTraceErrorCode<Error::Code::kNone>), |
| Invoke(ValidateTraceIdHierarchyOnSyncTrace<current, parent, |
| root, kAllParts>))); |
| #endif |
| |
| { |
| TraceIdHierarchy h = {current, parent, root}; |
| TRACE_SCOPED(TraceCategory::kAny, "Name", h); |
| |
| #if defined(ENABLE_TRACE_LOGGING) |
| auto ids = TRACE_HIERARCHY; |
| EXPECT_EQ(ids.current, current); |
| EXPECT_EQ(ids.parent, parent); |
| EXPECT_EQ(ids.root, root); |
| |
| EXPECT_EQ(TRACE_CURRENT_ID, current); |
| EXPECT_EQ(TRACE_ROOT_ID, root); |
| #endif |
| } |
| } |
| |
| TEST(TraceLoggingTest, ExpectHierarchyToBeApplied) { |
| constexpr TraceId current = 0x32; |
| constexpr TraceId parent = 0x47; |
| constexpr TraceId root = 0x84; |
| StrictMockLoggingPlatform platform; |
| #if defined(ENABLE_TRACE_LOGGING) |
| EXPECT_CALL(platform, IsTraceLoggingEnabled(TraceCategory::kAny)) |
| .Times(AtLeast(1)); |
| EXPECT_CALL(platform, LogTrace(_, _)) |
| .WillOnce(DoAll( |
| Invoke(ValidateTraceErrorCode<Error::Code::kNone>), |
| Invoke(ValidateTraceIdHierarchyOnSyncTrace<kEmptyId, current, root, |
| kParentAndRoot>))) |
| .WillOnce( |
| DoAll(Invoke(ValidateTraceErrorCode<Error::Code::kNone>), |
| Invoke(ValidateTraceIdHierarchyOnSyncTrace<current, parent, |
| root, kAllParts>))); |
| #endif |
| |
| { |
| TraceIdHierarchy h = {current, parent, root}; |
| TRACE_SCOPED(TraceCategory::kAny, "Name", h); |
| #if defined(ENABLE_TRACE_LOGGING) |
| auto ids = TRACE_HIERARCHY; |
| EXPECT_EQ(ids.current, current); |
| EXPECT_EQ(ids.parent, parent); |
| EXPECT_EQ(ids.root, root); |
| #endif |
| |
| TRACE_SCOPED(TraceCategory::kAny, "Name"); |
| #if defined(ENABLE_TRACE_LOGGING) |
| ids = TRACE_HIERARCHY; |
| EXPECT_NE(ids.current, current); |
| EXPECT_EQ(ids.parent, current); |
| EXPECT_EQ(ids.root, root); |
| #endif |
| } |
| } |
| |
| TEST(TraceLoggingTest, ExpectHierarchyToEndAfterScopeWhenSetWithSetter) { |
| constexpr TraceId current = 0x32; |
| constexpr TraceId parent = 0x47; |
| constexpr TraceId root = 0x84; |
| StrictMockLoggingPlatform platform; |
| #if defined(ENABLE_TRACE_LOGGING) |
| EXPECT_CALL(platform, IsTraceLoggingEnabled(TraceCategory::kAny)) |
| .Times(AtLeast(1)); |
| EXPECT_CALL(platform, LogTrace(_, _)) |
| .WillOnce(DoAll( |
| Invoke(ValidateTraceErrorCode<Error::Code::kNone>), |
| Invoke(ValidateTraceIdHierarchyOnSyncTrace<kEmptyId, current, root, |
| kParentAndRoot>))); |
| #endif |
| |
| { |
| TraceIdHierarchy ids = {current, parent, root}; |
| TRACE_SET_HIERARCHY(ids); |
| { |
| TRACE_SCOPED(TraceCategory::kAny, "Name"); |
| ids = TRACE_HIERARCHY; |
| #if defined(ENABLE_TRACE_LOGGING) |
| EXPECT_NE(ids.current, current); |
| EXPECT_EQ(ids.parent, current); |
| EXPECT_EQ(ids.root, root); |
| #endif |
| } |
| |
| ids = TRACE_HIERARCHY; |
| #if defined(ENABLE_TRACE_LOGGING) |
| EXPECT_EQ(ids.current, current); |
| EXPECT_EQ(ids.parent, parent); |
| EXPECT_EQ(ids.root, root); |
| #endif |
| } |
| } |
| |
| TEST(TraceLoggingTest, ExpectHierarchyToEndAfterScope) { |
| constexpr TraceId current = 0x32; |
| constexpr TraceId parent = 0x47; |
| constexpr TraceId root = 0x84; |
| StrictMockLoggingPlatform platform; |
| #if defined(ENABLE_TRACE_LOGGING) |
| EXPECT_CALL(platform, IsTraceLoggingEnabled(TraceCategory::kAny)) |
| .Times(AtLeast(1)); |
| EXPECT_CALL(platform, LogTrace(_, _)) |
| .WillOnce(DoAll( |
| Invoke(ValidateTraceErrorCode<Error::Code::kNone>), |
| Invoke(ValidateTraceIdHierarchyOnSyncTrace<kEmptyId, current, root, |
| kParentAndRoot>))) |
| .WillOnce( |
| DoAll(Invoke(ValidateTraceErrorCode<Error::Code::kNone>), |
| Invoke(ValidateTraceIdHierarchyOnSyncTrace<current, parent, |
| root, kAllParts>))); |
| #endif |
| |
| { |
| TraceIdHierarchy ids = {current, parent, root}; |
| TRACE_SCOPED(TraceCategory::kAny, "Name", ids); |
| { |
| TRACE_SCOPED(TraceCategory::kAny, "Name"); |
| ids = TRACE_HIERARCHY; |
| #if defined(ENABLE_TRACE_LOGGING) |
| EXPECT_NE(ids.current, current); |
| EXPECT_EQ(ids.parent, current); |
| EXPECT_EQ(ids.root, root); |
| #endif |
| } |
| |
| ids = TRACE_HIERARCHY; |
| #if defined(ENABLE_TRACE_LOGGING) |
| EXPECT_EQ(ids.current, current); |
| EXPECT_EQ(ids.parent, parent); |
| EXPECT_EQ(ids.root, root); |
| #endif |
| } |
| } |
| |
| TEST(TraceLoggingTest, ExpectSetHierarchyToApply) { |
| constexpr TraceId current = 0x32; |
| constexpr TraceId parent = 0x47; |
| constexpr TraceId root = 0x84; |
| StrictMockLoggingPlatform platform; |
| #if defined(ENABLE_TRACE_LOGGING) |
| EXPECT_CALL(platform, IsTraceLoggingEnabled(TraceCategory::kAny)) |
| .Times(AtLeast(1)); |
| EXPECT_CALL(platform, LogTrace(_, _)) |
| .WillOnce(DoAll( |
| Invoke(ValidateTraceErrorCode<Error::Code::kNone>), |
| Invoke(ValidateTraceIdHierarchyOnSyncTrace<kEmptyId, current, root, |
| kParentAndRoot>))); |
| #endif |
| |
| { |
| TraceIdHierarchy ids = {current, parent, root}; |
| TRACE_SET_HIERARCHY(ids); |
| ids = TRACE_HIERARCHY; |
| #if defined(ENABLE_TRACE_LOGGING) |
| EXPECT_EQ(ids.current, current); |
| EXPECT_EQ(ids.parent, parent); |
| EXPECT_EQ(ids.root, root); |
| #endif |
| |
| TRACE_SCOPED(TraceCategory::kAny, "Name"); |
| ids = TRACE_HIERARCHY; |
| #if defined(ENABLE_TRACE_LOGGING) |
| EXPECT_NE(ids.current, current); |
| EXPECT_EQ(ids.parent, current); |
| EXPECT_EQ(ids.root, root); |
| #endif |
| } |
| } |
| |
| TEST(TraceLoggingTest, CheckTraceSupportsArguments) { |
| StrictMockLoggingPlatform platform; |
| #if defined(ENABLE_TRACE_LOGGING) |
| EXPECT_CALL(platform, IsTraceLoggingEnabled(TraceCategory::kAny)) |
| .Times(AtLeast(1)); |
| |
| TraceEvent expected; |
| expected.arguments.emplace_back("foo", "300"); |
| EXPECT_CALL(platform, LogTrace(HasSameArguments(expected), _)).Times(2); |
| expected.arguments.emplace_back("bar", "baz"); |
| EXPECT_CALL(platform, LogTrace(HasSameArguments(expected), _)).Times(2); |
| #endif |
| |
| { |
| TRACE_SCOPED1(TraceCategory::kAny, "Name", "foo", std::to_string(300)); |
| TRACE_DEFAULT_SCOPED1(TraceCategory::kAny, "foo", std::to_string(300)); |
| } |
| { |
| TRACE_SCOPED2(TraceCategory::kAny, "Name", "foo", std::to_string(300), |
| "bar", "baz"); |
| TRACE_DEFAULT_SCOPED2(TraceCategory::kAny, "foo", std::to_string(300), |
| "bar", "baz"); |
| } |
| } |
| |
| TEST(TraceLoggingTest, CheckTraceCropsArgumentLengths) { |
| StrictMockLoggingPlatform platform; |
| #if defined(ENABLE_TRACE_LOGGING) |
| EXPECT_CALL(platform, IsTraceLoggingEnabled(TraceCategory::kAny)) |
| .Times(AtLeast(1)); |
| |
| TraceEvent expected; |
| std::string really_long_argument(1337, 'F'); |
| expected.arguments.emplace_back("foo", really_long_argument); |
| |
| // First, TraceEvent allows us to crop the argument length directly. |
| expected.TruncateStrings(); |
| EXPECT_EQ(expected.arguments[0].second.size(), 1024u); |
| EXPECT_TRUE(std::all_of(expected.arguments[0].second.begin(), |
| expected.arguments[0].second.begin() + 1020, |
| [](const char c) { return c == 'F'; })) |
| << expected.arguments[0].second; |
| EXPECT_TRUE(std::all_of(expected.arguments[0].second.begin() + 1021, |
| expected.arguments[0].second.end(), |
| [](const char c) { return c == '.'; })) |
| << expected.arguments[0].second; |
| |
| EXPECT_CALL(platform, LogTrace(HasSameArguments(expected), _)).Times(2); |
| EXPECT_CALL(platform, LogAsyncStart(HasSameArguments(expected))); |
| |
| expected.arguments.emplace_back("bar", really_long_argument); |
| expected.TruncateStrings(); |
| EXPECT_CALL(platform, LogTrace(HasSameArguments(expected), _)).Times(2); |
| EXPECT_CALL(platform, LogAsyncStart(HasSameArguments(expected))); |
| |
| #endif |
| |
| { |
| TRACE_SCOPED1(TraceCategory::kAny, "Name", "foo", really_long_argument); |
| TRACE_DEFAULT_SCOPED1(TraceCategory::kAny, "foo", really_long_argument); |
| TRACE_ASYNC_START1(TraceCategory::kAny, "Name", "foo", |
| really_long_argument); |
| |
| TRACE_SCOPED2(TraceCategory::kAny, "Name", "foo", really_long_argument, |
| "bar", really_long_argument); |
| TRACE_DEFAULT_SCOPED2(TraceCategory::kAny, "foo", really_long_argument, |
| "bar", really_long_argument); |
| TRACE_ASYNC_START2(TraceCategory::kAny, "Name", "foo", really_long_argument, |
| "bar", really_long_argument); |
| } |
| } |
| |
| TEST(TraceLoggingTest, CheckTraceAsyncStartLogsCorrectly) { |
| StrictMockLoggingPlatform platform; |
| #if defined(ENABLE_TRACE_LOGGING) |
| EXPECT_CALL(platform, IsTraceLoggingEnabled(TraceCategory::kAny)) |
| .Times(AtLeast(1)); |
| EXPECT_CALL(platform, LogAsyncStart(_)); |
| #endif |
| |
| { TRACE_ASYNC_START(TraceCategory::kAny, "Name"); } |
| } |
| |
| TEST(TraceLoggingTest, CheckTraceAsyncSupportsArguments) { |
| StrictMockLoggingPlatform platform; |
| #if defined(ENABLE_TRACE_LOGGING) |
| EXPECT_CALL(platform, IsTraceLoggingEnabled(TraceCategory::kAny)) |
| .Times(AtLeast(1)); |
| |
| TraceEvent expected; |
| expected.arguments.emplace_back("foo", "300"); |
| EXPECT_CALL(platform, LogAsyncStart(HasSameArguments(expected))); |
| expected.arguments.emplace_back("bar", "baz"); |
| EXPECT_CALL(platform, LogAsyncStart(HasSameArguments(expected))); |
| #endif |
| |
| { |
| TRACE_ASYNC_START1(TraceCategory::kAny, "Name", "foo", std::to_string(300)); |
| } |
| { |
| TRACE_ASYNC_START2(TraceCategory::kAny, "Name", "foo", std::to_string(300), |
| "bar", "baz"); |
| } |
| } |
| |
| TEST(TraceLoggingTest, CheckTraceAsyncStartSetsHierarchy) { |
| constexpr TraceId current = 32; |
| constexpr TraceId parent = 47; |
| constexpr TraceId root = 84; |
| StrictMockLoggingPlatform platform; |
| #if defined(ENABLE_TRACE_LOGGING) |
| EXPECT_CALL(platform, IsTraceLoggingEnabled(TraceCategory::kAny)) |
| .Times(AtLeast(1)); |
| EXPECT_CALL(platform, LogAsyncStart(_)) |
| .WillOnce( |
| Invoke(ValidateTraceIdHierarchyOnAsyncTrace<kEmptyId, current, root, |
| kParentAndRoot>)); |
| #endif |
| |
| { |
| TraceIdHierarchy ids = {current, parent, root}; |
| TRACE_SET_HIERARCHY(ids); |
| { |
| TRACE_ASYNC_START(TraceCategory::kAny, "Name"); |
| ids = TRACE_HIERARCHY; |
| #if defined(ENABLE_TRACE_LOGGING) |
| EXPECT_NE(ids.current, current); |
| EXPECT_EQ(ids.parent, current); |
| EXPECT_EQ(ids.root, root); |
| #endif |
| } |
| |
| ids = TRACE_HIERARCHY; |
| #if defined(ENABLE_TRACE_LOGGING) |
| EXPECT_EQ(ids.current, current); |
| EXPECT_EQ(ids.parent, parent); |
| EXPECT_EQ(ids.root, root); |
| #endif |
| } |
| } |
| |
| TEST(TraceLoggingTest, CheckTraceAsyncEndLogsCorrectly) { |
| constexpr TraceId id = 12345; |
| constexpr Error::Code result = Error::Code::kAgain; |
| StrictMockLoggingPlatform platform; |
| #if defined(ENABLE_TRACE_LOGGING) |
| TraceEvent expected; |
| expected.ids.current = id; |
| expected.result = result; |
| |
| EXPECT_CALL(platform, IsTraceLoggingEnabled(TraceCategory::kAny)) |
| .Times(AtLeast(1)); |
| EXPECT_CALL(platform, LogAsyncEnd(EqAsyncEndEvent(expected))); |
| #endif |
| |
| TRACE_ASYNC_END(TraceCategory::kAny, id, result); |
| } |
| |
| } // namespace |
| } // namespace openscreen |