| // 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 <stddef.h> | 
 |  | 
 | #include <memory> | 
 | #include <utility> | 
 |  | 
 | #include "base/logging.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; | 
 | using error_test_util::CreateNewManifestError; | 
 |  | 
 | class ErrorMapUnitTest : public testing::Test { | 
 |  public: | 
 |   ErrorMapUnitTest() { } | 
 |   ~ErrorMapUnitTest() override {} | 
 |  | 
 |   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::NumberToString(i), i % 2 == 0))); | 
 |   } | 
 |  | 
 |   // There should only be one entry in the map, since errors are stored in lists | 
 |   // keyed by extension id. | 
 |   EXPECT_EQ(1u, errors_.size()); | 
 |  | 
 |   EXPECT_EQ(kNumTotalErrors, errors_.GetErrorsForExtension(kId).size()); | 
 |  | 
 |   // Remove the incognito errors; three errors should remain, and all should | 
 |   // be from non-incognito contexts. | 
 |   std::set<std::string> affected_ids; | 
 |   errors_.RemoveErrors(ErrorMap::Filter::IncognitoErrors(), &affected_ids); | 
 |   const ErrorList& list = errors_.GetErrorsForExtension(kId); | 
 |   EXPECT_EQ(kNumNonIncognitoErrors, list.size()); | 
 |   for (size_t i = 0; i < list.size(); ++i) | 
 |     EXPECT_FALSE(list[i]->from_incognito()); | 
 |   EXPECT_EQ(1u, affected_ids.size()); | 
 |   EXPECT_TRUE(affected_ids.count(kId)); | 
 |  | 
 |   // Add another error for a different extension id. | 
 |   const std::string kSecondId = crx_file::id_util::GenerateId("id2"); | 
 |   EXPECT_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. | 
 |   EXPECT_EQ(2u, errors_.size()); | 
 |   EXPECT_EQ(1u, errors_.GetErrorsForExtension(kSecondId).size()); | 
 |  | 
 |   // Remove all errors for the second id. | 
 |   affected_ids.clear(); | 
 |   errors_.RemoveErrors(ErrorMap::Filter::ErrorsForExtension(kSecondId), | 
 |                        &affected_ids); | 
 |   EXPECT_EQ(0u, errors_.GetErrorsForExtension(kSecondId).size()); | 
 |   // First extension should be unaffected. | 
 |   EXPECT_EQ(kNumNonIncognitoErrors, errors_.GetErrorsForExtension(kId).size()); | 
 |   EXPECT_EQ(1u, affected_ids.size()); | 
 |   EXPECT_TRUE(affected_ids.count(kSecondId)); | 
 |  | 
 |   errors_.AddError(CreateNewManifestError(kId, "manifest error")); | 
 |   EXPECT_EQ(kNumNonIncognitoErrors + 1, | 
 |             errors_.GetErrorsForExtension(kId).size()); | 
 |   errors_.RemoveErrors(ErrorMap::Filter::ErrorsForExtensionWithType( | 
 |       kId, ExtensionError::MANIFEST_ERROR), nullptr); | 
 |   EXPECT_EQ(kNumNonIncognitoErrors, errors_.GetErrorsForExtension(kId).size()); | 
 |  | 
 |   const ExtensionError* added_error = | 
 |       errors_.AddError(CreateNewManifestError(kId, "manifest error2")); | 
 |   EXPECT_EQ(kNumNonIncognitoErrors + 1, | 
 |             errors_.GetErrorsForExtension(kId).size()); | 
 |   std::set<int> ids; | 
 |   ids.insert(added_error->id()); | 
 |   errors_.RemoveErrors(ErrorMap::Filter::ErrorsForExtensionWithIds(kId, ids), | 
 |                        nullptr); | 
 |   EXPECT_EQ(kNumNonIncognitoErrors, errors_.GetErrorsForExtension(kId).size()); | 
 |  | 
 |   // Remove remaining errors. | 
 |   errors_.RemoveAllErrors(); | 
 |   EXPECT_EQ(0u, errors_.size()); | 
 |   EXPECT_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::NumberToString(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::NumberToString16(kNumExtraErrors), list.front()->message()); | 
 |   // ..and the last stored should be the 105th reported. | 
 |   ASSERT_EQ( | 
 |       base::NumberToString16(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::NumberToString(i)))); | 
 |   } | 
 |  | 
 |   // Create an error identical to the second error reported, save its | 
 |   // location, and add it to the error map. | 
 |   std::unique_ptr<ExtensionError> runtime_error2 = | 
 |       CreateNewRuntimeError(kId, base::UintToString(1u)); | 
 |   const ExtensionError* weak_error = runtime_error2.get(); | 
 |   ASSERT_TRUE(errors_.AddError(std::move(runtime_error2))); | 
 |  | 
 |   // 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().get()); | 
 |   // ... and should have two reported occurrences. | 
 |   ASSERT_EQ(2u, list.back()->occurrences()); | 
 | } | 
 |  | 
 | }  // namespace extensions |