1// Copyright (c) 2013 The Chromium Authors. All rights reserved.
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 <string>
9
10#include "base/bind.h"
11#include "testing/gtest/include/gtest/gtest.h"
12
13namespace {
14
15std::map<std::string, std::string>* key_values_ = NULL;
16
17}  // namespace
18
19class CrashLoggingTest : public testing::Test {
20 public:
21  virtual void SetUp() {
22    key_values_ = new std::map<std::string, std::string>;
23    base::debug::SetCrashKeyReportingFunctions(
24        &CrashLoggingTest::SetKeyValue,
25        &CrashLoggingTest::ClearKeyValue);
26  }
27
28  virtual void TearDown() {
29    base::debug::ResetCrashLoggingForTesting();
30
31    delete key_values_;
32    key_values_ = NULL;
33  }
34
35 private:
36  static void SetKeyValue(const base::StringPiece& key,
37                          const base::StringPiece& value) {
38    (*key_values_)[key.as_string()] = value.as_string();
39  }
40
41  static void ClearKeyValue(const base::StringPiece& key) {
42    key_values_->erase(key.as_string());
43  }
44};
45
46TEST_F(CrashLoggingTest, SetClearSingle) {
47  const char* kTestKey = "test-key";
48  base::debug::CrashKey keys[] = { { kTestKey, 255 } };
49  base::debug::InitCrashKeys(keys, arraysize(keys), 255);
50
51  base::debug::SetCrashKeyValue(kTestKey, "value");
52  EXPECT_EQ("value", (*key_values_)[kTestKey]);
53
54  base::debug::ClearCrashKey(kTestKey);
55  EXPECT_TRUE(key_values_->end() == key_values_->find(kTestKey));
56}
57
58TEST_F(CrashLoggingTest, SetChunked) {
59  const char* kTestKey = "chunky";
60  const char* kChunk1 = "chunky-1";
61  const char* kChunk2 = "chunky-2";
62  const char* kChunk3 = "chunky-3";
63  base::debug::CrashKey keys[] = { { kTestKey, 15 } };
64  base::debug::InitCrashKeys(keys, arraysize(keys), 5);
65
66  std::map<std::string, std::string>& values = *key_values_;
67
68  // Fill only the first chunk.
69  base::debug::SetCrashKeyValue(kTestKey, "foo");
70  EXPECT_EQ(1u, values.size());
71  EXPECT_EQ("foo", values[kChunk1]);
72  EXPECT_TRUE(values.end() == values.find(kChunk2));
73  EXPECT_TRUE(values.end() == values.find(kChunk3));
74
75  // Fill three chunks with truncation (max length is 15, this string is 20).
76  base::debug::SetCrashKeyValue(kTestKey, "five four three two");
77  EXPECT_EQ(3u, values.size());
78  EXPECT_EQ("five ", values[kChunk1]);
79  EXPECT_EQ("four ", values[kChunk2]);
80  EXPECT_EQ("three", values[kChunk3]);
81
82  // Clear everything.
83  base::debug::ClearCrashKey(kTestKey);
84  EXPECT_EQ(0u, values.size());
85  EXPECT_TRUE(values.end() == values.find(kChunk1));
86  EXPECT_TRUE(values.end() == values.find(kChunk2));
87  EXPECT_TRUE(values.end() == values.find(kChunk3));
88
89  // Refill all three chunks with truncation, then test that setting a smaller
90  // value clears the third chunk.
91  base::debug::SetCrashKeyValue(kTestKey, "five four three two");
92  base::debug::SetCrashKeyValue(kTestKey, "allays");
93  EXPECT_EQ(2u, values.size());
94  EXPECT_EQ("allay", values[kChunk1]);
95  EXPECT_EQ("s", values[kChunk2]);
96  EXPECT_TRUE(values.end() == values.find(kChunk3));
97
98  // Clear everything.
99  base::debug::ClearCrashKey(kTestKey);
100  EXPECT_EQ(0u, values.size());
101  EXPECT_TRUE(values.end() == values.find(kChunk1));
102  EXPECT_TRUE(values.end() == values.find(kChunk2));
103  EXPECT_TRUE(values.end() == values.find(kChunk3));
104}
105
106TEST_F(CrashLoggingTest, ScopedCrashKey) {
107  const char* kTestKey = "test-key";
108  base::debug::CrashKey keys[] = { { kTestKey, 255 } };
109  base::debug::InitCrashKeys(keys, arraysize(keys), 255);
110
111  EXPECT_EQ(0u, key_values_->size());
112  EXPECT_TRUE(key_values_->end() == key_values_->find(kTestKey));
113  {
114    base::debug::ScopedCrashKey scoped_crash_key(kTestKey, "value");
115    EXPECT_EQ("value", (*key_values_)[kTestKey]);
116    EXPECT_EQ(1u, key_values_->size());
117  }
118  EXPECT_EQ(0u, key_values_->size());
119  EXPECT_TRUE(key_values_->end() == key_values_->find(kTestKey));
120}
121
122TEST_F(CrashLoggingTest, InitSize) {
123  base::debug::CrashKey keys[] = {
124    { "chunked-3", 15 },
125    { "single", 5 },
126    { "chunked-6", 30 },
127  };
128
129  size_t num_keys = base::debug::InitCrashKeys(keys, arraysize(keys), 5);
130
131  EXPECT_EQ(10u, num_keys);
132
133  EXPECT_TRUE(base::debug::LookupCrashKey("chunked-3"));
134  EXPECT_TRUE(base::debug::LookupCrashKey("single"));
135  EXPECT_TRUE(base::debug::LookupCrashKey("chunked-6"));
136  EXPECT_FALSE(base::debug::LookupCrashKey("chunked-6-4"));
137}
138
139TEST_F(CrashLoggingTest, ChunkValue) {
140  using base::debug::ChunkCrashKeyValue;
141
142  // Test truncation.
143  base::debug::CrashKey key = { "chunky", 10 };
144  std::vector<std::string> results =
145      ChunkCrashKeyValue(key, "hello world", 64);
146  ASSERT_EQ(1u, results.size());
147  EXPECT_EQ("hello worl", results[0]);
148
149  // Test short string.
150  results = ChunkCrashKeyValue(key, "hi", 10);
151  ASSERT_EQ(1u, results.size());
152  EXPECT_EQ("hi", results[0]);
153
154  // Test chunk pair.
155  key.max_length = 6;
156  results = ChunkCrashKeyValue(key, "foobar", 3);
157  ASSERT_EQ(2u, results.size());
158  EXPECT_EQ("foo", results[0]);
159  EXPECT_EQ("bar", results[1]);
160
161  // Test chunk pair truncation.
162  results = ChunkCrashKeyValue(key, "foobared", 3);
163  ASSERT_EQ(2u, results.size());
164  EXPECT_EQ("foo", results[0]);
165  EXPECT_EQ("bar", results[1]);
166
167  // Test extra chunks.
168  key.max_length = 100;
169  results = ChunkCrashKeyValue(key, "hello world", 3);
170  ASSERT_EQ(4u, results.size());
171  EXPECT_EQ("hel", results[0]);
172  EXPECT_EQ("lo ", results[1]);
173  EXPECT_EQ("wor", results[2]);
174  EXPECT_EQ("ld",  results[3]);
175}
176
177TEST_F(CrashLoggingTest, ChunkRounding) {
178  // If max_length=12 and max_chunk_length=5, there should be 3 chunks,
179  // not 2.
180  base::debug::CrashKey key = { "round", 12 };
181  EXPECT_EQ(3u, base::debug::InitCrashKeys(&key, 1, 5));
182}
183