Skip to content

Commit 304679f

Browse files
raphkimKarthikeyan
authored andcommitted
[TransferUtility] Added integration test for uploading in unreliable network (#1214)
1 parent a69ce72 commit 304679f

File tree

3 files changed

+260
-24
lines changed

3 files changed

+260
-24
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,242 @@
1+
/*
2+
* Copyright 2018-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+
* You may obtain a copy of the License at:
7+
*
8+
* http://aws.amazon.com/apache2.0
9+
*
10+
* This file is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES
11+
* OR CONDITIONS OF ANY KIND, either express or implied. See the
12+
* License for the specific language governing permissions and
13+
* limitations under the License.
14+
*/
15+
16+
package com.amazonaws.mobileconnectors.s3.transferutility;
17+
18+
import android.content.Context;
19+
import android.support.test.InstrumentationRegistry;
20+
21+
import com.amazonaws.AmazonClientException;
22+
import com.amazonaws.AmazonServiceException;
23+
import com.amazonaws.auth.AWSCredentials;
24+
import com.amazonaws.regions.Region;
25+
import com.amazonaws.regions.Regions;
26+
import com.amazonaws.services.s3.AmazonS3Client;
27+
import com.amazonaws.services.s3.S3IntegrationTestBase;
28+
import com.amazonaws.services.s3.model.PutObjectRequest;
29+
import com.amazonaws.services.s3.model.PutObjectResult;
30+
import com.amazonaws.services.s3.model.UploadPartRequest;
31+
import com.amazonaws.services.s3.model.UploadPartResult;
32+
33+
import org.junit.AfterClass;
34+
import org.junit.BeforeClass;
35+
import org.junit.Test;
36+
37+
import java.io.File;
38+
import java.net.SocketTimeoutException;
39+
import java.util.Date;
40+
import java.util.concurrent.CountDownLatch;
41+
import java.util.concurrent.TimeUnit;
42+
43+
import static org.junit.Assert.assertEquals;
44+
import static org.junit.Assert.fail;
45+
46+
/**
47+
* Mock S3 client that throws SocketTimeoutException
48+
* to trigger an upload failure
49+
*/
50+
class SocketTimeoutMockS3Client extends AmazonS3Client {
51+
private boolean transferEnabled;
52+
53+
public SocketTimeoutMockS3Client(AWSCredentials credentials, Region region) {
54+
super(credentials, region);
55+
transferEnabled = false;
56+
}
57+
58+
public void setTransferEnabled(boolean enable) {
59+
transferEnabled = enable;
60+
}
61+
62+
@Override
63+
public PutObjectResult putObject(PutObjectRequest putObjectRequest)
64+
throws AmazonClientException, AmazonServiceException {
65+
if (transferEnabled) {
66+
return super.putObject(putObjectRequest);
67+
} else {
68+
throw new AmazonClientException(new SocketTimeoutException());
69+
}
70+
}
71+
72+
@Override
73+
public UploadPartResult uploadPart(UploadPartRequest uploadPartRequest)
74+
throws AmazonClientException, AmazonServiceException {
75+
if (transferEnabled) {
76+
return super.uploadPart(uploadPartRequest);
77+
} else {
78+
throw new AmazonClientException(new SocketTimeoutException());
79+
}
80+
}
81+
}
82+
83+
public class SocketTimeoutIntegrationTest extends S3IntegrationTestBase {
84+
/** The bucket created and used by these tests */
85+
private static final String bucketName = "amazon-transfer-util-integ-test-" + new Date().getTime();
86+
87+
/** Instrumentation test context */
88+
private static Context context = InstrumentationRegistry.getContext();
89+
90+
/** Mock S3 client to artificially throw socket timeout exception */
91+
private static SocketTimeoutMockS3Client mockS3;
92+
93+
/** The transfer utility used to upload to S3 */
94+
private static TransferUtility util;
95+
96+
/** Countdown latch for testing */
97+
private static CountDownLatch latch;
98+
99+
/** The file containing the test data uploaded to S3 */
100+
private static File file = null;
101+
102+
/**
103+
* Creates and initializes all the test resources needed for these tests.
104+
*/
105+
@BeforeClass
106+
public static void setUpBeforeClass() throws Exception {
107+
setUp();
108+
mockS3 = new SocketTimeoutMockS3Client(credentials,
109+
Region.getRegion(Regions.US_WEST_1));
110+
util = TransferUtility.builder()
111+
.context(context)
112+
.s3Client(mockS3)
113+
.build();
114+
115+
try {
116+
s3.createBucket(bucketName);
117+
waitForBucketCreation(bucketName);
118+
} catch (final Exception e) {
119+
System.out.println("Error in creating the bucket. "
120+
+ "Please manually create the bucket " + bucketName);
121+
}
122+
}
123+
124+
@AfterClass
125+
public static void tearDown() {
126+
try {
127+
deleteBucketAndAllContents(bucketName);
128+
} catch (final Exception e) {
129+
System.out.println("Error in deleting the bucket. "
130+
+ "Please manually delete the bucket " + bucketName);
131+
e.printStackTrace();
132+
}
133+
134+
if (file != null) {
135+
file.delete();
136+
}
137+
}
138+
139+
@Test
140+
public void testSinglePartUploadTimeout() throws Exception {
141+
TransferObserver observer;
142+
143+
// Small (1KB) file upload
144+
file = getRandomTempFile("small", 1000L);
145+
146+
// First attempt at uploading should throw socket timeout exception error
147+
latch = new CountDownLatch(1);
148+
mockS3.setTransferEnabled(false);
149+
observer = util.upload(bucketName, file.getName(), file);
150+
observer.setTransferListener(new TransferListener() {
151+
@Override
152+
public void onProgressChanged(int id, long bytesCurrent, long bytesTotal) {
153+
}
154+
155+
@Override
156+
public void onStateChanged(int id, TransferState state) {
157+
if (state.equals(TransferState.FAILED)) { latch.countDown(); }
158+
}
159+
160+
@Override
161+
public void onError(int id, Exception ex) {
162+
}
163+
});
164+
latch.await(300, TimeUnit.SECONDS);
165+
assertEquals(TransferState.FAILED, observer.getState());
166+
167+
// Attempt to resume should complete upload as intended
168+
latch = new CountDownLatch(1);
169+
mockS3.setTransferEnabled(true);
170+
observer = util.resume(observer.getId());
171+
observer.setTransferListener(new TransferListener() {
172+
@Override
173+
public void onProgressChanged(int id, long bytesCurrent, long bytesTotal) {
174+
}
175+
176+
@Override
177+
public void onStateChanged(int id, TransferState state) {
178+
if (state.equals(TransferState.COMPLETED)) { latch.countDown(); }
179+
}
180+
181+
@Override
182+
public void onError(int id, Exception ex) {
183+
fail(ex.getMessage());
184+
}
185+
});
186+
latch.await(300, TimeUnit.SECONDS);
187+
assertEquals(TransferState.COMPLETED, observer.getState());
188+
}
189+
190+
@Test
191+
public void testMultiPartUploadTimeout() throws Exception {
192+
TransferObserver observer;
193+
194+
// Large (5MB) file upload
195+
long size = 20 * 1024 * 1024 + 1;
196+
file = getRandomSparseFile("large", size);
197+
198+
// First attempt at uploading should throw socket timeout exception error
199+
latch = new CountDownLatch(1);
200+
mockS3.setTransferEnabled(false);
201+
observer = util.upload(bucketName, file.getName(), file);
202+
observer.setTransferListener(new TransferListener() {
203+
@Override
204+
public void onProgressChanged(int id, long bytesCurrent, long bytesTotal) {
205+
}
206+
207+
@Override
208+
public void onStateChanged(int id, TransferState state) {
209+
if (state.equals(TransferState.FAILED)) { latch.countDown(); }
210+
}
211+
212+
@Override
213+
public void onError(int id, Exception ex) {
214+
}
215+
});
216+
latch.await(300, TimeUnit.SECONDS);
217+
assertEquals(TransferState.FAILED, observer.getState());
218+
219+
// Attempt to resume should complete upload as intended
220+
latch = new CountDownLatch(1);
221+
mockS3.setTransferEnabled(true);
222+
observer = util.resume(observer.getId());
223+
observer.setTransferListener(new TransferListener() {
224+
@Override
225+
public void onProgressChanged(int id, long bytesCurrent, long bytesTotal) {
226+
}
227+
228+
@Override
229+
public void onStateChanged(int id, TransferState state) {
230+
if (state.equals(TransferState.COMPLETED)) { latch.countDown(); }
231+
}
232+
233+
@Override
234+
public void onError(int id, Exception ex) {
235+
fail(ex.getMessage());
236+
}
237+
});
238+
latch.await(300, TimeUnit.SECONDS);
239+
assertEquals(TransferState.COMPLETED, observer.getState());
240+
}
241+
}
242+
Lines changed: 5 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -13,15 +13,12 @@
1313
* limitations under the License.
1414
*/
1515

16-
package com.amazonaws.services.s3;
16+
package com.amazonaws.mobileconnectors.s3.transferutility;
1717

1818
import android.content.Context;
1919
import android.support.test.InstrumentationRegistry;
2020

21-
import com.amazonaws.mobileconnectors.s3.transferutility.TransferListener;
22-
import com.amazonaws.mobileconnectors.s3.transferutility.TransferNetworkLossHandler;
23-
import com.amazonaws.mobileconnectors.s3.transferutility.TransferState;
24-
import com.amazonaws.mobileconnectors.s3.transferutility.TransferUtility;
21+
import com.amazonaws.services.s3.S3IntegrationTestBase;
2522
import com.amazonaws.services.s3.model.GetObjectTaggingRequest;
2623
import com.amazonaws.services.s3.model.ObjectMetadata;
2724
import com.amazonaws.services.s3.model.Tag;
@@ -31,7 +28,6 @@
3128
import org.junit.Test;
3229

3330
import java.io.File;
34-
import java.io.RandomAccessFile;
3531
import java.util.Date;
3632
import java.util.List;
3733
import java.util.concurrent.CountDownLatch;
@@ -42,7 +38,7 @@
4238
import static org.junit.Assert.assertThat;
4339
import static org.junit.Assert.fail;
4440

45-
public class TransferUtilityIntegrationTest extends S3IntegrationTestBase {
41+
public class TaggingIntegrationTest extends S3IntegrationTestBase {
4642

4743
/** The bucket created and used by these tests */
4844
private static final String bucketName = "amazon-transfer-util-integ-test-" + new Date().getTime();
@@ -83,7 +79,6 @@ public void onError(int id, Exception ex) {
8379
@BeforeClass
8480
public static void setUpBeforeClass() throws Exception {
8581
setUp();
86-
TransferNetworkLossHandler.getInstance(context);
8782
util = TransferUtility.builder()
8883
.context(context)
8984
.s3Client(s3)
@@ -114,7 +109,7 @@ public static void tearDown() {
114109
}
115110

116111
@Test
117-
public void testSingleUploadTagging() throws Exception {
112+
public void testSinglePartUploadTagging() throws Exception {
118113
// Object metadata to add
119114
metadata = new ObjectMetadata();
120115
metadata.addUserMetadata("x-amz-tagging", "key1=value1&key2=value2");
@@ -139,7 +134,7 @@ public void testSingleUploadTagging() throws Exception {
139134
}
140135

141136
@Test
142-
public void testMultiUploadTagging() throws Exception {
137+
public void testMultiPartUploadTagging() throws Exception {
143138
// Object metadata to add
144139
metadata = new ObjectMetadata();
145140
metadata.addUserMetadata("x-amz-tagging", "key1=value1&key2=value2");
@@ -163,17 +158,4 @@ public void testMultiUploadTagging() throws Exception {
163158
new Tag("key2", "value2")
164159
));
165160
}
166-
167-
/*
168-
* Helper method to create a sparse file (Faster than writing a file with random bytes with getRandomTempFile)
169-
* Use for testing large uploads
170-
*/
171-
private File getRandomSparseFile(String filename, long contentLength) throws Exception {
172-
// Use the same storage path as getRandomTempFile()
173-
String pathPrefix = System.getProperty("java.io.tmpdir") + File.separator + System.currentTimeMillis() + "-";
174-
File tempFile = new File(pathPrefix + filename);
175-
RandomAccessFile raf = new RandomAccessFile(tempFile, "rw"); // Create a new random access file with read/write access
176-
raf.setLength(contentLength);
177-
return tempFile;
178-
}
179161
}

aws-android-sdk-s3-test/src/androidTest/java/com/amazonaws/services/s3/S3IntegrationTestBase.java

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@
4747
import org.junit.BeforeClass;
4848

4949
import java.io.File;
50+
import java.io.RandomAccessFile;
5051
import java.util.HashSet;
5152
import java.util.Random;
5253
import java.util.Set;
@@ -100,6 +101,17 @@ protected static File getRandomTempFile(String filename, long contentLength) thr
100101
}
101102
}
102103

104+
/*
105+
* Helper method to create a sparse file (Faster than writing a file with random bytes with getRandomTempFile)
106+
* Use for testing large uploads
107+
*/
108+
protected static File getRandomSparseFile(String filename, long contentLength) throws Exception {
109+
File tempFile = getRandomTempFile(filename, 0);
110+
RandomAccessFile raf = new RandomAccessFile(tempFile, "rw"); // Create a new random access file with read/write access
111+
raf.setLength(contentLength);
112+
return tempFile;
113+
}
114+
103115
/**
104116
* Returns true if the specified ACL contains a grant for the specified
105117
* group grantee and permission.
@@ -321,7 +333,7 @@ protected Set<Grant> translateEmailAclsIntoCanonical(AccessControlList acl) {
321333
* waiting a specific bucket created When exceed the poll time, will throw
322334
* Max poll time exceeded exception
323335
*/
324-
static void waitForBucketCreation(String bucketName) throws Exception {
336+
protected static void waitForBucketCreation(String bucketName) throws Exception {
325337
final long startTime = System.currentTimeMillis();
326338
final long endTime = startTime + (30 * 60 * 1000);
327339
int hits = 0;

0 commit comments

Comments
 (0)