blob: 5ee842bda68b7076fff5073c3b74b7298fc8df75 [file] [edit]
// 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