s3_upload_manager_test.cpp
Go to the documentation of this file.
1 /*
2  * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License").
5  * You may not use this file except in compliance with the License.
6  * A copy of the License is located at
7  *
8  * http://aws.amazon.com/apache2.0
9  *
10  * or in the "license" file accompanying this file. This file is distributed
11  * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12  * express or implied. See the License for the specific language governing
13  * permissions and limitations under the License.
14  */
15 #include <condition_variable>
16 #include <cstdio>
17 #include <iostream>
18 #include <string>
19 #include <boost/bind.hpp>
20 
21 #include <aws/s3/S3Client.h>
22 #include <gtest/gtest.h>
23 #include <gmock/gmock.h>
24 
26 #include <s3_common/s3_facade.h>
28 
29 using namespace Aws::S3;
30 using ::testing::DoAll;
31 using ::testing::InvokeWithoutArgs;
32 using ::testing::IgnoreResult;
33 using ::testing::Return;
34 using ::testing::_;
35 
36 class MockS3Facade : public S3Facade
37 {
38 public:
39  MockS3Facade(const bool enable_encryption) : S3Facade(enable_encryption) {}
40  MOCK_METHOD3(PutObject, Model::PutObjectOutcome(const std::string &, const std::string &, const std::string &));
41 };
42 
43 class S3UploadManagerTest : public ::testing::Test
44 {
45 protected:
47  std::unique_ptr<MockS3Facade> facade;
48  std::vector<UploadDescription> uploads;
49  std::vector<UploadDescription> completed_uploads;
50  std::size_t num_feedback_calls;
51  Model::PutObjectOutcome successful_outcome;
52  Model::PutObjectOutcome failed_outcome;
53 
54  S3UploadManagerTest() : enable_encryption(false), successful_outcome(Model::PutObjectResult()) {}
55 
56  void SetUp() override
57  {
58  num_feedback_calls = 0;
59  facade = std::make_unique<MockS3Facade>(enable_encryption);
60  uploads =
61  {
62  {"file1", "location1"},
63  {"file2", "location2"}
64  };
65  }
66 
67 public:
68  void FeedbackCallback(const std::vector<UploadDescription>& callback_uploads)
69  {
70  num_feedback_calls++;
71  EXPECT_EQ(callback_uploads.size(), num_feedback_calls);
72  completed_uploads = callback_uploads;
73  }
74 
75  std::future<Model::PutObjectOutcome> UploadFilesUntilUnlocked(std::unique_ptr<S3UploadManager> & manager,
76  std::mutex & lock_during_uploading,
77  std::condition_variable & notify_is_uploading,
78  bool & is_uploading,
79  const boost::function<void(const std::vector<UploadDescription>&)>& callback,
80  int additional_returns = 0) {
81  auto & mock_calls = EXPECT_CALL(*facade, PutObject(_,_,_))
82  .WillOnce(DoAll(
83  InvokeWithoutArgs([&is_uploading, &notify_is_uploading, &lock_during_uploading]() {
84  is_uploading = true;
85  // Notify that the function is entered and blocking
86  notify_is_uploading.notify_all();
87  // Block until the mutex has been unlocked
88  std::unique_lock<std::mutex> lock(lock_during_uploading);
89  }),
90  Return(successful_outcome)));
91  for (int i = 0; i < additional_returns; ++i) {
92  mock_calls.WillOnce(Return(successful_outcome));
93  }
94 
95  manager = std::unique_ptr<S3UploadManager>(new S3UploadManager(std::move(facade)));
96 
97  auto upload = [this, &manager, callback]() {
98  return manager->UploadFiles(this->uploads, "bucket", callback);
99  };
100 
101  return std::async(std::launch::async, upload);
102  }
103 };
104 
105 TEST_F(S3UploadManagerTest, TestClientConfigConstructor)
106 {
107  Aws::Client::ClientConfiguration config;
108  S3UploadManager manager(enable_encryption, config);
109  EXPECT_TRUE(manager.IsAvailable());
110  auto outcome = manager.UploadFiles(uploads, "bucket",
111  [this](const std::vector<UploadDescription>& callback_uploads)
112  {this->FeedbackCallback(callback_uploads);});
113  // This test isn't using mocks so it could fail because of invalid credentials,
114  // not being able to connect to s3 or because the files that are being uploaded don't
115  // exist. The purpose of this test isn't to check the error modes.
116  EXPECT_FALSE(outcome.IsSuccess());
117 }
118 
119 TEST_F(S3UploadManagerTest, TestUploadFilesSuccess)
120 {
121  EXPECT_CALL(*facade,PutObject(_,_,_))
122  .Times(2)
123  .WillRepeatedly(Return(successful_outcome));
124 
125  S3UploadManager manager(std::move(facade));
126  EXPECT_TRUE(manager.IsAvailable());
127  EXPECT_TRUE(completed_uploads.empty());
128  auto outcome = manager.UploadFiles(uploads, "bucket",
129  [this](const std::vector<UploadDescription>& callback_uploads)
130  {this->FeedbackCallback(callback_uploads);});
131  EXPECT_TRUE(outcome.IsSuccess());
132  EXPECT_EQ(uploads.size(), num_feedback_calls);
133  EXPECT_EQ(uploads, completed_uploads);
134  EXPECT_TRUE(manager.IsAvailable());
135 }
136 
137 TEST_F(S3UploadManagerTest, TestUploadFilesFailsPutObjectFails)
138 {
139  // First call succeeds, indicated by having a result.
140  // Second call fails with an arbitrary error type.
141  EXPECT_CALL(*facade,PutObject(_,_,_))
142  .WillOnce(Return(successful_outcome))
143  .WillOnce(Return(failed_outcome));
144  S3UploadManager manager(std::move(facade));
145  EXPECT_TRUE(manager.IsAvailable());
146  auto outcome = manager.UploadFiles(uploads, "bucket",
147  [this](const std::vector<UploadDescription>& callback_uploads)
148  {this->FeedbackCallback(callback_uploads);});
149  EXPECT_FALSE(outcome.IsSuccess());
150  EXPECT_EQ(1u, num_feedback_calls);
151  EXPECT_EQ(1u, completed_uploads.size());
152  EXPECT_EQ(uploads.at(0), completed_uploads.at(0));
153  EXPECT_TRUE(manager.IsAvailable());
154 }
155 
156 TEST_F(S3UploadManagerTest, TestUploadFilesFailsWhileManagerUploading)
157 {
158  std::unique_ptr<S3UploadManager> manager;
159  bool is_uploading = false;
160  // Pause the execution of the facade to simulate waiting for upload to S3
161  std::mutex pause_mutex;
162  // Used to notify main thread that the upload has started
163  std::condition_variable upload_cv;
164 
165  auto feedback_callback = [this](const std::vector<UploadDescription> & callback_uploads) {
166  this->FeedbackCallback(callback_uploads);
167  };
168 
169  // Pause execution of file upload
170  pause_mutex.lock();
171 
172  std::future<Model::PutObjectOutcome> outcome1 = UploadFilesUntilUnlocked(manager, pause_mutex, upload_cv, is_uploading, feedback_callback, 1);
173 
174  // Wait until upload has started so that manager should be busy.
175  {
176  std::mutex cv_mutex;
177  std::unique_lock<std::mutex> lk(cv_mutex);
178  upload_cv.wait(lk, [&is_uploading]() { return is_uploading; });
179  }
180 
181  EXPECT_FALSE(manager->IsAvailable());
182 
183  Model::PutObjectOutcome outcome2 = manager->UploadFiles(uploads, "bucket", feedback_callback);
184 
185  // The manager is busy and should reject the upload request
186  EXPECT_EQ(S3Errors::INVALID_ACTION, outcome2.GetError().GetErrorType());
187  // No files have been uploaded
188  EXPECT_TRUE(completed_uploads.empty());
189 
190  // Finish execution of file upload
191  pause_mutex.unlock();
192 
193  // The first request should continue uninterrupted
194  EXPECT_TRUE(outcome1.get().IsSuccess());
195  EXPECT_EQ(uploads, completed_uploads);
196  EXPECT_EQ(uploads.size(), num_feedback_calls);
197 
198  EXPECT_TRUE(manager->IsAvailable());
199 }
200 
201 TEST_F(S3UploadManagerTest, TestCancelUpload)
202 {
203  std::unique_ptr<S3UploadManager> manager;
204  bool is_uploading = false;
205  // Pause the execution of the facade to simulate waiting for upload to S3
206  std::mutex pause_mutex;
207  // Used to notify main thread that the upload has started
208  std::condition_variable upload_cv;
209 
210  auto feedback_callback = [this](const std::vector<UploadDescription>& callback_uploads) {
211  this->FeedbackCallback(callback_uploads);
212  };
213 
214  // Pause execution of file upload
215  pause_mutex.lock();
216 
217  std::future<Model::PutObjectOutcome> outcome = UploadFilesUntilUnlocked(manager, pause_mutex, upload_cv, is_uploading, feedback_callback);
218 
219  // Wait until upload has started so that manager should be busy.
220  {
221  std::mutex cv_mutex;
222  std::unique_lock<std::mutex> lk(cv_mutex);
223  upload_cv.wait(lk, [&is_uploading]() { return is_uploading; });
224  }
225 
226  EXPECT_FALSE(manager->IsAvailable());
227  // First call to cancel should succeed
228  manager->CancelUpload();
229 
230  // Finish execution of file upload
231  pause_mutex.unlock();
232 
233  // Canceled uploads are marked as successfull
234  EXPECT_TRUE(outcome.get().IsSuccess());
235  EXPECT_EQ(1u, num_feedback_calls);
236  EXPECT_EQ(1u, completed_uploads.size());
237  EXPECT_EQ(uploads.at(0), completed_uploads.at(0));
238 
239  EXPECT_TRUE(manager->IsAvailable());
240 }
virtual bool IsAvailable() const
std::vector< UploadDescription > uploads
std::unique_ptr< MockS3Facade > facade
TEST_F(S3UploadManagerTest, TestClientConfigConstructor)
virtual Model::PutObjectOutcome UploadFiles(const std::vector< UploadDescription > &upload_descriptions, const std::string &bucket, const boost::function< void(const std::vector< UploadDescription > &)> &feedback_callback)
Model::PutObjectOutcome successful_outcome
MockS3Facade(const bool enable_encryption)
std::vector< UploadDescription > completed_uploads
void FeedbackCallback(const std::vector< UploadDescription > &callback_uploads)
std::future< Model::PutObjectOutcome > UploadFilesUntilUnlocked(std::unique_ptr< S3UploadManager > &manager, std::mutex &lock_during_uploading, std::condition_variable &notify_is_uploading, bool &is_uploading, const boost::function< void(const std::vector< UploadDescription > &)> &callback, int additional_returns=0)
Model::PutObjectOutcome failed_outcome


s3_common
Author(s): AWS RoboMaker
autogenerated on Tue Jun 1 2021 02:51:29