// 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 #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/net_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 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 usage, int64 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 callback_usage_; int64 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 storage_policy_; scoped_refptr 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 kUsage = 324554; const int64 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 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 kUsage = 74387; const int64 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 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 kUsage = -324554; const int64 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 kUsage = 6656; const int64 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 kUsage = 6656; const int64 kQuota = 99585556; const int64 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 kUsage = 5325; const int64 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 storage_policy_; scoped_refptr 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 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