Skip to content

Commit b64375a

Browse files
authored
improvements for storage integration tests (#2437)
1 parent 94ddf60 commit b64375a

File tree

9 files changed

+325
-297
lines changed

9 files changed

+325
-297
lines changed

AmplifyAsyncTesting/Sources/AsyncTesting/XCTestCase+AsyncTesting.swift

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@ import Foundation
99
import XCTest
1010

1111
extension XCTestCase {
12-
13-
static let defaultNetworkTimeoutForAsyncExpectations = TimeInterval(10)
12+
public static let defaultTimeoutForAsyncExpectations = TimeInterval(60)
13+
public static let defaultNetworkTimeoutForAsyncExpectations = TimeInterval(10)
1414

1515
/// Creates a new async expectation with an associated description.
1616
///
@@ -48,6 +48,29 @@ extension XCTestCase {
4848
line: line)
4949
}
5050

51+
/// Run a task with a timeout using an `AsyncExpectation`.
52+
/// - Parameters:
53+
/// - timeout: timeout
54+
/// - operation: operation to run
55+
/// - Returns: result of closure
56+
@discardableResult
57+
public func testTask<Success>(timeout: Double = defaultTimeoutForAsyncExpectations,
58+
file: StaticString = #filePath,
59+
line: UInt = #line,
60+
@_implicitSelfCapture operation: @escaping @Sendable () async throws -> Success) async throws -> Success {
61+
let done = asyncExpectation(description: "done")
62+
63+
let task = Task {
64+
let result = try await operation()
65+
await done.fulfill()
66+
return result
67+
}
68+
69+
await waitForExpectations([done], timeout: timeout, file: file, line: line)
70+
71+
return try await task.value
72+
}
73+
5174
/// Waits for the execution of a given async code, using a given async expectation,
5275
/// and returns its result or `nil` if it threw an error.
5376
///

AmplifyPlugins/Storage/Sources/AWSS3StoragePlugin/Service/Storage/AWSS3StorageService.swift

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -130,11 +130,12 @@ class AWSS3StorageService: AWSS3StorageServiceBehaviour, StorageServiceProxy {
130130

131131
func reset() {
132132
authService = nil
133-
logger = nil
134133
preSignedURLBuilder = nil
135134
awsS3 = nil
136135
region = nil
137136
bucket = nil
137+
tasks.removeAll()
138+
multipartUploadSessions.removeAll()
138139
}
139140

140141
func resetURLSession() {
@@ -167,20 +168,23 @@ class AWSS3StorageService: AWSS3StorageServiceBehaviour, StorageServiceProxy {
167168
}
168169

169170
func register(task: StorageTransferTask) {
171+
dispatchPrecondition(condition: .notOnQueue(serviceDispatchQueue))
170172
serviceDispatchQueue.sync {
171173
guard let taskIdentifier = task.taskIdentifier else { return }
172174
tasks[taskIdentifier] = task
173175
}
174176
}
175177

176178
func unregister(task: StorageTransferTask) {
179+
dispatchPrecondition(condition: .notOnQueue(serviceDispatchQueue))
177180
serviceDispatchQueue.sync {
178181
guard let taskIdentifier = task.taskIdentifier else { return }
179182
tasks[taskIdentifier] = nil
180183
}
181184
}
182185

183186
func unregister(taskIdentifiers: [TaskIdentifier]) {
187+
dispatchPrecondition(condition: .notOnQueue(serviceDispatchQueue))
184188
serviceDispatchQueue.sync {
185189
for taskIdentifier in taskIdentifiers {
186190
tasks[taskIdentifier] = nil
@@ -206,8 +210,11 @@ class AWSS3StorageService: AWSS3StorageServiceBehaviour, StorageServiceProxy {
206210
}
207211

208212
func findTask(taskIdentifier: TaskIdentifier) -> StorageTransferTask? {
209-
let task = tasks[taskIdentifier]
210-
return task
213+
dispatchPrecondition(condition: .notOnQueue(serviceDispatchQueue))
214+
return serviceDispatchQueue.sync {
215+
let task = tasks[taskIdentifier]
216+
return task
217+
}
211218
}
212219

213220
func findMultipartUploadSession(uploadId: UploadID) -> StorageMultipartUploadSession? {

AmplifyPlugins/Storage/Sources/AWSS3StoragePlugin/Support/Internal/StorageMultipartUpload.swift

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,14 @@ enum StorageMultipartUpload {
128128
}
129129
}
130130

131+
var isAborting: Bool {
132+
if case .aborting = self {
133+
return true
134+
} else {
135+
return false
136+
}
137+
}
138+
131139
var isAborted: Bool {
132140
if case .aborted = self {
133141
return true
@@ -221,14 +229,14 @@ enum StorageMultipartUpload {
221229
case .parts(let uploadId, let uploadFile, let partSize, let parts):
222230
self = .paused(uploadId: uploadId, uploadFile: uploadFile, partSize: partSize, parts: parts)
223231
default:
224-
throw Failure.invalidStateTransition(reason: "Cannot abort from current state: \(self)")
232+
throw Failure.invalidStateTransition(reason: "Cannot pause from current state: \(self)")
225233
}
226234
case .resuming:
227235
switch self {
228236
case .paused(let uploadId, let uploadFile, let partSize, let parts):
229237
self = .parts(uploadId: uploadId, uploadFile: uploadFile, partSize: partSize, parts: parts)
230238
default:
231-
throw Failure.invalidStateTransition(reason: "Cannot abort from current state: \(self)")
239+
throw Failure.invalidStateTransition(reason: "Cannot resume from current state: \(self)")
232240
}
233241
break
234242
case .completing(let taskIdentifier):
@@ -267,6 +275,7 @@ enum StorageMultipartUpload {
267275
// swiftlint:enable cyclomatic_complexity
268276

269277
mutating func transition(uploadPartEvent: StorageUploadPartEvent) throws {
278+
guard !isAborting, !isAborted else { return }
270279
guard case .parts(let uploadId, let uploadFile, let partSize, var parts) = self else {
271280
throw Failure.invalidStateTransition(reason: "Parts are required for this transition: \(uploadPartEvent)")
272281
}

AmplifyPlugins/Storage/Sources/AWSS3StoragePlugin/Support/Internal/StorageMultipartUploadSession.swift

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,10 @@ class StorageMultipartUploadSession {
130130
}
131131

132132
var uploadId: UploadID? {
133-
multipartUpload.uploadId
133+
dispatchPrecondition(condition: .notOnQueue(serialQueue))
134+
return serialQueue.sync {
135+
multipartUpload.uploadId
136+
}
134137
}
135138

136139
var completedParts: StorageUploadParts? {

AmplifyPlugins/Storage/Tests/StorageHostApp/AWSS3StoragePluginIntegrationTests/AWSS3StoragePluginAccessLevelTests.swift

Lines changed: 15 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -21,33 +21,23 @@ class AWSS3StoragePluginAccessLevelTests: AWSS3StoragePluginTestBase {
2121
/// Given: An unauthenticated user
2222
/// When: List API with protected access level
2323
/// Then: Operation completes successfully with no items since there are no keys at that location.
24-
func testListFromProtectedForUnauthenticatedUser() async {
25-
let done = asyncExpectation(description: "done")
26-
27-
Task {
28-
do {
29-
let key = UUID().uuidString
30-
let options = StorageListRequest.Options(accessLevel: .protected,
31-
path: key)
32-
let items = try await Amplify.Storage.list(options: options).items
33-
XCTAssertEqual(items.count, 0)
34-
} catch {
35-
XCTFail("Error: \(error)")
36-
}
37-
await done.fulfill()
24+
func testListFromProtectedForUnauthenticatedUser() async throws {
25+
try await testTask {
26+
let key = UUID().uuidString
27+
let options = StorageListRequest.Options(accessLevel: .protected,
28+
path: key)
29+
let items = try await Amplify.Storage.list(options: options).items
30+
XCTAssertEqual(items.count, 0)
3831
}
39-
40-
await waitForExpectations([done], timeout: TestCommonConstants.networkTimeout)
4132
}
4233

4334
/// Given: An unauthenticated user
4435
/// When: List API with private access level
4536
/// Then: Operation fails with access denied service error
46-
func testListFromPrivateForUnauthenticatedUserForReturnAccessDenied() async {
47-
let done = asyncExpectation(description: "done")
48-
let notDone = asyncExpectation(description: "not done", isInverted: true)
37+
func testListFromPrivateForUnauthenticatedUserForReturnAccessDenied() async throws {
38+
try await testTask {
39+
let notDone = asyncExpectation(description: "not done", isInverted: true)
4940

50-
Task {
5141
do {
5242
let key = UUID().uuidString
5343
let options = StorageListRequest.Options(accessLevel: .private,
@@ -62,11 +52,9 @@ class AWSS3StoragePluginAccessLevelTests: AWSS3StoragePluginTestBase {
6252
}
6353
XCTAssertEqual(description, StorageErrorConstants.accessDenied.errorDescription)
6454
}
65-
await done.fulfill()
66-
}
6755

68-
await waitForExpectations([notDone])
69-
await waitForExpectations([done], timeout: TestCommonConstants.networkTimeout)
56+
await waitForExpectations([notDone])
57+
}
7058
}
7159

7260
func testUploadAndRemoveForGuestOnly() async throws {
@@ -251,7 +239,7 @@ class AWSS3StoragePluginAccessLevelTests: AWSS3StoragePluginTestBase {
251239
await signOut()
252240

253241
logger.debug("Signing in user1")
254-
let user1SignedIn = try await signIn(username: AWSS3StoragePluginTestBase.user1)
242+
let user1SignedIn = try await Amplify.Auth.signIn(username: AWSS3StoragePluginTestBase.user1, password: AWSS3StoragePluginTestBase.password).isSignedIn
255243
XCTAssertTrue(user1SignedIn)
256244

257245
logger.debug("Getting identity for user1")
@@ -274,7 +262,7 @@ class AWSS3StoragePluginAccessLevelTests: AWSS3StoragePluginTestBase {
274262
await signOut()
275263

276264
logger.debug("Signing in as user2")
277-
let user2SignedIn = try await signIn(username: AWSS3StoragePluginTestBase.user2)
265+
let user2SignedIn = try await Amplify.Auth.signIn(username: AWSS3StoragePluginTestBase.user2, password: AWSS3StoragePluginTestBase.password).isSignedIn
278266
XCTAssertTrue(user2SignedIn)
279267

280268
logger.debug("Getting identity for user2")
@@ -313,7 +301,7 @@ class AWSS3StoragePluginAccessLevelTests: AWSS3StoragePluginTestBase {
313301
await signOut()
314302

315303
logger.debug("Signing in user1")
316-
let user1SignedIn = try await signIn(username: AWSS3StoragePluginTestBase.user1)
304+
let user1SignedIn = try await Amplify.Auth.signIn(username: AWSS3StoragePluginTestBase.user1, password: AWSS3StoragePluginTestBase.password).isSignedIn
317305
XCTAssertTrue(user1SignedIn)
318306

319307
logger.debug("Removing key as user1")
@@ -351,10 +339,6 @@ class AWSS3StoragePluginAccessLevelTests: AWSS3StoragePluginTestBase {
351339

352340
// MARK: - Auth Helpers -
353341

354-
func signIn(username: String) async throws -> Bool {
355-
try await Amplify.Auth.signIn(username: username, password: AWSS3StoragePluginTestBase.password).isSignedIn
356-
}
357-
358342
func getIdentityId() async throws -> String? {
359343
guard let session = try await Amplify.Auth.fetchAuthSession() as? AuthCognitoIdentityProvider else {
360344
throw AuthError.unknown("Could not get session", nil)

AmplifyPlugins/Storage/Tests/StorageHostApp/AWSS3StoragePluginIntegrationTests/AWSS3StoragePluginTestBase.swift

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,8 @@ class AWSS3StoragePluginTestBase: XCTestCase {
1515
static let logger = Amplify.Logging.logger(forCategory: "Storage", logLevel: .verbose)
1616
static let amplifyConfiguration = "testconfiguration/AWSS3StoragePluginTests-amplifyconfiguration"
1717

18-
static let largeDataObject = Data(repeating: 0xff, count: 1_024 * 1_024 * 6) // 6MB
18+
static let smallDataObject = Data(repeating: 0xff, count: 1_024 * 1_024 * ProcessInfo.processInfo.activeProcessorCount)
19+
static let largeDataObject = Data(repeating: 0xff, count: 1_024 * 1_024 * ProcessInfo.processInfo.activeProcessorCount * 4)
1920

2021
static var user1: String = "integTest\(UUID().uuidString)"
2122
static var user2: String = "integTest\(UUID().uuidString)"

AmplifyPlugins/Storage/Tests/StorageHostApp/AWSS3StoragePluginIntegrationTests/Helpers/TestConfigHelper.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,5 +39,5 @@ class TestConfigHelper {
3939
}
4040

4141
class TestCommonConstants {
42-
static let networkTimeout = TimeInterval(10)
42+
static let networkTimeout = TimeInterval(20)
4343
}

0 commit comments

Comments
 (0)