|  | // 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 <stdint.h> | 
|  |  | 
|  | #include <vector> | 
|  |  | 
|  | #include "base/files/scoped_temp_dir.h" | 
|  | #include "base/run_loop.h" | 
|  | #include "base/thread_task_runner_handle.h" | 
|  | #include "content/public/test/mock_special_storage_policy.h" | 
|  | #include "content/public/test/mock_storage_client.h" | 
|  | #include "net/base/url_util.h" | 
|  | #include "storage/browser/quota/quota_manager.h" | 
|  | #include "storage/browser/quota/quota_manager_proxy.h" | 
|  | #include "storage/browser/quota/storage_monitor.h" | 
|  | #include "storage/browser/quota/storage_observer.h" | 
|  | #include "testing/gtest/include/gtest/gtest.h" | 
|  |  | 
|  | using storage::HostStorageObservers; | 
|  | using storage::kQuotaErrorNotSupported; | 
|  | using storage::kQuotaStatusOk; | 
|  | using storage::kStorageTypePersistent; | 
|  | using storage::kStorageTypeTemporary; | 
|  | using storage::QuotaClient; | 
|  | using storage::QuotaManager; | 
|  | using storage::QuotaStatusCode; | 
|  | using storage::SpecialStoragePolicy; | 
|  | using storage::StorageMonitor; | 
|  | using storage::StorageObserver; | 
|  | using storage::StorageObserverList; | 
|  | using storage::StorageType; | 
|  | using storage::StorageTypeObservers; | 
|  |  | 
|  | namespace content { | 
|  |  | 
|  | namespace { | 
|  |  | 
|  | const char kDefaultOrigin[] = "http://www.foo.com/"; | 
|  | const char kAlternativeOrigin[] = "http://www.bar.com/"; | 
|  |  | 
|  | class MockObserver : public StorageObserver { | 
|  | public: | 
|  | const StorageObserver::Event& LastEvent() const { | 
|  | CHECK(!events_.empty()); | 
|  | return events_.back(); | 
|  | } | 
|  |  | 
|  | int EventCount() const { | 
|  | return events_.size(); | 
|  | } | 
|  |  | 
|  | // StorageObserver implementation: | 
|  | void OnStorageEvent(const StorageObserver::Event& event) override { | 
|  | events_.push_back(event); | 
|  | } | 
|  |  | 
|  | private: | 
|  | std::vector<StorageObserver::Event> events_; | 
|  | }; | 
|  |  | 
|  | // A mock quota manager for overriding GetUsageAndQuotaForWebApps(). | 
|  | class UsageMockQuotaManager : public QuotaManager { | 
|  | public: | 
|  | UsageMockQuotaManager(SpecialStoragePolicy* special_storage_policy) | 
|  | : QuotaManager(false, | 
|  | base::FilePath(), | 
|  | base::ThreadTaskRunnerHandle::Get().get(), | 
|  | base::ThreadTaskRunnerHandle::Get().get(), | 
|  | special_storage_policy), | 
|  | callback_usage_(0), | 
|  | callback_quota_(0), | 
|  | callback_status_(kQuotaStatusOk), | 
|  | initialized_(false) {} | 
|  |  | 
|  | void SetCallbackParams(int64_t usage, int64_t quota, QuotaStatusCode status) { | 
|  | initialized_ = true; | 
|  | callback_quota_ = quota; | 
|  | callback_usage_ = usage; | 
|  | callback_status_ = status; | 
|  | } | 
|  |  | 
|  | void InvokeCallback() { | 
|  | delayed_callback_.Run(callback_status_, callback_usage_, callback_quota_); | 
|  | } | 
|  |  | 
|  | void GetUsageAndQuotaForWebApps( | 
|  | const GURL& origin, | 
|  | StorageType type, | 
|  | const GetUsageAndQuotaCallback& callback) override { | 
|  | if (initialized_) | 
|  | callback.Run(callback_status_, callback_usage_, callback_quota_); | 
|  | else | 
|  | delayed_callback_ = callback; | 
|  | } | 
|  |  | 
|  | protected: | 
|  | ~UsageMockQuotaManager() override {} | 
|  |  | 
|  | private: | 
|  | int64_t callback_usage_; | 
|  | int64_t callback_quota_; | 
|  | QuotaStatusCode callback_status_; | 
|  | bool initialized_; | 
|  | GetUsageAndQuotaCallback delayed_callback_; | 
|  | }; | 
|  |  | 
|  | }  // namespace | 
|  |  | 
|  | class StorageMonitorTestBase : public testing::Test { | 
|  | protected: | 
|  | void DispatchPendingEvents(StorageObserverList& observer_list) { | 
|  | observer_list.DispatchPendingEvent(); | 
|  | } | 
|  |  | 
|  | const StorageObserver::Event* GetPendingEvent( | 
|  | const StorageObserverList& observer_list) { | 
|  | return observer_list.notification_timer_.IsRunning() | 
|  | ? &observer_list.pending_event_ : NULL; | 
|  | } | 
|  |  | 
|  | const StorageObserver::Event* GetPendingEvent( | 
|  | const HostStorageObservers& host_observers) { | 
|  | return GetPendingEvent(host_observers.observers_); | 
|  | } | 
|  |  | 
|  | int GetRequiredUpdatesCount(const StorageObserverList& observer_list) { | 
|  | int count = 0; | 
|  | for (StorageObserverList::StorageObserverStateMap::const_iterator it = | 
|  | observer_list.observers_.begin(); | 
|  | it != observer_list.observers_.end(); ++it) { | 
|  | if (it->second.requires_update) | 
|  | ++count; | 
|  | } | 
|  |  | 
|  | return count; | 
|  | } | 
|  |  | 
|  | int GetRequiredUpdatesCount(const HostStorageObservers& host_observers) { | 
|  | return GetRequiredUpdatesCount(host_observers.observers_); | 
|  | } | 
|  |  | 
|  | void SetLastNotificationTime(StorageObserverList& observer_list, | 
|  | StorageObserver* observer) { | 
|  | ASSERT_TRUE(observer_list.observers_.find(observer) != | 
|  | observer_list.observers_.end()); | 
|  |  | 
|  | StorageObserverList::ObserverState& state = | 
|  | observer_list.observers_[observer]; | 
|  | state.last_notification_time = base::TimeTicks::Now() - state.rate; | 
|  | } | 
|  |  | 
|  | void SetLastNotificationTime(HostStorageObservers& host_observers, | 
|  | StorageObserver* observer) { | 
|  | SetLastNotificationTime(host_observers.observers_, observer); | 
|  | } | 
|  |  | 
|  | int GetObserverCount(const HostStorageObservers& host_observers) { | 
|  | return host_observers.observers_.ObserverCount(); | 
|  | } | 
|  | }; | 
|  |  | 
|  | class StorageTestWithManagerBase : public StorageMonitorTestBase { | 
|  | public: | 
|  | void SetUp() override { | 
|  | storage_policy_ = new MockSpecialStoragePolicy(); | 
|  | quota_manager_ = new UsageMockQuotaManager(storage_policy_.get()); | 
|  | } | 
|  |  | 
|  | void TearDown() override { | 
|  | // This ensures the quota manager is destroyed correctly. | 
|  | quota_manager_ = NULL; | 
|  | base::RunLoop().RunUntilIdle(); | 
|  | } | 
|  |  | 
|  | protected: | 
|  | base::MessageLoop message_loop_; | 
|  | scoped_refptr<MockSpecialStoragePolicy> storage_policy_; | 
|  | scoped_refptr<UsageMockQuotaManager> quota_manager_; | 
|  | }; | 
|  |  | 
|  | // Tests for StorageObserverList: | 
|  |  | 
|  | typedef StorageMonitorTestBase StorageObserverListTest; | 
|  |  | 
|  | // Test dispatching events to one observer. | 
|  | TEST_F(StorageObserverListTest, DispatchEventToSingleObserver) { | 
|  | // A message loop is required as StorageObserverList may schedule jobs. | 
|  | base::MessageLoop loop(base::MessageLoop::TYPE_DEFAULT); | 
|  |  | 
|  | StorageObserver::MonitorParams params(kStorageTypePersistent, | 
|  | GURL(kDefaultOrigin), | 
|  | base::TimeDelta::FromHours(1), | 
|  | false); | 
|  | MockObserver mock_observer; | 
|  | StorageObserverList observer_list; | 
|  | observer_list.AddObserver(&mock_observer, params); | 
|  |  | 
|  | StorageObserver::Event event; | 
|  | event.filter = params.filter; | 
|  |  | 
|  | // Verify that the first event is dispatched immediately. | 
|  | event.quota = 1; | 
|  | event.usage = 1; | 
|  | observer_list.OnStorageChange(event); | 
|  | EXPECT_EQ(1, mock_observer.EventCount()); | 
|  | EXPECT_EQ(event, mock_observer.LastEvent()); | 
|  | EXPECT_EQ(NULL, GetPendingEvent(observer_list)); | 
|  | EXPECT_EQ(0, GetRequiredUpdatesCount(observer_list)); | 
|  |  | 
|  | // Verify that the next event is pending. | 
|  | event.quota = 2; | 
|  | event.usage = 2; | 
|  | observer_list.OnStorageChange(event); | 
|  | EXPECT_EQ(1, mock_observer.EventCount()); | 
|  | ASSERT_TRUE(GetPendingEvent(observer_list)); | 
|  | EXPECT_EQ(event, *GetPendingEvent(observer_list)); | 
|  | EXPECT_EQ(1, GetRequiredUpdatesCount(observer_list)); | 
|  |  | 
|  | // Fake the last notification time so that an event will be dispatched. | 
|  | SetLastNotificationTime(observer_list, &mock_observer); | 
|  | event.quota = 3; | 
|  | event.usage = 3; | 
|  | observer_list.OnStorageChange(event); | 
|  | EXPECT_EQ(2, mock_observer.EventCount()); | 
|  | EXPECT_EQ(event, mock_observer.LastEvent()); | 
|  | EXPECT_EQ(NULL, GetPendingEvent(observer_list)); | 
|  | EXPECT_EQ(0, GetRequiredUpdatesCount(observer_list)); | 
|  |  | 
|  | // Remove the observer. | 
|  | event.quota = 4; | 
|  | event.usage = 4; | 
|  | observer_list.RemoveObserver(&mock_observer); | 
|  | observer_list.OnStorageChange(event); | 
|  | EXPECT_EQ(2, mock_observer.EventCount()); | 
|  | EXPECT_EQ(NULL, GetPendingEvent(observer_list)); | 
|  | } | 
|  |  | 
|  | // Test dispatching events to multiple observers. | 
|  | TEST_F(StorageObserverListTest, DispatchEventToMultipleObservers) { | 
|  | // A message loop is required as StorageObserverList may schedule jobs. | 
|  | base::MessageLoop loop(base::MessageLoop::TYPE_DEFAULT); | 
|  |  | 
|  | MockObserver mock_observer1; | 
|  | MockObserver mock_observer2; | 
|  | StorageObserverList observer_list; | 
|  | StorageObserver::Filter filter(kStorageTypePersistent, | 
|  | GURL(kDefaultOrigin)); | 
|  | observer_list.AddObserver( | 
|  | &mock_observer1, | 
|  | StorageObserver::MonitorParams( | 
|  | filter, base::TimeDelta::FromHours(1), false)); | 
|  | observer_list.AddObserver( | 
|  | &mock_observer2, | 
|  | StorageObserver::MonitorParams( | 
|  | filter, base::TimeDelta::FromHours(2), false)); | 
|  |  | 
|  | StorageObserver::Event event; | 
|  | event.filter = filter; | 
|  |  | 
|  | // Verify that the first event is dispatched immediately. | 
|  | event.quota = 1; | 
|  | event.usage = 1; | 
|  | observer_list.OnStorageChange(event); | 
|  | EXPECT_EQ(1, mock_observer1.EventCount()); | 
|  | EXPECT_EQ(1, mock_observer2.EventCount()); | 
|  | EXPECT_EQ(event, mock_observer1.LastEvent()); | 
|  | EXPECT_EQ(event, mock_observer2.LastEvent()); | 
|  | EXPECT_EQ(NULL, GetPendingEvent(observer_list)); | 
|  | EXPECT_EQ(0, GetRequiredUpdatesCount(observer_list)); | 
|  |  | 
|  | // Fake the last notification time so that observer1 will receive the next | 
|  | // event, but it will be pending for observer2. | 
|  | SetLastNotificationTime(observer_list, &mock_observer1); | 
|  | event.quota = 2; | 
|  | event.usage = 2; | 
|  | observer_list.OnStorageChange(event); | 
|  | EXPECT_EQ(2, mock_observer1.EventCount()); | 
|  | EXPECT_EQ(1, mock_observer2.EventCount()); | 
|  | EXPECT_EQ(event, mock_observer1.LastEvent()); | 
|  | ASSERT_TRUE(GetPendingEvent(observer_list)); | 
|  | EXPECT_EQ(event, *GetPendingEvent(observer_list)); | 
|  | EXPECT_EQ(1, GetRequiredUpdatesCount(observer_list)); | 
|  |  | 
|  | // Now dispatch the pending event to observer2. | 
|  | SetLastNotificationTime(observer_list, &mock_observer2); | 
|  | DispatchPendingEvents(observer_list); | 
|  | EXPECT_EQ(2, mock_observer1.EventCount()); | 
|  | EXPECT_EQ(2, mock_observer2.EventCount()); | 
|  | EXPECT_EQ(event, mock_observer1.LastEvent()); | 
|  | EXPECT_EQ(event, mock_observer2.LastEvent()); | 
|  | EXPECT_EQ(NULL, GetPendingEvent(observer_list)); | 
|  | EXPECT_EQ(0, GetRequiredUpdatesCount(observer_list)); | 
|  | } | 
|  |  | 
|  | // Ensure that the |origin| field in events match the origin specified by the | 
|  | // observer on registration. | 
|  | TEST_F(StorageObserverListTest, ReplaceEventOrigin) { | 
|  | StorageObserver::MonitorParams params(kStorageTypePersistent, | 
|  | GURL(kDefaultOrigin), | 
|  | base::TimeDelta::FromHours(1), | 
|  | false); | 
|  | MockObserver mock_observer; | 
|  | StorageObserverList observer_list; | 
|  | observer_list.AddObserver(&mock_observer, params); | 
|  |  | 
|  | StorageObserver::Event dispatched_event; | 
|  | dispatched_event.filter = params.filter; | 
|  | dispatched_event.filter.origin = GURL("https://www.foo.com/bar"); | 
|  | observer_list.OnStorageChange(dispatched_event); | 
|  |  | 
|  | EXPECT_EQ(params.filter.origin, mock_observer.LastEvent().filter.origin); | 
|  | } | 
|  |  | 
|  | // Tests for HostStorageObservers: | 
|  |  | 
|  | typedef StorageTestWithManagerBase HostStorageObserversTest; | 
|  |  | 
|  | // Verify that HostStorageObservers is initialized after the first usage change. | 
|  | TEST_F(HostStorageObserversTest, InitializeOnUsageChange) { | 
|  | StorageObserver::MonitorParams params(kStorageTypePersistent, | 
|  | GURL(kDefaultOrigin), | 
|  | base::TimeDelta::FromHours(1), | 
|  | false); | 
|  | const int64_t kUsage = 324554; | 
|  | const int64_t kQuota = 234354354; | 
|  | quota_manager_->SetCallbackParams(kUsage, kQuota, kQuotaStatusOk); | 
|  |  | 
|  | MockObserver mock_observer; | 
|  | HostStorageObservers host_observers(quota_manager_.get()); | 
|  | host_observers.AddObserver(&mock_observer, params); | 
|  |  | 
|  | // Verify that HostStorageObservers dispatches the first event correctly. | 
|  | StorageObserver::Event expected_event(params.filter, kUsage, kQuota); | 
|  | host_observers.NotifyUsageChange(params.filter, 87324); | 
|  | EXPECT_EQ(1, mock_observer.EventCount()); | 
|  | EXPECT_EQ(expected_event, mock_observer.LastEvent()); | 
|  | EXPECT_TRUE(host_observers.is_initialized()); | 
|  |  | 
|  | // Verify that HostStorageObservers handles subsequent usage changes | 
|  | // correctly. | 
|  | const int64_t kDelta = 2345; | 
|  | expected_event.usage += kDelta; | 
|  | SetLastNotificationTime(host_observers, &mock_observer); | 
|  | host_observers.NotifyUsageChange(params.filter, kDelta); | 
|  | EXPECT_EQ(2, mock_observer.EventCount()); | 
|  | EXPECT_EQ(expected_event, mock_observer.LastEvent()); | 
|  | } | 
|  |  | 
|  | // Verify that HostStorageObservers is initialized after the adding the first | 
|  | // observer that elected to receive the initial state. | 
|  | TEST_F(HostStorageObserversTest, InitializeOnObserver) { | 
|  | const int64_t kUsage = 74387; | 
|  | const int64_t kQuota = 92834743; | 
|  | quota_manager_->SetCallbackParams(kUsage, kQuota, kQuotaStatusOk); | 
|  | HostStorageObservers host_observers(quota_manager_.get()); | 
|  |  | 
|  | // |host_observers| should not be initialized after the first observer is | 
|  | // added because it did not elect to receive the initial state. | 
|  | StorageObserver::MonitorParams params(kStorageTypePersistent, | 
|  | GURL(kDefaultOrigin), | 
|  | base::TimeDelta::FromHours(1), | 
|  | false); | 
|  | MockObserver mock_observer1; | 
|  | host_observers.AddObserver(&mock_observer1, params); | 
|  | EXPECT_FALSE(host_observers.is_initialized()); | 
|  | EXPECT_EQ(0, mock_observer1.EventCount()); | 
|  |  | 
|  | // |host_observers| should be initialized after the second observer is | 
|  | // added. | 
|  | MockObserver mock_observer2; | 
|  | params.dispatch_initial_state = true; | 
|  | host_observers.AddObserver(&mock_observer2, params); | 
|  | StorageObserver::Event expected_event(params.filter, kUsage, kQuota); | 
|  | EXPECT_EQ(0, mock_observer1.EventCount()); | 
|  | EXPECT_EQ(1, mock_observer2.EventCount()); | 
|  | EXPECT_EQ(expected_event, mock_observer2.LastEvent()); | 
|  | EXPECT_TRUE(host_observers.is_initialized()); | 
|  | EXPECT_EQ(NULL, GetPendingEvent(host_observers)); | 
|  | EXPECT_EQ(0, GetRequiredUpdatesCount(host_observers)); | 
|  |  | 
|  | // Verify that both observers will receive events after a usage change. | 
|  | const int64_t kDelta = 2345; | 
|  | expected_event.usage += kDelta; | 
|  | SetLastNotificationTime(host_observers, &mock_observer2); | 
|  | host_observers.NotifyUsageChange(params.filter, kDelta); | 
|  | EXPECT_EQ(1, mock_observer1.EventCount()); | 
|  | EXPECT_EQ(2, mock_observer2.EventCount()); | 
|  | EXPECT_EQ(expected_event, mock_observer1.LastEvent()); | 
|  | EXPECT_EQ(expected_event, mock_observer2.LastEvent()); | 
|  | EXPECT_EQ(NULL, GetPendingEvent(host_observers)); | 
|  | EXPECT_EQ(0, GetRequiredUpdatesCount(host_observers)); | 
|  |  | 
|  | // Verify that the addition of a third observer only causes an event to be | 
|  | // dispatched to the new observer. | 
|  | MockObserver mock_observer3; | 
|  | params.dispatch_initial_state = true; | 
|  | host_observers.AddObserver(&mock_observer3, params); | 
|  | EXPECT_EQ(1, mock_observer1.EventCount()); | 
|  | EXPECT_EQ(2, mock_observer2.EventCount()); | 
|  | EXPECT_EQ(1, mock_observer3.EventCount()); | 
|  | EXPECT_EQ(expected_event, mock_observer3.LastEvent()); | 
|  | } | 
|  |  | 
|  | // Verify that negative usage and quota is changed to zero. | 
|  | TEST_F(HostStorageObserversTest, NegativeUsageAndQuota) { | 
|  | StorageObserver::MonitorParams params(kStorageTypePersistent, | 
|  | GURL(kDefaultOrigin), | 
|  | base::TimeDelta::FromHours(1), | 
|  | false); | 
|  | const int64_t kUsage = -324554; | 
|  | const int64_t kQuota = -234354354; | 
|  | quota_manager_->SetCallbackParams(kUsage, kQuota, kQuotaStatusOk); | 
|  |  | 
|  | MockObserver mock_observer; | 
|  | HostStorageObservers host_observers(quota_manager_.get()); | 
|  | host_observers.AddObserver(&mock_observer, params); | 
|  |  | 
|  | StorageObserver::Event expected_event(params.filter, 0, 0); | 
|  | host_observers.NotifyUsageChange(params.filter, -87324); | 
|  | EXPECT_EQ(expected_event, mock_observer.LastEvent()); | 
|  | } | 
|  |  | 
|  | // Verify that HostStorageObservers can recover from a bad initialization. | 
|  | TEST_F(HostStorageObserversTest, RecoverFromBadUsageInit) { | 
|  | StorageObserver::MonitorParams params(kStorageTypePersistent, | 
|  | GURL(kDefaultOrigin), | 
|  | base::TimeDelta::FromHours(1), | 
|  | false); | 
|  | MockObserver mock_observer; | 
|  | HostStorageObservers host_observers(quota_manager_.get()); | 
|  | host_observers.AddObserver(&mock_observer, params); | 
|  |  | 
|  | // Set up the quota manager to return an error status. | 
|  | const int64_t kUsage = 6656; | 
|  | const int64_t kQuota = 99585556; | 
|  | quota_manager_->SetCallbackParams(kUsage, kQuota, kQuotaErrorNotSupported); | 
|  |  | 
|  | // Verify that |host_observers| is not initialized and an event has not been | 
|  | // dispatched. | 
|  | host_observers.NotifyUsageChange(params.filter, 9438); | 
|  | EXPECT_EQ(0, mock_observer.EventCount()); | 
|  | EXPECT_FALSE(host_observers.is_initialized()); | 
|  | EXPECT_EQ(NULL, GetPendingEvent(host_observers)); | 
|  | EXPECT_EQ(0, GetRequiredUpdatesCount(host_observers)); | 
|  |  | 
|  | // Now ensure that quota manager returns a good status. | 
|  | quota_manager_->SetCallbackParams(kUsage, kQuota, kQuotaStatusOk); | 
|  | host_observers.NotifyUsageChange(params.filter, 9048543); | 
|  | StorageObserver::Event expected_event(params.filter, kUsage, kQuota); | 
|  | EXPECT_EQ(1, mock_observer.EventCount()); | 
|  | EXPECT_EQ(expected_event, mock_observer.LastEvent()); | 
|  | EXPECT_TRUE(host_observers.is_initialized()); | 
|  | } | 
|  |  | 
|  | // Verify that HostStorageObservers handle initialization of the cached usage | 
|  | // and quota correctly. | 
|  | TEST_F(HostStorageObserversTest, AsyncInitialization) { | 
|  | StorageObserver::MonitorParams params(kStorageTypePersistent, | 
|  | GURL(kDefaultOrigin), | 
|  | base::TimeDelta::FromHours(1), | 
|  | false); | 
|  | MockObserver mock_observer; | 
|  | HostStorageObservers host_observers(quota_manager_.get()); | 
|  | host_observers.AddObserver(&mock_observer, params); | 
|  |  | 
|  | // Trigger initialization. Leave the mock quota manager uninitialized so that | 
|  | // the callback is not invoked. | 
|  | host_observers.NotifyUsageChange(params.filter, 7645); | 
|  | EXPECT_EQ(0, mock_observer.EventCount()); | 
|  | EXPECT_FALSE(host_observers.is_initialized()); | 
|  | EXPECT_EQ(NULL, GetPendingEvent(host_observers)); | 
|  | EXPECT_EQ(0, GetRequiredUpdatesCount(host_observers)); | 
|  |  | 
|  | // Simulate notifying |host_observers| of a usage change before initialization | 
|  | // is complete. | 
|  | const int64_t kUsage = 6656; | 
|  | const int64_t kQuota = 99585556; | 
|  | const int64_t kDelta = 327643; | 
|  | host_observers.NotifyUsageChange(params.filter, kDelta); | 
|  | EXPECT_EQ(0, mock_observer.EventCount()); | 
|  | EXPECT_FALSE(host_observers.is_initialized()); | 
|  | EXPECT_EQ(NULL, GetPendingEvent(host_observers)); | 
|  | EXPECT_EQ(0, GetRequiredUpdatesCount(host_observers)); | 
|  |  | 
|  | // Simulate an asynchronous callback from QuotaManager. | 
|  | quota_manager_->SetCallbackParams(kUsage, kQuota, kQuotaStatusOk); | 
|  | quota_manager_->InvokeCallback(); | 
|  | StorageObserver::Event expected_event(params.filter, kUsage + kDelta, kQuota); | 
|  | EXPECT_EQ(1, mock_observer.EventCount()); | 
|  | EXPECT_EQ(expected_event, mock_observer.LastEvent()); | 
|  | EXPECT_TRUE(host_observers.is_initialized()); | 
|  | EXPECT_EQ(NULL, GetPendingEvent(host_observers)); | 
|  | EXPECT_EQ(0, GetRequiredUpdatesCount(host_observers)); | 
|  | } | 
|  |  | 
|  | // Tests for StorageTypeObservers: | 
|  |  | 
|  | typedef StorageTestWithManagerBase StorageTypeObserversTest; | 
|  |  | 
|  | // Test adding and removing observers. | 
|  | TEST_F(StorageTypeObserversTest, AddRemoveObservers) { | 
|  | StorageTypeObservers type_observers(quota_manager_.get()); | 
|  |  | 
|  | StorageObserver::MonitorParams params1(kStorageTypePersistent, | 
|  | GURL(kDefaultOrigin), | 
|  | base::TimeDelta::FromHours(1), | 
|  | false); | 
|  | StorageObserver::MonitorParams params2(kStorageTypePersistent, | 
|  | GURL(kAlternativeOrigin), | 
|  | base::TimeDelta::FromHours(1), | 
|  | false); | 
|  | std::string host1 = net::GetHostOrSpecFromURL(params1.filter.origin); | 
|  | std::string host2 = net::GetHostOrSpecFromURL(params2.filter.origin); | 
|  |  | 
|  | MockObserver mock_observer1; | 
|  | MockObserver mock_observer2; | 
|  | MockObserver mock_observer3; | 
|  | type_observers.AddObserver(&mock_observer1, params1); | 
|  | type_observers.AddObserver(&mock_observer2, params1); | 
|  |  | 
|  | type_observers.AddObserver(&mock_observer1, params2); | 
|  | type_observers.AddObserver(&mock_observer2, params2); | 
|  | type_observers.AddObserver(&mock_observer3, params2); | 
|  |  | 
|  | // Verify that the observers have been removed correctly. | 
|  | ASSERT_TRUE(type_observers.GetHostObservers(host1)); | 
|  | ASSERT_TRUE(type_observers.GetHostObservers(host2)); | 
|  | EXPECT_EQ(2, GetObserverCount(*type_observers.GetHostObservers(host1))); | 
|  | EXPECT_EQ(3, GetObserverCount(*type_observers.GetHostObservers(host2))); | 
|  |  | 
|  | // Remove an observer for a specific filter. | 
|  | type_observers.RemoveObserverForFilter(&mock_observer1, params1.filter); | 
|  | ASSERT_TRUE(type_observers.GetHostObservers(host1)); | 
|  | ASSERT_TRUE(type_observers.GetHostObservers(host2)); | 
|  | EXPECT_EQ(1, GetObserverCount(*type_observers.GetHostObservers(host1))); | 
|  | EXPECT_EQ(3, GetObserverCount(*type_observers.GetHostObservers(host2))); | 
|  |  | 
|  | // Remove all instances of an observer. | 
|  | type_observers.RemoveObserver(&mock_observer2); | 
|  | ASSERT_TRUE(type_observers.GetHostObservers(host2)); | 
|  | EXPECT_EQ(2, GetObserverCount(*type_observers.GetHostObservers(host2))); | 
|  | // Observers of host1 has been deleted as it is empty. | 
|  | EXPECT_FALSE(type_observers.GetHostObservers(host1)); | 
|  | } | 
|  |  | 
|  | // Tests for StorageMonitor: | 
|  |  | 
|  | class StorageMonitorTest : public StorageTestWithManagerBase { | 
|  | public: | 
|  | StorageMonitorTest() | 
|  | : storage_monitor_(NULL), | 
|  | params1_(kStorageTypeTemporary, | 
|  | GURL(kDefaultOrigin), | 
|  | base::TimeDelta::FromHours(1), | 
|  | false), | 
|  | params2_(kStorageTypePersistent, | 
|  | GURL(kDefaultOrigin), | 
|  | base::TimeDelta::FromHours(1), | 
|  | false) { | 
|  | } | 
|  |  | 
|  | protected: | 
|  | void SetUp() override { | 
|  | StorageTestWithManagerBase::SetUp(); | 
|  |  | 
|  | storage_monitor_ = quota_manager_->storage_monitor_.get(); | 
|  | host_ = net::GetHostOrSpecFromURL(params1_.filter.origin); | 
|  |  | 
|  | storage_monitor_->AddObserver(&mock_observer1_, params1_); | 
|  | storage_monitor_->AddObserver(&mock_observer2_, params1_); | 
|  |  | 
|  | storage_monitor_->AddObserver(&mock_observer1_, params2_); | 
|  | storage_monitor_->AddObserver(&mock_observer2_, params2_); | 
|  | storage_monitor_->AddObserver(&mock_observer3_, params2_); | 
|  | } | 
|  |  | 
|  | int GetObserverCount(StorageType storage_type) { | 
|  | const StorageTypeObservers* type_observers = | 
|  | storage_monitor_->GetStorageTypeObservers(storage_type); | 
|  | return StorageMonitorTestBase::GetObserverCount( | 
|  | *type_observers->GetHostObservers(host_)); | 
|  | } | 
|  |  | 
|  | void CheckObserverCount(int expected_temporary, int expected_persistent) { | 
|  | ASSERT_TRUE(storage_monitor_->GetStorageTypeObservers( | 
|  | kStorageTypeTemporary)); | 
|  | ASSERT_TRUE(storage_monitor_->GetStorageTypeObservers( | 
|  | kStorageTypeTemporary)->GetHostObservers(host_)); | 
|  | EXPECT_EQ(expected_temporary, GetObserverCount(kStorageTypeTemporary)); | 
|  |  | 
|  | ASSERT_TRUE(storage_monitor_->GetStorageTypeObservers( | 
|  | kStorageTypePersistent)); | 
|  | ASSERT_TRUE(storage_monitor_->GetStorageTypeObservers( | 
|  | kStorageTypePersistent)->GetHostObservers(host_)); | 
|  | EXPECT_EQ(expected_persistent, GetObserverCount(kStorageTypePersistent)); | 
|  | } | 
|  |  | 
|  | StorageMonitor* storage_monitor_; | 
|  | StorageObserver::MonitorParams params1_; | 
|  | StorageObserver::MonitorParams params2_; | 
|  | MockObserver mock_observer1_; | 
|  | MockObserver mock_observer2_; | 
|  | MockObserver mock_observer3_; | 
|  | std::string host_; | 
|  | }; | 
|  |  | 
|  | // Test adding storage observers. | 
|  | TEST_F(StorageMonitorTest, AddObservers) { | 
|  | // Verify that the observers are added correctly. | 
|  | CheckObserverCount(2, 3); | 
|  | } | 
|  |  | 
|  | // Test dispatching events to storage observers. | 
|  | TEST_F(StorageMonitorTest, EventDispatch) { | 
|  | // Verify dispatch of events. | 
|  | const int64_t kUsage = 5325; | 
|  | const int64_t kQuota = 903845; | 
|  | quota_manager_->SetCallbackParams(kUsage, kQuota, kQuotaStatusOk); | 
|  | storage_monitor_->NotifyUsageChange(params1_.filter, 9048543); | 
|  |  | 
|  | StorageObserver::Event expected_event(params1_.filter, kUsage, kQuota); | 
|  | EXPECT_EQ(1, mock_observer1_.EventCount()); | 
|  | EXPECT_EQ(1, mock_observer2_.EventCount()); | 
|  | EXPECT_EQ(0, mock_observer3_.EventCount()); | 
|  | EXPECT_EQ(expected_event, mock_observer1_.LastEvent()); | 
|  | EXPECT_EQ(expected_event, mock_observer2_.LastEvent()); | 
|  | } | 
|  |  | 
|  | // Test removing all instances of an observer. | 
|  | TEST_F(StorageMonitorTest, RemoveObserver) { | 
|  | storage_monitor_->RemoveObserver(&mock_observer1_); | 
|  | CheckObserverCount(1, 2); | 
|  | } | 
|  |  | 
|  | // Test removing an observer for a specific filter. | 
|  | TEST_F(StorageMonitorTest, RemoveObserverForFilter) { | 
|  | storage_monitor_->RemoveObserverForFilter(&mock_observer1_, params2_.filter); | 
|  | CheckObserverCount(2, 2); | 
|  | } | 
|  |  | 
|  | // Integration test for QuotaManager and StorageMonitor: | 
|  |  | 
|  | class StorageMonitorIntegrationTest : public testing::Test { | 
|  | public: | 
|  | void SetUp() override { | 
|  | ASSERT_TRUE(data_dir_.CreateUniqueTempDir()); | 
|  | storage_policy_ = new MockSpecialStoragePolicy(); | 
|  | quota_manager_ = new QuotaManager( | 
|  | false, data_dir_.path(), base::ThreadTaskRunnerHandle::Get().get(), | 
|  | base::ThreadTaskRunnerHandle::Get().get(), storage_policy_.get()); | 
|  |  | 
|  | client_ = new MockStorageClient(quota_manager_->proxy(), | 
|  | NULL, | 
|  | QuotaClient::kFileSystem, | 
|  | 0); | 
|  |  | 
|  | quota_manager_->proxy()->RegisterClient(client_); | 
|  | } | 
|  |  | 
|  | void TearDown() override { | 
|  | // This ensures the quota manager is destroyed correctly. | 
|  | quota_manager_ = NULL; | 
|  | base::RunLoop().RunUntilIdle(); | 
|  | } | 
|  |  | 
|  | protected: | 
|  | base::MessageLoop message_loop_; | 
|  | base::ScopedTempDir data_dir_; | 
|  | scoped_refptr<MockSpecialStoragePolicy> storage_policy_; | 
|  | scoped_refptr<QuotaManager> quota_manager_; | 
|  | MockStorageClient* client_; | 
|  | }; | 
|  |  | 
|  | // This test simulates a usage change in a quota client and verifies that a | 
|  | // storage observer will receive a storage event. | 
|  | TEST_F(StorageMonitorIntegrationTest, NotifyUsageEvent) { | 
|  | const StorageType kTestStorageType = kStorageTypePersistent; | 
|  | const int64_t kTestUsage = 234743; | 
|  |  | 
|  | // Register the observer. | 
|  | StorageObserver::MonitorParams params(kTestStorageType, | 
|  | GURL(kDefaultOrigin), | 
|  | base::TimeDelta::FromHours(1), | 
|  | false); | 
|  | MockObserver mock_observer; | 
|  | quota_manager_->AddStorageObserver(&mock_observer, params); | 
|  |  | 
|  | // Fire a usage change. | 
|  | client_->AddOriginAndNotify(GURL(kDefaultOrigin), | 
|  | kTestStorageType, | 
|  | kTestUsage); | 
|  | base::RunLoop().RunUntilIdle(); | 
|  |  | 
|  | // Verify that the observer receives it. | 
|  | ASSERT_EQ(1, mock_observer.EventCount()); | 
|  | const StorageObserver::Event& event = mock_observer.LastEvent(); | 
|  | EXPECT_EQ(params.filter, event.filter); | 
|  | EXPECT_EQ(kTestUsage, event.usage); | 
|  | } | 
|  |  | 
|  | }  // namespace content |