// Copyright 2014 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "extensions/browser/error_map.h" #include "base/logging.h" #include "base/memory/scoped_ptr.h" #include "base/strings/string_number_conversions.h" #include "components/crx_file/id_util.h" #include "extensions/browser/extension_error.h" #include "extensions/browser/extension_error_test_util.h" #include "extensions/common/constants.h" #include "testing/gtest/include/gtest/gtest.h" namespace extensions { using error_test_util::CreateNewRuntimeError; class ErrorMapUnitTest : public testing::Test { public: ErrorMapUnitTest() { } virtual ~ErrorMapUnitTest() { } virtual void SetUp() OVERRIDE { testing::Test::SetUp(); } protected: ErrorMap errors_; }; // Test adding errors, and removing them by reference, by incognito status, // and in bulk. TEST_F(ErrorMapUnitTest, AddAndRemoveErrors) { ASSERT_EQ(0u, errors_.size()); const size_t kNumTotalErrors = 6; const size_t kNumNonIncognitoErrors = 3; const std::string kId = crx_file::id_util::GenerateId("id"); // Populate with both incognito and non-incognito errors (evenly distributed). for (size_t i = 0; i < kNumTotalErrors; ++i) { ASSERT_TRUE(errors_.AddError( CreateNewRuntimeError(kId, base::UintToString(i), i % 2 == 0))); } // There should only be one entry in the map, since errors are stored in lists // keyed by extension id. ASSERT_EQ(1u, errors_.size()); ASSERT_EQ(kNumTotalErrors, errors_.GetErrorsForExtension(kId).size()); // Remove the incognito errors; three errors should remain, and all should // be from non-incognito contexts. errors_.RemoveIncognitoErrors(); const ErrorList& list = errors_.GetErrorsForExtension(kId); ASSERT_EQ(kNumNonIncognitoErrors, list.size()); for (size_t i = 0; i < list.size(); ++i) ASSERT_FALSE(list[i]->from_incognito()); // Add another error for a different extension id. const std::string kSecondId = crx_file::id_util::GenerateId("id2"); ASSERT_TRUE(errors_.AddError(CreateNewRuntimeError(kSecondId, "foo"))); // There should be two entries now, one for each id, and there should be one // error for the second extension. ASSERT_EQ(2u, errors_.size()); ASSERT_EQ(1u, errors_.GetErrorsForExtension(kSecondId).size()); // Remove all errors for the second id. errors_.Remove(kSecondId); ASSERT_EQ(1u, errors_.size()); ASSERT_EQ(0u, errors_.GetErrorsForExtension(kSecondId).size()); // First extension should be unaffected. ASSERT_EQ(kNumNonIncognitoErrors, errors_.GetErrorsForExtension(kId).size()); // Remove remaining errors. errors_.RemoveAllErrors(); ASSERT_EQ(0u, errors_.size()); ASSERT_EQ(0u, errors_.GetErrorsForExtension(kId).size()); } // Test that if we add enough errors, only the most recent // kMaxErrorsPerExtension are kept. TEST_F(ErrorMapUnitTest, ExcessiveErrorsGetCropped) { ASSERT_EQ(0u, errors_.size()); // This constant matches one of the same name in error_console.cc. const size_t kMaxErrorsPerExtension = 100; const size_t kNumExtraErrors = 5; const std::string kId = crx_file::id_util::GenerateId("id"); // Add new errors, with each error's message set to its number. for (size_t i = 0; i < kMaxErrorsPerExtension + kNumExtraErrors; ++i) { ASSERT_TRUE(errors_.AddError( CreateNewRuntimeError(kId, base::UintToString(i)))); } ASSERT_EQ(1u, errors_.size()); const ErrorList& list = errors_.GetErrorsForExtension(kId); ASSERT_EQ(kMaxErrorsPerExtension, list.size()); // We should have popped off errors in the order they arrived, so the // first stored error should be the 6th reported (zero-based)... ASSERT_EQ(base::UintToString16(kNumExtraErrors), list.front()->message()); // ..and the last stored should be the 105th reported. ASSERT_EQ(base::UintToString16(kMaxErrorsPerExtension + kNumExtraErrors - 1), list.back()->message()); } // Test to ensure that the error console will not add duplicate errors, but will // keep the latest version of an error. TEST_F(ErrorMapUnitTest, DuplicateErrorsAreReplaced) { ASSERT_EQ(0u, errors_.size()); const std::string kId = crx_file::id_util::GenerateId("id"); const size_t kNumErrors = 3u; // Report three errors. for (size_t i = 0; i < kNumErrors; ++i) { ASSERT_TRUE(errors_.AddError( CreateNewRuntimeError(kId, base::UintToString(i)))); } // Create an error identical to the second error reported, save its // location, and add it to the error map. scoped_ptr<ExtensionError> runtime_error2 = CreateNewRuntimeError(kId, base::UintToString(1u)); const ExtensionError* weak_error = runtime_error2.get(); ASSERT_TRUE(errors_.AddError(runtime_error2.Pass())); // We should only have three errors stored, since two of the four reported // were identical, and the older should have been replaced. ASSERT_EQ(1u, errors_.size()); const ErrorList& list = errors_.GetErrorsForExtension(kId); ASSERT_EQ(kNumErrors, list.size()); // The duplicate error should be the last reported (pointer comparison)... ASSERT_EQ(weak_error, list.back()); // ... and should have two reported occurrences. ASSERT_EQ(2u, list.back()->occurrences()); } } // namespace extensions