Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 // Copyright 2014 The Chromium Authors. All rights reserved. | |
| 2 // Use of this source code is governed by a BSD-style license that can be | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 #include "sync/internal_api/public/attachments/attachment_uploader_impl.h" | |
| 6 | |
| 7 #include "base/bind.h" | |
| 8 #include "base/memory/ref_counted.h" | |
| 9 #include "base/memory/ref_counted_memory.h" | |
| 10 #include "base/message_loop/message_loop.h" | |
| 11 #include "base/strings/stringprintf.h" | |
| 12 #include "base/threading/non_thread_safe.h" | |
| 13 #include "base/threading/thread.h" | |
| 14 #include "net/test/embedded_test_server/embedded_test_server.h" | |
| 15 #include "net/test/embedded_test_server/http_request.h" | |
| 16 #include "net/test/embedded_test_server/http_response.h" | |
| 17 #include "net/url_request/url_request_test_util.h" | |
| 18 #include "sync/api/attachments/attachment.h" | |
| 19 #include "sync/protocol/sync.pb.h" | |
| 20 #include "testing/gtest/include/gtest/gtest.h" | |
| 21 | |
| 22 namespace { | |
| 23 | |
| 24 const char kAttachmentData[] = "some data"; | |
| 25 | |
| 26 } // namespace | |
| 27 | |
| 28 namespace syncer { | |
| 29 | |
| 30 using net::test_server::BasicHttpResponse; | |
| 31 using net::test_server::HttpRequest; | |
| 32 using net::test_server::HttpResponse; | |
| 33 | |
| 34 class AttachmentUploaderImplTest; | |
|
pavely
2014/05/15 23:27:28
Not needed
maniscalco
2014/05/16 16:46:37
Done.
| |
| 35 class RequestHandler; | |
| 36 | |
| 37 // Text fixture for AttachmentUploaderImpl test. | |
| 38 // | |
| 39 // This fixture provides an embedded HTTP server for interacting with | |
| 40 // AttachmentUploaderImpl. | |
| 41 class AttachmentUploaderImplTest : public testing::Test, | |
| 42 public base::NonThreadSafe { | |
| 43 public: | |
| 44 void OnRequestReceived(const HttpRequest& request); | |
| 45 | |
| 46 protected: | |
| 47 AttachmentUploaderImplTest(); | |
| 48 virtual void SetUp(); | |
| 49 | |
| 50 // Run the message loop until UploadDone has been invoked |num_invocations| | |
| 51 // times. | |
| 52 void RunAndQuitAfter(int num_invocations); | |
| 53 | |
| 54 scoped_ptr<AttachmentUploader>& uploader(); | |
| 55 const AttachmentUploader::UploadCallback& upload_callback() const; | |
| 56 std::vector<HttpRequest>& http_requests_received(); | |
| 57 std::vector<AttachmentUploader::UploadResult>& upload_results(); | |
| 58 std::vector<AttachmentId>& updated_attachment_ids(); | |
| 59 | |
| 60 private: | |
| 61 base::MessageLoopForIO message_loop_; | |
| 62 scoped_refptr<net::URLRequestContextGetter> url_request_context_getter_; | |
| 63 scoped_ptr<RequestHandler> request_handler_; | |
| 64 scoped_ptr<AttachmentUploader> uploader_; | |
| 65 AttachmentUploader::UploadCallback upload_callback_; | |
| 66 net::test_server::EmbeddedTestServer server_; | |
| 67 | |
| 68 // Number of times we expect UploadDone to be invoked. | |
| 69 int invocations_remaining_; | |
| 70 std::vector<HttpRequest> http_requests_received_; | |
| 71 std::vector<AttachmentUploader::UploadResult> upload_results_; | |
| 72 std::vector<AttachmentId> updated_attachment_ids_; | |
| 73 | |
| 74 private: | |
| 75 // An UploadCallback invoked by AttachmentUploaderImpl. | |
| 76 void UploadDone(const AttachmentUploader::UploadResult& result, | |
| 77 const AttachmentId& updated_attachment_id); | |
| 78 | |
| 79 // Must be last data member. | |
| 80 base::WeakPtrFactory<AttachmentUploaderImplTest> weak_ptr_factory_; | |
| 81 }; | |
| 82 | |
| 83 // Handles HTTP requests received by the EmbeddedTestServer. | |
| 84 class RequestHandler : public base::NonThreadSafe { | |
|
pavely
2014/05/15 23:27:28
Do you really need RequestHandler as separate clas
maniscalco
2014/05/16 16:46:37
Not strictly necessary, but I found it made it eas
| |
| 85 public: | |
| 86 // Construct a RequestHandler that will PostTask to |test| using | |
| 87 // |test_task_runner|. | |
| 88 RequestHandler( | |
| 89 const scoped_refptr<base::SingleThreadTaskRunner>& test_task_runner, | |
| 90 const base::WeakPtr<AttachmentUploaderImplTest>& test); | |
| 91 | |
| 92 ~RequestHandler(); | |
| 93 | |
| 94 scoped_ptr<HttpResponse> HandleRequest(const HttpRequest& request); | |
| 95 | |
| 96 private: | |
| 97 scoped_refptr<base::SingleThreadTaskRunner> test_task_runner_; | |
| 98 base::WeakPtr<AttachmentUploaderImplTest> test_; | |
| 99 }; | |
| 100 | |
| 101 AttachmentUploaderImplTest::AttachmentUploaderImplTest() | |
| 102 : invocations_remaining_(0), weak_ptr_factory_(this) { | |
| 103 } | |
| 104 | |
| 105 void AttachmentUploaderImplTest::OnRequestReceived(const HttpRequest& request) { | |
| 106 DCHECK(CalledOnValidThread()); | |
| 107 http_requests_received_.push_back(request); | |
| 108 } | |
| 109 | |
| 110 void AttachmentUploaderImplTest::SetUp() { | |
| 111 DCHECK(CalledOnValidThread()); | |
| 112 request_handler_.reset(new RequestHandler(message_loop_.message_loop_proxy(), | |
| 113 weak_ptr_factory_.GetWeakPtr())); | |
| 114 url_request_context_getter_ = | |
| 115 new net::TestURLRequestContextGetter(message_loop_.message_loop_proxy()); | |
| 116 | |
| 117 ASSERT_TRUE(server_.InitializeAndWaitUntilReady()); | |
| 118 server_.RegisterRequestHandler( | |
| 119 base::Bind(&RequestHandler::HandleRequest, | |
| 120 base::Unretained(request_handler_.get()))); | |
| 121 | |
| 122 std::string url_prefix( | |
| 123 base::StringPrintf("http://localhost:%d/uploads/", server_.port())); | |
| 124 | |
| 125 uploader().reset( | |
| 126 new AttachmentUploaderImpl(url_prefix, url_request_context_getter_)); | |
| 127 upload_callback_ = base::Bind(&AttachmentUploaderImplTest::UploadDone, | |
| 128 base::Unretained(this)); | |
| 129 } | |
| 130 | |
| 131 void AttachmentUploaderImplTest::RunAndQuitAfter(int num_invocations) { | |
| 132 invocations_remaining_ = num_invocations; | |
| 133 message_loop_.Run(); | |
|
pavely
2014/05/15 23:27:28
In some previous change Tim asked me not to use Me
maniscalco
2014/05/16 16:46:37
I like this pattern better. Done. Thanks for poi
| |
| 134 } | |
| 135 | |
| 136 scoped_ptr<AttachmentUploader>& AttachmentUploaderImplTest::uploader() { | |
| 137 return uploader_; | |
| 138 } | |
| 139 | |
| 140 const AttachmentUploader::UploadCallback& | |
| 141 AttachmentUploaderImplTest::upload_callback() const { | |
| 142 return upload_callback_; | |
| 143 } | |
| 144 | |
| 145 std::vector<HttpRequest>& AttachmentUploaderImplTest::http_requests_received() { | |
| 146 return http_requests_received_; | |
| 147 } | |
| 148 | |
| 149 std::vector<AttachmentUploader::UploadResult>& | |
| 150 AttachmentUploaderImplTest::upload_results() { | |
| 151 return upload_results_; | |
| 152 } | |
| 153 | |
| 154 std::vector<AttachmentId>& | |
| 155 AttachmentUploaderImplTest::updated_attachment_ids() { | |
| 156 return updated_attachment_ids_; | |
| 157 } | |
| 158 | |
| 159 void AttachmentUploaderImplTest::UploadDone( | |
| 160 const AttachmentUploader::UploadResult& result, | |
| 161 const AttachmentId& updated_attachment_id) { | |
| 162 DCHECK(CalledOnValidThread()); | |
| 163 upload_results_.push_back(result); | |
| 164 updated_attachment_ids_.push_back(updated_attachment_id); | |
| 165 --invocations_remaining_; | |
| 166 if (invocations_remaining_ == 0) { | |
| 167 base::MessageLoop::current()->Quit(); | |
| 168 } | |
| 169 } | |
| 170 | |
| 171 RequestHandler::RequestHandler( | |
| 172 const scoped_refptr<base::SingleThreadTaskRunner>& test_task_runner, | |
| 173 const base::WeakPtr<AttachmentUploaderImplTest>& test) | |
| 174 : test_task_runner_(test_task_runner), test_(test) { | |
| 175 DetachFromThread(); | |
| 176 } | |
| 177 | |
| 178 RequestHandler::~RequestHandler() { | |
| 179 DetachFromThread(); | |
| 180 } | |
| 181 | |
| 182 scoped_ptr<HttpResponse> RequestHandler::HandleRequest( | |
| 183 const HttpRequest& request) { | |
| 184 DCHECK(CalledOnValidThread()); | |
| 185 test_task_runner_->PostTask( | |
| 186 FROM_HERE, | |
| 187 base::Bind( | |
| 188 &AttachmentUploaderImplTest::OnRequestReceived, test_, request)); | |
| 189 scoped_ptr<BasicHttpResponse> http_response(new BasicHttpResponse); | |
| 190 http_response->set_code(net::HTTP_OK); | |
| 191 http_response->set_content("hello"); | |
| 192 http_response->set_content_type("text/plain"); | |
| 193 return http_response.PassAs<HttpResponse>(); | |
| 194 } | |
| 195 | |
| 196 // Verify the "happy case" of uploading an attachment. | |
| 197 TEST_F(AttachmentUploaderImplTest, UploadAttachment_HappyCase) { | |
| 198 scoped_refptr<base::RefCountedString> some_data(new base::RefCountedString); | |
| 199 some_data->data() = kAttachmentData; | |
| 200 Attachment attachment = Attachment::Create(some_data); | |
| 201 uploader()->UploadAttachment(attachment, upload_callback()); | |
| 202 RunAndQuitAfter(1); | |
| 203 | |
| 204 // See that the HTTP server received one request. | |
| 205 EXPECT_EQ(1U, http_requests_received().size()); | |
| 206 const HttpRequest& http_request = http_requests_received().front(); | |
| 207 EXPECT_EQ(net::test_server::METHOD_POST, http_request.method); | |
| 208 std::string expected_relative_url("/uploads/" + | |
| 209 attachment.GetId().GetProto().unique_id()); | |
| 210 EXPECT_EQ(expected_relative_url, http_request.relative_url); | |
| 211 EXPECT_TRUE(http_request.has_content); | |
| 212 EXPECT_EQ(kAttachmentData, http_request.content); | |
| 213 | |
| 214 // See that the UploadCallback received a result and updated AttachmentId. | |
| 215 EXPECT_EQ(1U, upload_results().size()); | |
| 216 EXPECT_EQ(1U, updated_attachment_ids().size()); | |
| 217 EXPECT_EQ(AttachmentUploader::UPLOAD_SUCCESS, upload_results().front()); | |
| 218 EXPECT_EQ(attachment.GetId(), updated_attachment_ids().front()); | |
| 219 // TODO(maniscalco): Once AttachmentUploaderImpl is capable of updating the | |
| 220 // AttachmentId with server address information about the attachment, add some | |
| 221 // checks here to verify it works properly (bug 371522). | |
| 222 } | |
| 223 | |
| 224 // Verify two overlapping calls to upload the same attachment result in only one | |
| 225 // HTTP request. | |
| 226 TEST_F(AttachmentUploaderImplTest, UploadAttachment_Collapse) { | |
| 227 scoped_refptr<base::RefCountedString> some_data(new base::RefCountedString); | |
| 228 some_data->data() = kAttachmentData; | |
| 229 Attachment attachment1 = Attachment::Create(some_data); | |
| 230 Attachment attachment2 = attachment1; | |
| 231 uploader()->UploadAttachment(attachment1, upload_callback()); | |
| 232 uploader()->UploadAttachment(attachment2, upload_callback()); | |
| 233 // Wait for upload_callback() to be invoked twice. | |
| 234 RunAndQuitAfter(2); | |
| 235 // See there was only one request. | |
| 236 EXPECT_EQ(1U, http_requests_received().size()); | |
| 237 } | |
| 238 | |
| 239 // Verify that the internal state associated with an upload is removed when the | |
| 240 // uplaod finishes. We do this by issuing two non-overlapping uploads for the | |
| 241 // same attachment and see that it results in two HTTP requests. | |
| 242 TEST_F(AttachmentUploaderImplTest, UploadAttachment_CleanUpAfterUpload) { | |
| 243 scoped_refptr<base::RefCountedString> some_data(new base::RefCountedString); | |
| 244 some_data->data() = kAttachmentData; | |
| 245 Attachment attachment1 = Attachment::Create(some_data); | |
| 246 Attachment attachment2 = attachment1; | |
| 247 uploader()->UploadAttachment(attachment1, upload_callback()); | |
| 248 // Wait for upload_callback() to be invoked before starting the second upload. | |
| 249 RunAndQuitAfter(1); | |
| 250 uploader()->UploadAttachment(attachment2, upload_callback()); | |
| 251 // Wait for upload_callback() to be invoked a second time. | |
| 252 RunAndQuitAfter(1); | |
| 253 // See there were two requests. | |
| 254 EXPECT_EQ(2U, http_requests_received().size()); | |
| 255 } | |
| 256 | |
| 257 } // namespace syncer | |
| OLD | NEW |