| // Copyright 2019 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. |
| |
| #import "ios/chrome/browser/overlays/overlay_request_queue_impl.h" |
| |
| #include <vector> |
| |
| #include "ios/chrome/browser/overlays/public/overlay_request.h" |
| #import "ios/chrome/browser/overlays/public/overlay_request_cancel_handler.h" |
| #include "ios/chrome/browser/overlays/test/fake_overlay_request_cancel_handler.h" |
| #include "ios/chrome/browser/overlays/test/fake_overlay_user_data.h" |
| #import "ios/web/public/test/fakes/fake_web_state.h" |
| #include "testing/gmock/include/gmock/gmock.h" |
| #include "testing/platform_test.h" |
| |
| #if !defined(__has_feature) || !__has_feature(objc_arc) |
| #error "This file requires ARC support." |
| #endif |
| |
| namespace { |
| // Fake queue delegate. Keeps ownership of all requests removed from a queue, |
| // recording whether the requests were removed for cancellation. |
| class FakeOverlayRequestQueueImplDelegate |
| : public OverlayRequestQueueImpl::Delegate { |
| public: |
| FakeOverlayRequestQueueImplDelegate() {} |
| ~FakeOverlayRequestQueueImplDelegate() {} |
| |
| void OverlayRequestRemoved(OverlayRequestQueueImpl* queue, |
| std::unique_ptr<OverlayRequest> request, |
| bool cancelled) override { |
| removed_requests_.emplace_back(std::move(request), cancelled); |
| } |
| |
| void OverlayRequestQueueWillReplaceDelegate( |
| OverlayRequestQueueImpl* queue) override {} |
| |
| // Whether |request| was removed from the queue. |
| bool WasRequestRemoved(OverlayRequest* request) { |
| return GetRemovedRequestStorage(request) != nullptr; |
| } |
| |
| // Whether |request| was removed from the queue for cancellation. |
| bool WasRequestCancelled(OverlayRequest* request) { |
| const RemovedRequestStorage* storage = GetRemovedRequestStorage(request); |
| return storage && storage->cancelled; |
| } |
| |
| private: |
| // Stores the removed requests and whether the requests were cancelled. |
| struct RemovedRequestStorage { |
| RemovedRequestStorage(std::unique_ptr<OverlayRequest> request, |
| bool cancelled) |
| : request(std::move(request)), cancelled(cancelled) {} |
| RemovedRequestStorage(RemovedRequestStorage&& storage) |
| : request(std::move(storage.request)), cancelled(storage.cancelled) {} |
| ~RemovedRequestStorage() {} |
| |
| std::unique_ptr<OverlayRequest> request; |
| bool cancelled; |
| }; |
| |
| // Returns the request storage for |request|. |
| const RemovedRequestStorage* GetRemovedRequestStorage( |
| OverlayRequest* request) { |
| for (auto& storage : removed_requests_) { |
| if (storage.request.get() == request) |
| return &storage; |
| } |
| return nullptr; |
| } |
| |
| // Storages holding the requests that were removed from the queue being |
| // delegated. Keeps ownership of removed requests and whether they were |
| // cancelled. |
| std::vector<RemovedRequestStorage> removed_requests_; |
| }; |
| // Mock queue observer. |
| class MockOverlayRequestQueueImplObserver |
| : public OverlayRequestQueueImpl::Observer { |
| public: |
| MockOverlayRequestQueueImplObserver() {} |
| ~MockOverlayRequestQueueImplObserver() {} |
| |
| MOCK_METHOD3(RequestAddedToQueue, |
| void(OverlayRequestQueueImpl*, OverlayRequest*, size_t)); |
| |
| void OverlayRequestQueueDestroyed(OverlayRequestQueueImpl* queue) override { |
| queue->RemoveObserver(this); |
| } |
| }; |
| // A cancel handler that never cancels its request. |
| class NoOpCancelHandler : public OverlayRequestCancelHandler { |
| public: |
| NoOpCancelHandler(OverlayRequest* request, OverlayRequestQueue* queue) |
| : OverlayRequestCancelHandler(request, queue) {} |
| ~NoOpCancelHandler() override = default; |
| }; |
| } // namespace |
| |
| // Test fixture for RequestQueueImpl. |
| class OverlayRequestQueueImplTest : public PlatformTest { |
| public: |
| OverlayRequestQueueImplTest() |
| : PlatformTest(), web_state_(std::make_unique<web::FakeWebState>()) { |
| OverlayRequestQueueImpl::Container::CreateForWebState(web_state_.get()); |
| queue()->SetDelegate(&delegate_); |
| queue()->AddObserver(&observer_); |
| } |
| ~OverlayRequestQueueImplTest() override { |
| if (web_state_) { |
| queue()->SetDelegate(nullptr); |
| queue()->RemoveObserver(&observer_); |
| } |
| } |
| |
| OverlayRequestQueueImpl* queue() { |
| // Use the kWebContentArea queue for testing. |
| return OverlayRequestQueueImpl::Container::FromWebState(web_state_.get()) |
| ->QueueForModality(OverlayModality::kWebContentArea); |
| } |
| MockOverlayRequestQueueImplObserver& observer() { return observer_; } |
| |
| OverlayRequest* AddRequest() { |
| std::unique_ptr<OverlayRequest> passed_request = |
| OverlayRequest::CreateWithConfig<FakeOverlayUserData>(); |
| OverlayRequest* request = passed_request.get(); |
| EXPECT_CALL(observer(), |
| RequestAddedToQueue(queue(), request, queue()->size())); |
| queue()->AddRequest(std::move(passed_request)); |
| return request; |
| } |
| |
| protected: |
| FakeOverlayRequestQueueImplDelegate delegate_; |
| MockOverlayRequestQueueImplObserver observer_; |
| std::unique_ptr<web::FakeWebState> web_state_; |
| }; |
| |
| // Tests that state is updated correctly and observer callbacks are received |
| // when adding requests to the back of the queue. |
| TEST_F(OverlayRequestQueueImplTest, AddRequest) { |
| OverlayRequest* first_request = AddRequest(); |
| AddRequest(); |
| |
| EXPECT_EQ(first_request, queue()->front_request()); |
| EXPECT_EQ(2U, queue()->size()); |
| } |
| |
| // Tests that GetRequest() returns the expected values. |
| TEST_F(OverlayRequestQueueImplTest, GetRequest) { |
| OverlayRequest* first_request = AddRequest(); |
| OverlayRequest* second_request = AddRequest(); |
| OverlayRequest* third_request = AddRequest(); |
| |
| // Verify GetRequest() results. |
| EXPECT_EQ(first_request, queue()->GetRequest(/*index=*/0)); |
| EXPECT_EQ(second_request, queue()->GetRequest(/*index=*/1)); |
| EXPECT_EQ(third_request, queue()->GetRequest(/*index=*/2)); |
| |
| // Verify array-syntax accessor results. |
| EXPECT_EQ(first_request, (*queue())[0]); |
| EXPECT_EQ(second_request, (*queue())[1]); |
| EXPECT_EQ(third_request, (*queue())[2]); |
| } |
| |
| // Tests that state is updated correctly and observer callbacks are received |
| // when inserting requests into the middle of the queue. |
| TEST_F(OverlayRequestQueueImplTest, InsertRequest) { |
| AddRequest(); |
| AddRequest(); |
| ASSERT_EQ(2U, queue()->size()); |
| |
| // Insert a request into the middle of the queue. |
| void* kInsertedRequestConfigValue = &kInsertedRequestConfigValue; |
| std::unique_ptr<OverlayRequest> inserted_request = |
| OverlayRequest::CreateWithConfig<FakeOverlayUserData>( |
| kInsertedRequestConfigValue); |
| OverlayRequest* request = inserted_request.get(); |
| EXPECT_CALL(observer(), RequestAddedToQueue(queue(), request, 1)); |
| queue()->InsertRequest(1, std::move(inserted_request)); |
| |
| // Verify that the request is inserted correctly. |
| EXPECT_EQ(3U, queue()->size()); |
| EXPECT_EQ(kInsertedRequestConfigValue, |
| queue()->GetRequest(1)->GetConfig<FakeOverlayUserData>()->value()); |
| } |
| |
| // Tests that PopFrontRequest() correctly updates state, notifies observers, and |
| // transfers ownership of the popped request to the delegate. |
| TEST_F(OverlayRequestQueueImplTest, PopFrontRequest) { |
| // Add two requests to the queue. |
| OverlayRequest* first_request = AddRequest(); |
| OverlayRequest* second_request = AddRequest(); |
| ASSERT_EQ(first_request, queue()->front_request()); |
| ASSERT_EQ(2U, queue()->size()); |
| |
| // Pop the first request and check that the size and front request have been |
| // updated. |
| queue()->PopFrontRequest(); |
| EXPECT_EQ(second_request, queue()->front_request()); |
| EXPECT_EQ(1U, queue()->size()); |
| EXPECT_TRUE(delegate_.WasRequestRemoved(first_request)); |
| EXPECT_FALSE(delegate_.WasRequestCancelled(first_request)); |
| } |
| |
| // Tests that CancelAllRequests() correctly updates state and transfers requests |
| // to the delegate. |
| TEST_F(OverlayRequestQueueImplTest, CancelAllRequests) { |
| // Add two requests to the queue then cancel all requests, verifying that |
| // the observer callback is received for each. |
| OverlayRequest* first_request = AddRequest(); |
| OverlayRequest* second_request = AddRequest(); |
| queue()->CancelAllRequests(); |
| |
| EXPECT_EQ(0U, queue()->size()); |
| EXPECT_TRUE(delegate_.WasRequestCancelled(first_request)); |
| EXPECT_TRUE(delegate_.WasRequestCancelled(second_request)); |
| } |
| |
| // Tests that a cancellation via a cancel handler correctly updates state and |
| // transfers the caoncelled requests to the delegate. |
| TEST_F(OverlayRequestQueueImplTest, CustomCancelHandler) { |
| std::unique_ptr<OverlayRequest> passed_request = |
| OverlayRequest::CreateWithConfig<FakeOverlayUserData>(); |
| OverlayRequest* request = passed_request.get(); |
| std::unique_ptr<FakeOverlayRequestCancelHandler> passed_cancel_handler = |
| std::make_unique<FakeOverlayRequestCancelHandler>(request, queue()); |
| FakeOverlayRequestCancelHandler* cancel_handler = passed_cancel_handler.get(); |
| EXPECT_CALL(observer(), |
| RequestAddedToQueue(queue(), request, queue()->size())); |
| queue()->AddRequest(std::move(passed_request), |
| std::move(passed_cancel_handler)); |
| |
| // Trigger cancellation via the cancel handler, and verify that the request is |
| // correctly removed. |
| cancel_handler->TriggerCancellation(); |
| |
| EXPECT_EQ(0U, queue()->size()); |
| EXPECT_TRUE(delegate_.WasRequestCancelled(request)); |
| } |
| |
| // Tests that the request's WebState is set up when it is added to the queue. |
| TEST_F(OverlayRequestQueueImplTest, WebStateSetup) { |
| std::unique_ptr<OverlayRequest> added_request = |
| OverlayRequest::CreateWithConfig<FakeOverlayUserData>(); |
| OverlayRequest* request = added_request.get(); |
| ASSERT_FALSE(request->GetQueueWebState()); |
| |
| // Add the request and verify that the WebState is set. |
| EXPECT_CALL(observer(), |
| RequestAddedToQueue(queue(), request, queue()->size())); |
| queue()->AddRequest(std::move(added_request)); |
| EXPECT_EQ(web_state_.get(), request->GetQueueWebState()); |
| } |
| |
| // Tests that requests are cancelled upon queue destruction even if their cancel |
| // handlers do not explicitly handle cancellation on WebState destruction. |
| TEST_F(OverlayRequestQueueImplTest, CancellationUponDestruction) { |
| std::unique_ptr<OverlayRequest> passed_request = |
| OverlayRequest::CreateWithConfig<FakeOverlayUserData>(); |
| OverlayRequest* request = passed_request.get(); |
| std::unique_ptr<OverlayRequestCancelHandler> cancel_handler = |
| std::make_unique<NoOpCancelHandler>(passed_request.get(), queue()); |
| EXPECT_CALL(observer(), |
| RequestAddedToQueue(queue(), request, queue()->size())); |
| queue()->AddRequest(std::move(passed_request), std::move(cancel_handler)); |
| |
| // Destroy the OverlayRequestQueue by destroying its owning WebState. |
| web_state_ = nullptr; |
| |
| // Verify that the request was cancelled even though the cancel handler never |
| // executed CancelRequest(). |
| EXPECT_TRUE(delegate_.WasRequestCancelled(request)); |
| } |