1 // Copyright 2013 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "base/debug/crash_logging.h"
6
7 #include <map>
8 #include <memory>
9 #include <sstream>
10 #include <string_view>
11
12 #include "base/memory/raw_ref.h"
13 #include "testing/gmock/include/gmock/gmock.h"
14 #include "testing/gtest/include/gtest/gtest.h"
15
16 using ::testing::ElementsAre;
17 using ::testing::IsEmpty;
18 using ::testing::Pair;
19
20 namespace base {
21 namespace debug {
22
23 namespace {
24
25 class TestCrashKeyImplementation : public CrashKeyImplementation {
26 public:
TestCrashKeyImplementation(std::map<std::string,std::string> & data)27 explicit TestCrashKeyImplementation(std::map<std::string, std::string>& data)
28 : data_(data) {}
29
30 TestCrashKeyImplementation(const TestCrashKeyImplementation&) = delete;
31 TestCrashKeyImplementation& operator=(const TestCrashKeyImplementation&) =
32 delete;
33
Allocate(const char * name,CrashKeySize size)34 CrashKeyString* Allocate(const char* name, CrashKeySize size) override {
35 return new CrashKeyString(name, size);
36 }
37
Set(CrashKeyString * crash_key,std::string_view value)38 void Set(CrashKeyString* crash_key, std::string_view value) override {
39 ASSERT_TRUE(data_->emplace(crash_key->name, value).second);
40 }
41
Clear(CrashKeyString * crash_key)42 void Clear(CrashKeyString* crash_key) override {
43 ASSERT_EQ(1u, data_->erase(crash_key->name));
44 }
45
OutputCrashKeysToStream(std::ostream & out)46 void OutputCrashKeysToStream(std::ostream& out) override {
47 out << "Got " << data_->size() << " crash keys.";
48 }
49
50 private:
51 const raw_ref<std::map<std::string, std::string>> data_;
52 };
53
54 } // namespace
55
56 class CrashLoggingTest : public ::testing::Test {
57 public:
CrashLoggingTest()58 CrashLoggingTest() {
59 SetCrashKeyImplementation(
60 std::make_unique<TestCrashKeyImplementation>(data_));
61 }
62
~CrashLoggingTest()63 ~CrashLoggingTest() override { SetCrashKeyImplementation(nullptr); }
64
data() const65 const std::map<std::string, std::string>& data() const { return data_; }
66
67 private:
68 std::map<std::string, std::string> data_;
69 };
70
71 // Should not crash.
TEST(UninitializedCrashLoggingTest,Basic)72 TEST(UninitializedCrashLoggingTest, Basic) {
73 static auto* crash_key = AllocateCrashKeyString("test", CrashKeySize::Size32);
74 EXPECT_FALSE(crash_key);
75
76 SetCrashKeyString(crash_key, "value");
77
78 ClearCrashKeyString(crash_key);
79 }
80
TEST_F(CrashLoggingTest,Basic)81 TEST_F(CrashLoggingTest, Basic) {
82 static auto* crash_key = AllocateCrashKeyString("test", CrashKeySize::Size32);
83 EXPECT_TRUE(crash_key);
84 EXPECT_THAT(data(), IsEmpty());
85
86 SetCrashKeyString(crash_key, "value");
87 EXPECT_THAT(data(), ElementsAre(Pair("test", "value")));
88 std::ostringstream stream;
89 OutputCrashKeysToStream(stream);
90 EXPECT_EQ("Got 1 crash keys.", stream.str());
91
92 ClearCrashKeyString(crash_key);
93 EXPECT_THAT(data(), IsEmpty());
94 std::ostringstream stream2;
95 OutputCrashKeysToStream(stream2);
96 EXPECT_EQ("Got 0 crash keys.", stream2.str());
97 }
98
99 // Verify that the macros are properly setting crash keys.
TEST_F(CrashLoggingTest,Macros)100 TEST_F(CrashLoggingTest, Macros) {
101 {
102 SCOPED_CRASH_KEY_BOOL("category", "bool-value", false);
103 EXPECT_THAT(data(), ElementsAre(Pair("category-bool-value", "false")));
104 }
105
106 {
107 SCOPED_CRASH_KEY_BOOL("category", "bool-value", true);
108 EXPECT_THAT(data(), ElementsAre(Pair("category-bool-value", "true")));
109 }
110
111 {
112 SCOPED_CRASH_KEY_NUMBER("category", "float-value", 0.5);
113 EXPECT_THAT(data(), ElementsAre(Pair("category-float-value", "0.5")));
114 }
115
116 {
117 SCOPED_CRASH_KEY_NUMBER("category", "int-value", 1);
118 EXPECT_THAT(data(), ElementsAre(Pair("category-int-value", "1")));
119 }
120
121 {
122 SCOPED_CRASH_KEY_STRING32("category", "string32-value", "餅");
123 EXPECT_THAT(data(), ElementsAre(Pair("category-string32-value", "餅")));
124 }
125
126 {
127 SCOPED_CRASH_KEY_STRING64("category", "string64-value", "餅");
128 EXPECT_THAT(data(), ElementsAre(Pair("category-string64-value", "餅")));
129 }
130
131 {
132 SCOPED_CRASH_KEY_STRING256("category", "string256-value", "餅");
133 EXPECT_THAT(data(), ElementsAre(Pair("category-string256-value", "餅")));
134 }
135
136 {
137 SCOPED_CRASH_KEY_STRING1024("category", "string1024-value", "餅");
138 EXPECT_THAT(data(), ElementsAre(Pair("category-string1024-value", "餅")));
139 }
140 }
141
142 // Test that the helper macros properly uniqify the internal variable used for
143 // the scoper.
TEST_F(CrashLoggingTest,MultipleCrashKeysInSameScope)144 TEST_F(CrashLoggingTest, MultipleCrashKeysInSameScope) {
145 SCOPED_CRASH_KEY_BOOL("category", "bool-value", false);
146 SCOPED_CRASH_KEY_NUMBER("category", "int-value", 1);
147
148 EXPECT_THAT(data(), ElementsAre(Pair("category-bool-value", "false"),
149 Pair("category-int-value", "1")));
150
151 std::ostringstream stream;
152 OutputCrashKeysToStream(stream);
153 EXPECT_EQ("Got 2 crash keys.", stream.str());
154 }
155
156 } // namespace debug
157 } // namespace base
158