// 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 #include #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; 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::SizeTToString(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 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 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::SizeTToString(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::SizeTToString16(kNumExtraErrors), list.front()->message()); // ..and the last stored should be the 105th reported. ASSERT_EQ(base::SizeTToString16(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::SizeTToString(i)))); } // Create an error identical to the second error reported, save its // location, and add it to the error map. scoped_ptr 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()); // ... and should have two reported occurrences. ASSERT_EQ(2u, list.back()->occurrences()); } } // namespace extensions