error_console_unittest.cc revision f2477e01787aa58f445919b809d89e252beef54f
1// Copyright 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 "chrome/browser/extensions/error_console/error_console.h" 6 7#include "base/json/json_writer.h" 8#include "base/logging.h" 9#include "base/memory/scoped_ptr.h" 10#include "base/prefs/pref_service.h" 11#include "base/strings/string16.h" 12#include "base/strings/string_number_conversions.h" 13#include "base/strings/string_util.h" 14#include "base/strings/utf_string_conversions.h" 15#include "chrome/common/pref_names.h" 16#include "chrome/test/base/testing_profile.h" 17#include "content/public/common/url_constants.h" 18#include "extensions/browser/extension_error.h" 19#include "extensions/common/constants.h" 20#include "extensions/common/feature_switch.h" 21#include "extensions/common/id_util.h" 22#include "testing/gtest/include/gtest/gtest.h" 23#include "url/gurl.h" 24 25using base::string16; 26 27namespace extensions { 28 29namespace { 30 31const char kDefaultStackTrace[] = "function_name (https://url.com:1:1)"; 32 33StackTrace GetDefaultStackTrace() { 34 StackTrace stack_trace; 35 scoped_ptr<StackFrame> frame = 36 StackFrame::CreateFromText(base::UTF8ToUTF16(kDefaultStackTrace)); 37 CHECK(frame.get()); 38 stack_trace.push_back(*frame); 39 return stack_trace; 40} 41 42string16 GetSourceForExtensionId(const std::string& extension_id) { 43 return base::UTF8ToUTF16( 44 std::string(kExtensionScheme) + 45 content::kStandardSchemeSeparator + 46 extension_id); 47} 48 49scoped_ptr<ExtensionError> CreateNewRuntimeError( 50 bool from_incognito, 51 const std::string& extension_id, 52 const string16& message) { 53 return scoped_ptr<ExtensionError>(new RuntimeError( 54 extension_id, 55 from_incognito, 56 GetSourceForExtensionId(extension_id), 57 message, 58 GetDefaultStackTrace(), 59 GURL::EmptyGURL(), // no context url 60 logging::LOG_INFO, 61 0, 0 /* Render [View|Process] ID */ )); 62} 63 64} // namespace 65 66class ErrorConsoleUnitTest : public testing::Test { 67 public: 68 ErrorConsoleUnitTest() : error_console_(NULL) { } 69 virtual ~ErrorConsoleUnitTest() { } 70 71 virtual void SetUp() OVERRIDE { 72 testing::Test::SetUp(); 73 74 // Errors are only kept if we have the FeatureSwitch and have Developer Mode 75 // enabled. 76 FeatureSwitch::error_console()->SetOverrideValue( 77 FeatureSwitch::OVERRIDE_ENABLED); 78 profile_.reset(new TestingProfile); 79 profile_->GetPrefs()->SetBoolean(prefs::kExtensionsUIDeveloperMode, true); 80 error_console_ = ErrorConsole::Get(profile_.get()); 81 } 82 83 protected: 84 scoped_ptr<TestingProfile> profile_; 85 ErrorConsole* error_console_; 86}; 87 88// Test adding errors, and removing them by reference, by incognito status, 89// and in bulk. 90TEST_F(ErrorConsoleUnitTest, AddAndRemoveErrors) { 91 ASSERT_EQ(0u, error_console_->errors().size()); 92 93 const size_t kNumTotalErrors = 6; 94 const size_t kNumNonIncognitoErrors = 3; 95 const std::string kId = id_util::GenerateId("id"); 96 // Populate with both incognito and non-incognito errors (evenly distributed). 97 for (size_t i = 0; i < kNumTotalErrors; ++i) { 98 error_console_->ReportError( 99 CreateNewRuntimeError(i % 2 == 0, kId, base::UintToString16(i))); 100 } 101 102 // There should only be one entry in the map, since errors are stored in lists 103 // keyed by extension id. 104 ASSERT_EQ(1u, error_console_->errors().size()); 105 106 ASSERT_EQ(kNumTotalErrors, error_console_->GetErrorsForExtension(kId).size()); 107 108 // Remove the incognito errors; three errors should remain, and all should 109 // be from non-incognito contexts. 110 error_console_->RemoveIncognitoErrors(); 111 const ErrorConsole::ErrorList& errors = 112 error_console_->GetErrorsForExtension(kId); 113 ASSERT_EQ(kNumNonIncognitoErrors, errors.size()); 114 for (size_t i = 0; i < errors.size(); ++i) 115 ASSERT_FALSE(errors[i]->from_incognito()); 116 117 // Add another error for a different extension id. 118 const std::string kSecondId = id_util::GenerateId("id2"); 119 error_console_->ReportError( 120 CreateNewRuntimeError(false, kSecondId, string16())); 121 122 // There should be two entries now, one for each id, and there should be one 123 // error for the second extension. 124 ASSERT_EQ(2u, error_console_->errors().size()); 125 ASSERT_EQ(1u, error_console_->GetErrorsForExtension(kSecondId).size()); 126 127 // Remove all errors for the second id. 128 error_console_->RemoveErrorsForExtension(kSecondId); 129 ASSERT_EQ(1u, error_console_->errors().size()); 130 ASSERT_EQ(0u, error_console_->GetErrorsForExtension(kSecondId).size()); 131 // First extension should be unaffected. 132 ASSERT_EQ(kNumNonIncognitoErrors, 133 error_console_->GetErrorsForExtension(kId).size()); 134 135 // Remove remaining errors. 136 error_console_->RemoveAllErrors(); 137 ASSERT_EQ(0u, error_console_->errors().size()); 138 ASSERT_EQ(0u, error_console_->GetErrorsForExtension(kId).size()); 139} 140 141// Test that if we add enough errors, only the most recent 142// kMaxErrorsPerExtension are kept. 143TEST_F(ErrorConsoleUnitTest, ExcessiveErrorsGetCropped) { 144 ASSERT_EQ(0u, error_console_->errors().size()); 145 146 // This constant matches one of the same name in error_console.cc. 147 const size_t kMaxErrorsPerExtension = 100; 148 const size_t kNumExtraErrors = 5; 149 const std::string kId = id_util::GenerateId("id"); 150 151 // Add new errors, with each error's message set to its number. 152 for (size_t i = 0; i < kMaxErrorsPerExtension + kNumExtraErrors; ++i) { 153 error_console_->ReportError( 154 CreateNewRuntimeError(false, kId, base::UintToString16(i))); 155 } 156 157 ASSERT_EQ(1u, error_console_->errors().size()); 158 159 const ErrorConsole::ErrorList& errors = 160 error_console_->GetErrorsForExtension(kId); 161 ASSERT_EQ(kMaxErrorsPerExtension, errors.size()); 162 163 // We should have popped off errors in the order they arrived, so the 164 // first stored error should be the 6th reported (zero-based)... 165 ASSERT_EQ(base::UintToString16(kNumExtraErrors), 166 errors.front()->message()); 167 // ..and the last stored should be the 105th reported. 168 ASSERT_EQ(base::UintToString16(kMaxErrorsPerExtension + kNumExtraErrors - 1), 169 errors.back()->message()); 170} 171 172// Test to ensure that the error console will not add duplicate errors, but will 173// keep the latest version of an error. 174TEST_F(ErrorConsoleUnitTest, DuplicateErrorsAreReplaced) { 175 ASSERT_EQ(0u, error_console_->errors().size()); 176 177 const std::string kId = id_util::GenerateId("id"); 178 const size_t kNumErrors = 3u; 179 180 // Report three errors. 181 for (size_t i = 0; i < kNumErrors; ++i) { 182 error_console_->ReportError( 183 CreateNewRuntimeError(false, kId, base::UintToString16(i))); 184 } 185 186 // Create an error identical to the second error reported, and save its 187 // location. 188 scoped_ptr<ExtensionError> runtime_error2 = 189 CreateNewRuntimeError(false, kId, base::UintToString16(1u)); 190 const ExtensionError* weak_error = runtime_error2.get(); 191 192 // Add it to the error console. 193 error_console_->ReportError(runtime_error2.Pass()); 194 195 // We should only have three errors stored, since two of the four reported 196 // were identical, and the older should have been replaced. 197 ASSERT_EQ(1u, error_console_->errors().size()); 198 const ErrorConsole::ErrorList& errors = 199 error_console_->GetErrorsForExtension(kId); 200 ASSERT_EQ(kNumErrors, errors.size()); 201 202 // The duplicate error should be the last reported (pointer comparison)... 203 ASSERT_EQ(weak_error, errors.back()); 204 // ... and should have two reported occurrences. 205 ASSERT_EQ(2u, errors.back()->occurrences()); 206} 207 208} // namespace extensions 209