summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJoel Fischer <joeljfischer@gmail.com>2021-09-27 10:05:32 -0400
committerGitHub <noreply@github.com>2021-09-27 10:05:32 -0400
commitba251e2cd09ffe478f7eab0c861ca58ccd98981e (patch)
tree80771e1e90a14446134a224d5a7319350a2146e7
parent6eaf543e6b6ecea478bd760be9cbf37564398681 (diff)
parente5683a078e9d120303dc331fec890fc38b929247 (diff)
downloadsdl_ios-ba251e2cd09ffe478f7eab0c861ca58ccd98981e.tar.gz
Merge pull request #2040 from smartdevicelink/bugfix/issue-2034-file-manager-multiple-uploads
Fix file operations check file system at queue time instead of run time
-rw-r--r--SmartDeviceLink-iOS.xcodeproj/project.pbxproj5
-rw-r--r--SmartDeviceLink/private/SDLDeleteFileOperation.h40
-rw-r--r--SmartDeviceLink/private/SDLDeleteFileOperation.m25
-rw-r--r--SmartDeviceLink/private/SDLError.m2
-rw-r--r--SmartDeviceLink/private/SDLUploadFileOperation.h14
-rw-r--r--SmartDeviceLink/private/SDLUploadFileOperation.m89
-rw-r--r--SmartDeviceLink/public/SDLFile.m23
-rw-r--r--SmartDeviceLink/public/SDLFileManager.m54
-rw-r--r--SmartDeviceLinkTests/DevAPISpecs/SDLDeleteFileOperationSpec.m122
-rw-r--r--SmartDeviceLinkTests/DevAPISpecs/SDLFileManagerSpec.m133
-rw-r--r--SmartDeviceLinkTests/DevAPISpecs/SDLUploadFileOperationSpec.m199
11 files changed, 399 insertions, 307 deletions
diff --git a/SmartDeviceLink-iOS.xcodeproj/project.pbxproj b/SmartDeviceLink-iOS.xcodeproj/project.pbxproj
index 4fe6cebb4..1dcef80c6 100644
--- a/SmartDeviceLink-iOS.xcodeproj/project.pbxproj
+++ b/SmartDeviceLink-iOS.xcodeproj/project.pbxproj
@@ -9579,10 +9579,7 @@
DYLIB_CURRENT_VERSION = 1;
DYLIB_INSTALL_NAME_BASE = "@rpath";
FRAMEWORK_SEARCH_PATHS = "$(inherited)";
- GCC_PREPROCESSOR_DEFINITIONS = (
- "DEBUG=1",
- "$(inherited)",
- );
+ GCC_PREPROCESSOR_DEFINITIONS = "$(inherited)";
GCC_WARN_INITIALIZER_NOT_FULLY_BRACKETED = YES;
GCC_WARN_SIGN_COMPARE = NO;
GCC_WARN_UNKNOWN_PRAGMAS = YES;
diff --git a/SmartDeviceLink/private/SDLDeleteFileOperation.h b/SmartDeviceLink/private/SDLDeleteFileOperation.h
index 9252ae725..f5031a61e 100644
--- a/SmartDeviceLink/private/SDLDeleteFileOperation.h
+++ b/SmartDeviceLink/private/SDLDeleteFileOperation.h
@@ -18,30 +18,26 @@ NS_ASSUME_NONNULL_BEGIN
@interface SDLDeleteFileOperation : SDLAsynchronousOperation
-/**
- * Create an instance of a delete files operation which will tell the remote system to remove a file form its storage.
- *
- * @param fileName The name of the file to be deleted on the remote system.
- * @param connectionManager The connection manager which will handle transporting the request to the remote system.
- * @param completionHandler A completion handler to be called when the delete finishes.
- *
- * @return An instance of SDLDeleteFilesOperation
- */
-- (instancetype)initWithFileName:(NSString *)fileName connectionManager:(id<SDLConnectionManagerType>)connectionManager completionHandler:(nullable SDLFileManagerDeleteCompletionHandler)completionHandler;
-
-/**
- The name of the file to be deleted on the remote system.
- */
-@property (copy, nonatomic, readonly) NSString *fileName;
-
-/**
- The connection manager which will handle transporting the request to the remote system.
- */
+/// Create an instance of a delete files operation which will tell the remote system to remove a file form its storage.
+/// @param fileName The name of the file to be deleted on the remote system.
+/// @param connectionManager The connection manager which will handle transporting the request to the remote system.
+/// @param remoteFileNames The files currently on the remote file system. Used to check if the file is available to be deleted.
+/// @param completionHandler A completion handler to be called when the delete finishes.
+///
+/// @returns An instance of SDLDeleteFilesOperation
+- (instancetype)initWithFileName:(SDLFileName *)fileName connectionManager:(id<SDLConnectionManagerType>)connectionManager remoteFileNames:(NSSet<SDLFileName *> *)remoteFileNames completionHandler:(SDLFileManagerDeleteCompletionHandler)completionHandler;
+
+
+/// The name of the file to be deleted on the remote system.
+@property (copy, nonatomic, readonly) SDLFileName *fileName;
+
+/// The files currently on the remote file system. Used to check if the file is available to be deleted.
+@property (nonatomic, strong) NSSet<SDLFileName *> *remoteFileNames;
+
+/// The connection manager which will handle transporting the request to the remote system.
@property (weak, nonatomic, readonly) id<SDLConnectionManagerType> connectionManager;
-/**
- A completion handler to be called when the delete finishes.
- */
+/// A completion handler to be called when the delete finishes.
@property (copy, nonatomic, nullable, readonly) SDLFileManagerDeleteCompletionHandler completionHandler;
@end
diff --git a/SmartDeviceLink/private/SDLDeleteFileOperation.m b/SmartDeviceLink/private/SDLDeleteFileOperation.m
index 80efdf332..e170837f7 100644
--- a/SmartDeviceLink/private/SDLDeleteFileOperation.m
+++ b/SmartDeviceLink/private/SDLDeleteFileOperation.m
@@ -12,6 +12,7 @@
#import "SDLDeleteFile.h"
#import "SDLDeleteFileResponse.h"
#import "SDLError.h"
+#import "SDLLogMacros.h"
NS_ASSUME_NONNULL_BEGIN
@@ -26,7 +27,7 @@ NS_ASSUME_NONNULL_BEGIN
@implementation SDLDeleteFileOperation
-- (instancetype)initWithFileName:(NSString *)fileName connectionManager:(id<SDLConnectionManagerType>)connectionManager completionHandler:(nullable SDLFileManagerDeleteCompletionHandler)completionHandler {
+- (instancetype)initWithFileName:(SDLFileName *)fileName connectionManager:(id<SDLConnectionManagerType>)connectionManager remoteFileNames:(NSSet<SDLFileName *> *)remoteFileNames completionHandler:(SDLFileManagerDeleteCompletionHandler)completionHandler {
self = [super init];
if (!self) {
if (completionHandler != nil) {
@@ -37,6 +38,7 @@ NS_ASSUME_NONNULL_BEGIN
_fileName = fileName;
_connectionManager = connectionManager;
+ _remoteFileNames = remoteFileNames;
_completionHandler = completionHandler;
return self;
@@ -46,6 +48,12 @@ NS_ASSUME_NONNULL_BEGIN
[super start];
if (self.isCancelled) { return; }
+ if (![self.remoteFileNames containsObject:self.fileName]) {
+ SDLLogW(@"File to delete is no longer on the head unit, aborting operation");
+ self.completionHandler(NO, NSNotFound, [NSError sdl_fileManager_fileDoesNotExistError]);
+ return [self finishOperation];
+ }
+
[self sdl_deleteFile];
}
@@ -54,18 +62,17 @@ NS_ASSUME_NONNULL_BEGIN
typeof(self) weakself = self;
[self.connectionManager sendConnectionManagerRequest:deleteFile withResponseHandler:^(__kindof SDLRPCRequest *request, __kindof SDLRPCResponse *response, NSError *error) {
- // Pull out the parameters
SDLDeleteFileResponse *deleteFileResponse = (SDLDeleteFileResponse *)response;
BOOL success = [deleteFileResponse.success boolValue];
- // If spaceAvailable is nil, set it to the max value
- NSUInteger bytesAvailable = deleteFileResponse.spaceAvailable != nil ? deleteFileResponse.spaceAvailable.unsignedIntegerValue : 2000000000;
-
- // Callback
- if (weakself.completionHandler != nil) {
- weakself.completionHandler(success, bytesAvailable, error);
+ if (error != nil) {
+ SDLLogE(@"Error deleting file: %@", error);
}
+ // If spaceAvailable is nil, set it to the max value
+ NSUInteger bytesAvailable = (deleteFileResponse.spaceAvailable != nil) ? deleteFileResponse.spaceAvailable.unsignedIntegerValue : 2000000000;
+
+ weakself.completionHandler(success, bytesAvailable, error);
[weakself finishOperation];
}];
}
@@ -78,7 +85,7 @@ NS_ASSUME_NONNULL_BEGIN
}
- (NSOperationQueuePriority)queuePriority {
- return NSOperationQueuePriorityVeryHigh;
+ return NSOperationQueuePriorityNormal;
}
@end
diff --git a/SmartDeviceLink/private/SDLError.m b/SmartDeviceLink/private/SDLError.m
index 120077f88..6a72f296a 100644
--- a/SmartDeviceLink/private/SDLError.m
+++ b/SmartDeviceLink/private/SDLError.m
@@ -176,7 +176,7 @@ NS_ASSUME_NONNULL_BEGIN
NSDictionary<NSString *, NSString *> *userInfo = @{
NSLocalizedDescriptionKey: @"Cannot overwrite remote file",
NSLocalizedFailureReasonErrorKey: @"The remote file system already has a file of this name, and the file manager is set to not automatically overwrite files",
- NSLocalizedRecoverySuggestionErrorKey: @"Set SDLFileManager autoOverwrite to YES, or call forceUploadFile:completion:"
+ NSLocalizedRecoverySuggestionErrorKey: @"Set file.overwrite to true to overwrite the file"
};
return [NSError errorWithDomain:SDLErrorDomainFileManager code:SDLFileManagerErrorCannotOverwrite userInfo:userInfo];
}
diff --git a/SmartDeviceLink/private/SDLUploadFileOperation.h b/SmartDeviceLink/private/SDLUploadFileOperation.h
index cd3eaa4af..c14cbfe4e 100644
--- a/SmartDeviceLink/private/SDLUploadFileOperation.h
+++ b/SmartDeviceLink/private/SDLUploadFileOperation.h
@@ -11,8 +11,8 @@
#import "SDLAsynchronousOperation.h"
#import "SDLFileManagerConstants.h"
-
@protocol SDLConnectionManagerType;
+@class SDLFileManager;
@class SDLFileWrapper;
@@ -22,16 +22,18 @@ NS_ASSUME_NONNULL_BEGIN
@interface SDLUploadFileOperation : SDLAsynchronousOperation
/**
- * Create an instance of an upload files operation which will send a file to a remote system when added to an operation queue.
+ * Create an instance of an upload files operation which will send a file to a remote system when added to an operation queue.
*
- * @param file A file wrapper around the file which will be sent and a completion handler for when the file finishes sending.
- * @param connectionManager The connection manager which will handle transporting the file bytes to the remote system
+ * @param file A file wrapper around the file which will be sent and a completion handler for when the file finishes sending.
+ * @param connectionManager The connection manager which will handle transporting the file bytes to the remote system
+ * @param fileManager The file manager, used to check if the file is uploaded
*
- * @return An instance of SDLUploadFilesOperation
+ * @return An instance of SDLUploadFilesOperation
*/
-- (instancetype)initWithFile:(SDLFileWrapper *)file connectionManager:(id<SDLConnectionManagerType>)connectionManager;
+- (instancetype)initWithFile:(SDLFileWrapper *)file connectionManager:(id<SDLConnectionManagerType>)connectionManager fileManager:(SDLFileManager *)fileManager;
@property (nonatomic, strong, readonly) SDLFileWrapper *fileWrapper;
+@property (nonatomic, weak) SDLFileManager *fileManager;
@end
diff --git a/SmartDeviceLink/private/SDLUploadFileOperation.m b/SmartDeviceLink/private/SDLUploadFileOperation.m
index aa476964a..bda634524 100644
--- a/SmartDeviceLink/private/SDLUploadFileOperation.m
+++ b/SmartDeviceLink/private/SDLUploadFileOperation.m
@@ -11,6 +11,7 @@
#import "SDLConnectionManagerType.h"
#import "SDLError.h"
#import "SDLFile.h"
+#import "SDLFileManager.h"
#import "SDLFileWrapper.h"
#import "SDLGlobals.h"
#import "SDLLogMacros.h"
@@ -39,22 +40,17 @@ static NSUInteger const MaxCRCValue = UINT32_MAX;
@end
-@implementation SDLUploadFileOperation {
- BOOL executing;
- BOOL finished;
-}
+@implementation SDLUploadFileOperation
-- (instancetype)initWithFile:(SDLFileWrapper *)file connectionManager:(id<SDLConnectionManagerType>)connectionManager {
+- (instancetype)initWithFile:(SDLFileWrapper *)file connectionManager:(id<SDLConnectionManagerType>)connectionManager fileManager:(SDLFileManager *)fileManager {
self = [super init];
if (!self) {
return nil;
}
- executing = NO;
- finished = NO;
-
_fileWrapper = file;
_connectionManager = connectionManager;
+ _fileManager = fileManager;
return self;
}
@@ -63,65 +59,56 @@ static NSUInteger const MaxCRCValue = UINT32_MAX;
[super start];
if (self.isCancelled) { return; }
- [self sdl_sendFile:self.fileWrapper.file mtuSize:[[SDLGlobals sharedGlobals] mtuSizeForServiceType:SDLServiceTypeRPC] withCompletion:self.fileWrapper.completionHandler];
+ SDLFile *file = self.fileWrapper.file;
+
+ // HAX: [#827](https://github.com/smartdevicelink/sdl_ios/issues/827) Older versions of Core had a bug where list files would cache incorrectly. This led to attempted uploads failing due to the system thinking they were already there when they were not. This is only needed if connecting to Core v4.3.1 or less which corresponds to RPC v4.3.1 or less
+ if (!file.persistent && ![self.fileManager hasUploadedFile:file] && [[SDLGlobals sharedGlobals].rpcVersion isLessThanVersion:[SDLVersion versionWithMajor:4 minor:4 patch:0]]) {
+ file.overwrite = YES;
+ }
+
+ if (![self.fileManager fileNeedsUpload:file]) {
+ SDLLogW(@"File is already on the head unit, aborting upload operation");
+ self.fileWrapper.completionHandler(NO, NSNotFound, [NSError sdl_fileManager_cannotOverwriteError]);
+ return [self finishOperation];
+ }
+
+ [self sdl_sendFile:file mtuSize:[[SDLGlobals sharedGlobals] mtuSizeForServiceType:SDLServiceTypeRPC] withCompletion:self.fileWrapper.completionHandler];
}
/**
- Sends data asynchronously to the SDL Core by breaking the data into smaller packets, each of which is sent via a putfile. To prevent large files from eating up memory, the data packet is deleted once it is sent via a putfile. If the SDL Core receives all the putfiles successfully, a success response with the amount of free storage space left on the SDL Core is returned. Otherwise the error returned by the SDL Core is passed along.
+ Sends data asynchronously to the SDL Core by breaking the data into smaller packets, each of which is sent via a PutFile. To prevent large files from eating up memory, the data packet is deleted once it is sent via a PutFile. If the SDL Core receives all the PutFiles successfully, a success response with the amount of free storage space left on the SDL Core is returned. Otherwise the error returned by the SDL Core is passed along.
@param file The file containing the data to be sent to the SDL Core
@param mtuSize The maximum packet size allowed
@param completion Closure returning whether or not the upload was a success
*/
-- (void)sdl_sendFile:(SDLFile *)file mtuSize:(NSUInteger)mtuSize withCompletion:(SDLFileManagerUploadCompletionHandler)completion {
+- (void)sdl_sendFile:(SDLFile *)file mtuSize:(NSUInteger)mtuSize withCompletion:(SDLFileManagerUploadCompletionHandler)completion {
__block NSError *streamError = nil;
__block NSUInteger bytesAvailable = 2000000000;
__block NSInteger highestCorrelationIDReceived = -1;
- if (self.isCancelled) {
- completion(NO, bytesAvailable, [NSError sdl_fileManager_fileUploadCanceled]);
- [self finishOperation];
- return;
- }
-
if (file == nil) {
completion(NO, bytesAvailable, [NSError sdl_fileManager_fileDoesNotExistError]);
- [self finishOperation];
- return;
+ return [self finishOperation];
}
self.inputStream = [self sdl_openInputStreamWithFile:file];
- if (self.inputStream == nil || ![self.inputStream hasBytesAvailable]) {
+ if (self.inputStream == nil || !self.inputStream.hasBytesAvailable) {
// If the file does not exist or the passed data is nil, return an error
[self sdl_closeInputStream];
completion(NO, bytesAvailable, [NSError sdl_fileManager_fileDoesNotExistError]);
- [self finishOperation];
- return;
+ return [self finishOperation];
}
dispatch_group_t putFileGroup = dispatch_group_create();
dispatch_group_enter(putFileGroup);
- // Wait for all packets be sent before returning whether or not the upload was a success
- __weak typeof(self) weakself = self;
- dispatch_group_notify(putFileGroup, [SDLGlobals sharedGlobals].sdlProcessingQueue, ^{
- typeof(weakself) strongself = weakself;
- [weakself sdl_closeInputStream];
-
- if (streamError != nil || strongself.isCancelled) {
- completion(NO, bytesAvailable, streamError);
- } else {
- completion(YES, bytesAvailable, nil);
- }
-
- [weakself finishOperation];
- });
-
// Break the data into small pieces, each of which will be sent in a separate putfile
NSUInteger maxBulkDataSize = [self.class sdl_getMaxBulkDataSizeForFile:file mtuSize:mtuSize];
NSUInteger currentOffset = 0;
- for (int i = 0; i < (((file.fileSize - 1) / maxBulkDataSize) + 1); i++) {
+ NSUInteger numPutFiles = (((file.fileSize - 1) / maxBulkDataSize) + 1);
+ for (int i = 0; i < numPutFiles; i++) {
dispatch_group_enter(putFileGroup);
// Get a chunk of data from the input stream
@@ -145,19 +132,11 @@ static NSUInteger const MaxCRCValue = UINT32_MAX;
typeof(weakself) strongself = weakself;
SDLPutFileResponse *putFileResponse = (SDLPutFileResponse *)response;
- // Check if the upload process has been cancelled by another packet. If so, stop the upload process.
// TODO: Is this the right way to handle this case? Should we just abort everything in the future? Should we be deleting what we sent? Should we have an automatic retry strategy based on what the error was?
- if (strongself.isCancelled) {
- dispatch_group_leave(putFileGroup);
- BLOCK_RETURN;
- }
-
// If the SDL Core returned an error, cancel the upload the process in the future
if (error != nil || response == nil || !response.success.boolValue || strongself.isCancelled) {
- [strongself cancel];
streamError = error;
- dispatch_group_leave(putFileGroup);
- BLOCK_RETURN;
+ BLOCK_RETURN dispatch_group_leave(putFileGroup);;
}
// If no errors, watch for a response containing the amount of storage left on the SDL Core
@@ -172,6 +151,22 @@ static NSUInteger const MaxCRCValue = UINT32_MAX;
}];
}
dispatch_group_leave(putFileGroup);
+
+ // Wait for all packets be sent before returning whether or not the upload was a success
+ __weak typeof(self) weakself = self;
+ dispatch_group_notify(putFileGroup, [SDLGlobals sharedGlobals].sdlProcessingQueue, ^{
+ typeof(weakself) strongself = weakself;
+ [strongself sdl_closeInputStream];
+
+ if (streamError != nil || strongself.isCancelled) {
+ SDLLogE(@"Error sending PutFile RPCs for upload: %@, error: %@", strongself.fileWrapper.file, streamError);
+ completion(NO, bytesAvailable, streamError);
+ } else {
+ completion(YES, bytesAvailable, nil);
+ }
+
+ [strongself finishOperation];
+ });
}
/**
diff --git a/SmartDeviceLink/public/SDLFile.m b/SmartDeviceLink/public/SDLFile.m
index b3efaf200..424e7c3b3 100644
--- a/SmartDeviceLink/public/SDLFile.m
+++ b/SmartDeviceLink/public/SDLFile.m
@@ -161,7 +161,18 @@ NS_ASSUME_NONNULL_BEGIN
#pragma mark - NSCopying
- (id)copyWithZone:(nullable NSZone *)zone {
- return [[self.class allocWithZone:zone] initWithFileURL:_fileURL name:_name persistent:_persistent];
+ SDLFile *fileCopy = [[self.class allocWithZone:zone] init];
+ fileCopy.name = _name.copy;
+ fileCopy.fileURL = _fileURL.copy;
+ fileCopy.fileType = _fileType.copy;
+ fileCopy.persistent = _persistent;
+ fileCopy.isStaticIcon = _isStaticIcon;
+
+ if (_data.length != 0) {
+ fileCopy.data = _data.copy;
+ }
+
+ return fileCopy;
}
#pragma mark - NSObject overrides
@@ -186,9 +197,17 @@ NS_ASSUME_NONNULL_BEGIN
if (!file) { return NO; }
BOOL haveEqualNames = [self.name isEqualToString:file.name];
- BOOL haveEqualData = [self.data isEqualToData:file.data];
BOOL haveEqualFormats = [self.fileType isEqualToEnum:file.fileType];
+ BOOL haveEqualData = NO;
+ if (self.data.length == 0 && file.data.length == 0) {
+ haveEqualData = [self.fileURL isEqual:file.fileURL];
+ } else if (self.data.length > 0 && file.data.length > 0) {
+ haveEqualData = [self.data isEqualToData:file.data];
+ } else {
+ return NO;
+ }
+
return haveEqualNames && haveEqualData && haveEqualFormats;
}
diff --git a/SmartDeviceLink/public/SDLFileManager.m b/SmartDeviceLink/public/SDLFileManager.m
index 8407dd748..85bf07480 100644
--- a/SmartDeviceLink/public/SDLFileManager.m
+++ b/SmartDeviceLink/public/SDLFileManager.m
@@ -12,6 +12,7 @@
#import "SDLLogMacros.h"
#import "SDLDeleteFileOperation.h"
#import "SDLError.h"
+#import "SDLErrorConstants.h"
#import "SDLFile.h"
#import "SDLFileManagerConfiguration.h"
#import "SDLFileWrapper.h"
@@ -230,13 +231,8 @@ SDLFileManagerState *const SDLFileManagerStateStartupError = @"StartupError";
#pragma mark - Deleting
- (void)deleteRemoteFileWithName:(SDLFileName *)name completionHandler:(nullable SDLFileManagerDeleteCompletionHandler)handler {
- if ((![self.remoteFileNames containsObject:name]) && (handler != nil)) {
- handler(NO, self.bytesAvailable, [NSError sdl_fileManager_noKnownFileError]);
- return;
- }
-
__weak typeof(self) weakSelf = self;
- SDLDeleteFileOperation *deleteOperation = [[SDLDeleteFileOperation alloc] initWithFileName:name connectionManager:self.connectionManager completionHandler:^(BOOL success, NSUInteger bytesAvailable, NSError *_Nullable error) {
+ SDLDeleteFileOperation *deleteOperation = [[SDLDeleteFileOperation alloc] initWithFileName:name connectionManager:self.connectionManager remoteFileNames:self.remoteFileNames completionHandler:^(BOOL success, NSUInteger bytesAvailable, NSError *_Nullable error) {
__strong typeof(weakSelf) strongSelf = weakSelf;
// Mutate self based on the changes
@@ -262,10 +258,11 @@ SDLFileManagerState *const SDLFileManagerStateStartupError = @"StartupError";
dispatch_group_t deleteFilesTask = dispatch_group_create();
dispatch_group_enter(deleteFilesTask);
- for(NSString *name in names) {
+ __weak typeof(self) weakself = self;
+ for (NSString *name in names) {
dispatch_group_enter(deleteFilesTask);
[self deleteRemoteFileWithName:name completionHandler:^(BOOL success, NSUInteger bytesAvailable, NSError * _Nullable error) {
- if(!success) {
+ if (!success) {
failedDeletes[name] = error;
}
dispatch_group_leave(deleteFilesTask);
@@ -273,7 +270,7 @@ SDLFileManagerState *const SDLFileManagerStateStartupError = @"StartupError";
}
dispatch_group_leave(deleteFilesTask);
- // Wait for all files to be deleted
+ // When all files to be deleted
dispatch_group_notify(deleteFilesTask, [SDLGlobals sharedGlobals].sdlProcessingQueue, ^{
if (completionHandler == nil) { return; }
if (failedDeletes.count > 0) {
@@ -334,7 +331,7 @@ SDLFileManagerState *const SDLFileManagerStateStartupError = @"StartupError";
dispatch_group_enter(uploadFilesTask);
__weak typeof(self) weakself = self;
[self uploadFile:file completionHandler:^(BOOL success, NSUInteger bytesAvailable, NSError * _Nullable error) {
- if(!success) {
+ if (!success) {
failedUploads[file.name] = error;
}
@@ -388,39 +385,29 @@ SDLFileManagerState *const SDLFileManagerStateStartupError = @"StartupError";
return;
}
- // HAX: [#827](https://github.com/smartdevicelink/sdl_ios/issues/827) Older versions of Core had a bug where list files would cache incorrectly. This led to attempted uploads failing due to the system thinking they were already there when they were not. This is only needed if connecting to Core v4.3.1 or less which corresponds to RPC v4.3.1 or less
- if (!file.persistent && ![self hasUploadedFile:file] && [[SDLGlobals sharedGlobals].rpcVersion isLessThanVersion:[SDLVersion versionWithMajor:4 minor:4 patch:0]]) {
- file.overwrite = YES;
- }
-
- // Check our overwrite settings and error out if it would overwrite
- if (!file.overwrite && [self.remoteFileNames containsObject:file.name]) {
- if (handler != nil) {
- handler(NO, self.bytesAvailable, [NSError sdl_fileManager_cannotOverwriteError]);
- }
- return;
- }
-
// If we didn't error out over the overwrite, then continue on
[self sdl_uploadFile:file completionHandler:handler];
}
- (void)sdl_uploadFile:(SDLFile *)file completionHandler:(nullable SDLFileManagerUploadCompletionHandler)handler {
- __block NSString *fileName = file.name;
+ SDLFile *fileCopy = [file copy];
__weak typeof(self) weakSelf = self;
- SDLFileWrapper *fileWrapper = [SDLFileWrapper wrapperWithFile:file completionHandler:^(BOOL success, NSUInteger bytesAvailable, NSError *_Nullable error) {
+ SDLFileWrapper *fileWrapper = [SDLFileWrapper wrapperWithFile:fileCopy completionHandler:^(BOOL success, NSUInteger bytesAvailable, NSError *_Nullable error) {
if (success) {
weakSelf.bytesAvailable = bytesAvailable;
- [weakSelf.mutableRemoteFileNames addObject:fileName];
- [weakSelf.uploadedEphemeralFileNames addObject:fileName];
- } else {
- weakSelf.failedFileUploadsCount = [weakSelf.class sdl_incrementFailedUploadCountForFileName:file.name failedFileUploadsCount:weakSelf.failedFileUploadsCount];
+ [weakSelf.mutableRemoteFileNames addObject:fileCopy.name];
- NSUInteger maxUploadCount = [file isMemberOfClass:[SDLArtwork class]] ? weakSelf.maxArtworkUploadAttempts : self.maxFileUploadAttempts;
- if ([weakSelf sdl_canFileBeUploadedAgain:file maxUploadCount:maxUploadCount failedFileUploadsCount:weakSelf.failedFileUploadsCount]) {
+ if (!file.persistent) {
+ [weakSelf.uploadedEphemeralFileNames addObject:fileCopy.name];
+ }
+ } else if (error.code != SDLFileManagerErrorCannotOverwrite) {
+ weakSelf.failedFileUploadsCount = [weakSelf.class sdl_incrementFailedUploadCountForFileName:fileCopy.name failedFileUploadsCount:weakSelf.failedFileUploadsCount];
+
+ NSUInteger maxUploadCount = [fileCopy isMemberOfClass:[SDLArtwork class]] ? weakSelf.maxArtworkUploadAttempts : weakSelf.maxFileUploadAttempts;
+ if ([weakSelf sdl_canFileBeUploadedAgain:fileCopy maxUploadCount:maxUploadCount failedFileUploadsCount:weakSelf.failedFileUploadsCount]) {
SDLLogD(@"Attempting to resend file with name %@ after a failed upload attempt", file.name);
- return [weakSelf sdl_uploadFile:file completionHandler:handler];
+ return [weakSelf sdl_uploadFile:fileCopy completionHandler:handler];
}
}
@@ -429,8 +416,7 @@ SDLFileManagerState *const SDLFileManagerStateStartupError = @"StartupError";
}
}];
- SDLUploadFileOperation *uploadOperation = [[SDLUploadFileOperation alloc] initWithFile:fileWrapper connectionManager:self.connectionManager];
-
+ SDLUploadFileOperation *uploadOperation = [[SDLUploadFileOperation alloc] initWithFile:fileWrapper connectionManager:self.connectionManager fileManager:self];
[self.transactionQueue addOperation:uploadOperation];
}
diff --git a/SmartDeviceLinkTests/DevAPISpecs/SDLDeleteFileOperationSpec.m b/SmartDeviceLinkTests/DevAPISpecs/SDLDeleteFileOperationSpec.m
index a86796abe..7bf89d289 100644
--- a/SmartDeviceLinkTests/DevAPISpecs/SDLDeleteFileOperationSpec.m
+++ b/SmartDeviceLinkTests/DevAPISpecs/SDLDeleteFileOperationSpec.m
@@ -22,7 +22,7 @@ describe(@"Delete File Operation", ^{
deleteFileName = @"Some File";
testConnectionManager = [[TestConnectionManager alloc] init];
- testOperation = [[SDLDeleteFileOperation alloc] initWithFileName:deleteFileName connectionManager:testConnectionManager completionHandler:^(BOOL success, NSUInteger bytesAvailable, NSError * _Nullable error) {
+ testOperation = [[SDLDeleteFileOperation alloc] initWithFileName:deleteFileName connectionManager:testConnectionManager remoteFileNames:[NSSet set] completionHandler:^(BOOL success, NSUInteger bytesAvailable, NSError * _Nullable error) {
successResult = success;
bytesAvailableResult = bytesAvailable;
errorResult = error;
@@ -30,68 +30,82 @@ describe(@"Delete File Operation", ^{
});
it(@"should have a priority of 'very high'", ^{
- expect(@(testOperation.queuePriority)).to(equal(@(NSOperationQueuePriorityVeryHigh)));
+ expect(@(testOperation.queuePriority)).to(equal(@(NSOperationQueuePriorityNormal)));
});
describe(@"running the operation", ^{
- beforeEach(^{
- [testOperation start];
- });
-
- it(@"should send a list files request", ^{
- expect(testConnectionManager.receivedRequests.lastObject).to(beAnInstanceOf([SDLDeleteFile class]));
- });
-
- context(@"when a good response comes back", ^{
- __block SDLDeleteFileResponse *goodResponse = nil;
- __block NSNumber *responseSpaceAvailable = nil;
-
+ context(@"when the file is not on the remote system", ^{
beforeEach(^{
- responseSpaceAvailable = @(11212512);
-
- goodResponse = [[SDLDeleteFileResponse alloc] init];
- goodResponse.success = @YES;
- goodResponse.spaceAvailable = responseSpaceAvailable;
-
- [testConnectionManager respondToLastRequestWithResponse:goodResponse];
- });
-
- it(@"should have called the completion handler with proper data", ^{
- expect(@(successResult)).to(equal(@YES));
- expect(@(bytesAvailableResult)).to(equal(responseSpaceAvailable));
- expect(errorResult).to(beNil());
+ [testOperation start];
});
-
- it(@"should be set to finished", ^{
- expect(@(testOperation.finished)).to(equal(@YES));
- expect(@(testOperation.executing)).to(equal(@NO));
+
+ it(@"should finish without sending the delete file request", ^{
+ expect(testConnectionManager.receivedRequests).to(haveCount(0));
+ expect(testOperation.isFinished).to(beTrue());
});
});
-
- context(@"when a bad response comes back", ^{
- __block SDLDeleteFileResponse *badResponse = nil;
- __block NSNumber *responseSpaceAvailable = nil;
-
- __block NSString *responseErrorDescription = nil;
- __block NSString *responseErrorReason = nil;
-
+
+ context(@"when the file is on the remote system", ^{
beforeEach(^{
- responseSpaceAvailable = @(0);
-
- responseErrorDescription = @"some description";
- responseErrorReason = @"some reason";
-
- badResponse = [[SDLDeleteFileResponse alloc] init];
- badResponse.success = @NO;
- badResponse.spaceAvailable = responseSpaceAvailable;
-
- [testConnectionManager respondToLastRequestWithResponse:badResponse error:[NSError sdl_lifecycle_unknownRemoteErrorWithDescription:responseErrorDescription andReason:responseErrorReason]];
+ testOperation.remoteFileNames = [NSSet setWithObject:deleteFileName];
+ [testOperation start];
+ });
+
+ it(@"should send a delete file request", ^{
+ expect(testConnectionManager.receivedRequests.lastObject).to(beAnInstanceOf([SDLDeleteFile class]));
+ });
+
+ context(@"when a good response comes back", ^{
+ __block SDLDeleteFileResponse *goodResponse = nil;
+ __block NSNumber *responseSpaceAvailable = nil;
+
+ beforeEach(^{
+ responseSpaceAvailable = @(11212512);
+
+ goodResponse = [[SDLDeleteFileResponse alloc] init];
+ goodResponse.success = @YES;
+ goodResponse.spaceAvailable = responseSpaceAvailable;
+
+ [testConnectionManager respondToLastRequestWithResponse:goodResponse];
+ });
+
+ it(@"should have called the completion handler with proper data", ^{
+ expect(@(successResult)).to(equal(@YES));
+ expect(@(bytesAvailableResult)).to(equal(responseSpaceAvailable));
+ expect(errorResult).to(beNil());
+ });
+
+ it(@"should be set to finished", ^{
+ expect(@(testOperation.finished)).to(equal(@YES));
+ expect(@(testOperation.executing)).to(equal(@NO));
+ });
});
-
- it(@"should have called completion handler with error", ^{
- expect(errorResult.localizedDescription).to(match(responseErrorDescription));
- expect(errorResult.localizedFailureReason).to(match(responseErrorReason));
- expect(@(successResult)).to(equal(@NO));
+
+ context(@"when a bad response comes back", ^{
+ __block SDLDeleteFileResponse *badResponse = nil;
+ __block NSNumber *responseSpaceAvailable = nil;
+
+ __block NSString *responseErrorDescription = nil;
+ __block NSString *responseErrorReason = nil;
+
+ beforeEach(^{
+ responseSpaceAvailable = @(0);
+
+ responseErrorDescription = @"some description";
+ responseErrorReason = @"some reason";
+
+ badResponse = [[SDLDeleteFileResponse alloc] init];
+ badResponse.success = @NO;
+ badResponse.spaceAvailable = responseSpaceAvailable;
+
+ [testConnectionManager respondToLastRequestWithResponse:badResponse error:[NSError sdl_lifecycle_unknownRemoteErrorWithDescription:responseErrorDescription andReason:responseErrorReason]];
+ });
+
+ it(@"should have called completion handler with error", ^{
+ expect(errorResult.localizedDescription).to(match(responseErrorDescription));
+ expect(errorResult.localizedFailureReason).to(match(responseErrorReason));
+ expect(@(successResult)).to(equal(@NO));
+ });
});
});
});
diff --git a/SmartDeviceLinkTests/DevAPISpecs/SDLFileManagerSpec.m b/SmartDeviceLinkTests/DevAPISpecs/SDLFileManagerSpec.m
index 9c82297e0..782bc090d 100644
--- a/SmartDeviceLinkTests/DevAPISpecs/SDLFileManagerSpec.m
+++ b/SmartDeviceLinkTests/DevAPISpecs/SDLFileManagerSpec.m
@@ -200,9 +200,7 @@ describe(@"uploading / deleting single files with the file manager", ^{
});
describe(@"deleting a file", ^{
- __block BOOL completionSuccess = NO;
- __block NSUInteger completionBytesAvailable = 0;
- __block NSError *completionError = nil;
+ __block BOOL completionCalled = NO;
beforeEach(^{
testFileManager.mutableRemoteFileNames = [NSMutableSet setWithArray:testInitialFileNames];
@@ -213,19 +211,14 @@ describe(@"uploading / deleting single files with the file manager", ^{
beforeEach(^{
NSString *someUnknownFileName = @"Some Unknown File Name";
[testFileManager deleteRemoteFileWithName:someUnknownFileName completionHandler:^(BOOL success, NSUInteger bytesAvailable, NSError * _Nullable error) {
- completionSuccess = success;
- completionBytesAvailable = bytesAvailable;
- completionError = error;
+ completionCalled = YES;
}];
- expect(testFileManager.pendingTransactions).to(beEmpty());
+ expect(testFileManager.pendingTransactions).toNot(beEmpty());
});
- it(@"should return the correct data", ^{
- expect(completionSuccess).to(beFalse());
- expect(completionBytesAvailable).to(equal(initialSpaceAvailable));
- expect(completionError).to(equal([NSError sdl_fileManager_noKnownFileError]));
- expect(testFileManager.remoteFileNames).to(haveCount(testInitialFileNames.count));
+ it(@"should not call the completion handler", ^{
+ expect(completionCalled).to(beFalse());
});
});
@@ -475,32 +468,36 @@ describe(@"uploading / deleting single files with the file manager", ^{
[SDLGlobals sharedGlobals].rpcVersion = [SDLVersion versionWithMajor:4 minor:3 patch:0];
});
- it(@"should not upload the file if persistence is YES", ^{
- SDLFile *persistentFile = [[SDLFile alloc] initWithData:testFileData name:testUploadFileName fileExtension:@"bin" persistent:YES];
- persistentFile.overwrite = testUploadOverwrite;
+ context(@"when persistent = YES", ^{
+ it(@"should create the upload operation", ^{
+ SDLFile *persistentFile = [[SDLFile alloc] initWithData:testFileData name:testUploadFileName fileExtension:@"bin" persistent:YES];
+ persistentFile.overwrite = testUploadOverwrite;
- [testFileManager uploadFile:persistentFile completionHandler:^(BOOL success, NSUInteger bytesAvailable, NSError * _Nullable error) {
- expect(@(success)).to(beFalse());
- expect(@(bytesAvailable)).to(equal(@(testFileManager.bytesAvailable)));
- expect(error).to(equal([NSError sdl_fileManager_cannotOverwriteError]));
- }];
+ [testFileManager uploadFile:persistentFile completionHandler:^(BOOL success, NSUInteger bytesAvailable, NSError * _Nullable error) {
+ expect(@(success)).to(beFalse());
+ expect(@(bytesAvailable)).to(equal(@(testFileManager.bytesAvailable)));
+ expect(error).to(equal([NSError sdl_fileManager_cannotOverwriteError]));
+ }];
- expect(testFileManager.pendingTransactions.count).to(equal(0));
+ expect(testFileManager.pendingTransactions.count).to(equal(1));
+ });
});
- it(@"should upload the file if persistence is NO", ^{
- SDLFile *unPersistentFile = [[SDLFile alloc] initWithData:testFileData name:testUploadFileName fileExtension:@"bin" persistent:NO];
- unPersistentFile.overwrite = testUploadOverwrite;
+ context(@"when persistent = NO", ^{
+ it(@"should upload the file", ^{
+ SDLFile *unPersistentFile = [[SDLFile alloc] initWithData:testFileData name:testUploadFileName fileExtension:@"bin" persistent:NO];
+ unPersistentFile.overwrite = testUploadOverwrite;
- [testFileManager uploadFile:unPersistentFile completionHandler:^(BOOL success, NSUInteger bytesAvailable, NSError * _Nullable error) {
- expect(success).to(beTrue());
- expect(bytesAvailable).to(equal(newBytesAvailable));
- expect(error).to(beNil());
- }];
+ [testFileManager uploadFile:unPersistentFile completionHandler:^(BOOL success, NSUInteger bytesAvailable, NSError * _Nullable error) {
+ expect(success).to(beTrue());
+ expect(bytesAvailable).to(equal(newBytesAvailable));
+ expect(error).to(beNil());
+ }];
- SDLUploadFileOperation *sentOperation = testFileManager.pendingTransactions.firstObject;
- sentOperation.fileWrapper.completionHandler(YES, newBytesAvailable, nil);
- expect(testFileManager.pendingTransactions.count).to(equal(1));
+ SDLUploadFileOperation *sentOperation = testFileManager.pendingTransactions.firstObject;
+ sentOperation.fileWrapper.completionHandler(YES, newBytesAvailable, nil);
+ expect(testFileManager.pendingTransactions.count).to(equal(1));
+ });
});
});
});
@@ -572,45 +569,51 @@ describe(@"uploading / deleting single files with the file manager", ^{
[testFileManager.stateMachine setToState:SDLFileManagerStateReady fromOldState:SDLFileManagerStateShutdown callEnterTransition:NO];
});
- it(@"should not upload the artwork again and simply return the artwork name when sending artwork that has already been uploaded", ^{
- expectedArtworkName = testInitialFileNames.firstObject;
+ context(@"when uploading an already uploading artwork", ^{
+ it(@"should create an upload operation", ^{
+ expectedArtworkName = testInitialFileNames.firstObject;
- SDLArtwork *art = [SDLArtwork artworkWithImage:testUIImage name:expectedArtworkName asImageFormat:SDLArtworkImageFormatPNG];
- [testFileManager uploadArtwork:art completionHandler:^(BOOL success, NSString * _Nonnull artworkName, NSUInteger bytesAvailable, NSError * _Nullable error) {
- expect(success).to(beTrue());
- expect(bytesAvailable).to(equal(initialSpaceAvailable));
- expect(error).to(beNil());
- }];
+ SDLArtwork *art = [SDLArtwork artworkWithImage:testUIImage name:expectedArtworkName asImageFormat:SDLArtworkImageFormatPNG];
+ [testFileManager uploadArtwork:art completionHandler:^(BOOL success, NSString * _Nonnull artworkName, NSUInteger bytesAvailable, NSError * _Nullable error) {
+ expect(success).to(beTrue());
+ expect(bytesAvailable).to(equal(initialSpaceAvailable));
+ expect(error).to(beNil());
+ }];
- expect(testFileManager.pendingTransactions.count).to(equal(0));
+ expect(testFileManager.pendingTransactions.count).to(equal(1));
+ });
});
- it(@"should upload the artwork and return the artwork name when done when sending artwork that has not yet been uploaded", ^{
- expectedArtworkName = @"uniqueArtworkName";
- [testFileManager uploadArtwork:[SDLArtwork artworkWithImage:testUIImage name:expectedArtworkName asImageFormat:SDLArtworkImageFormatPNG] completionHandler:^(BOOL success, NSString * _Nonnull artworkName, NSUInteger bytesAvailable, NSError * _Nullable error) {
- expect(success).to(beTrue());
- expect(bytesAvailable).to(equal(newBytesAvailable));
- expect(error).to(beNil());
- }];
+ context(@"when sending artwork that has not yet been uploaded", ^{
+ it(@"should upload the artwork and return the artwork name", ^{
+ expectedArtworkName = @"uniqueArtworkName";
+ [testFileManager uploadArtwork:[SDLArtwork artworkWithImage:testUIImage name:expectedArtworkName asImageFormat:SDLArtworkImageFormatPNG] completionHandler:^(BOOL success, NSString * _Nonnull artworkName, NSUInteger bytesAvailable, NSError * _Nullable error) {
+ expect(success).to(beTrue());
+ expect(bytesAvailable).to(equal(newBytesAvailable));
+ expect(error).to(beNil());
+ }];
- SDLUploadFileOperation *sentOperation = testFileManager.pendingTransactions.firstObject;
- sentOperation.fileWrapper.completionHandler(YES, newBytesAvailable, nil);
- expect(testFileManager.pendingTransactions.count).to(equal(1));
+ SDLUploadFileOperation *sentOperation = testFileManager.pendingTransactions.firstObject;
+ sentOperation.fileWrapper.completionHandler(YES, newBytesAvailable, nil);
+ expect(testFileManager.pendingTransactions.count).to(equal(1));
+ });
});
- it(@"should upload the artwork and return the artwork name when done when sending arwork that is already been uploaded but overwrite is enabled", ^{
- expectedArtworkName = testInitialFileNames.firstObject;
- SDLArtwork *testArt = [SDLArtwork artworkWithImage:testUIImage name:expectedArtworkName asImageFormat:SDLArtworkImageFormatPNG];
- testArt.overwrite = YES;
- [testFileManager uploadArtwork:testArt completionHandler:^(BOOL success, NSString * _Nonnull artworkName, NSUInteger bytesAvailable, NSError * _Nullable error) {
- expect(success).to(beTrue());
- expect(bytesAvailable).to(equal(newBytesAvailable));
- expect(error).to(beNil());
- }];
+ context(@"when sending arwork that is already been uploaded but overwrite is enabled", ^{
+ it(@"should upload the artwork and return the artwork name when done when sending arwork that is already been uploaded but overwrite is enabled", ^{
+ expectedArtworkName = testInitialFileNames.firstObject;
+ SDLArtwork *testArt = [SDLArtwork artworkWithImage:testUIImage name:expectedArtworkName asImageFormat:SDLArtworkImageFormatPNG];
+ testArt.overwrite = YES;
+ [testFileManager uploadArtwork:testArt completionHandler:^(BOOL success, NSString * _Nonnull artworkName, NSUInteger bytesAvailable, NSError * _Nullable error) {
+ expect(success).to(beTrue());
+ expect(bytesAvailable).to(equal(newBytesAvailable));
+ expect(error).to(beNil());
+ }];
- SDLUploadFileOperation *sentOperation = testFileManager.pendingTransactions.firstObject;
- sentOperation.fileWrapper.completionHandler(YES, newBytesAvailable, nil);
- expect(testFileManager.pendingTransactions.count).to(equal(1));
+ SDLUploadFileOperation *sentOperation = testFileManager.pendingTransactions.firstObject;
+ sentOperation.fileWrapper.completionHandler(YES, newBytesAvailable, nil);
+ expect(testFileManager.pendingTransactions.count).to(equal(1));
+ });
});
});
@@ -1131,7 +1134,7 @@ describe(@"uploading/deleting multiple files in the file manager", ^{
});
it(@"should cancel the remaining files if cancel is triggered after first upload", ^{
- for(int i = 0; i < 5; i += 1) {
+ for(int i = 0; i < 5; i++) {
NSString *testFileName = [NSString stringWithFormat:@"TestSmallFilesMemory%d", i];
SDLFile *testSDLFile = [SDLFile fileWithData:[@"someTextData" dataUsingEncoding:NSUTF8StringEncoding] name:testFileName fileExtension:@"bin"];
testSDLFile.overwrite = true;
@@ -1168,7 +1171,7 @@ describe(@"uploading/deleting multiple files in the file manager", ^{
});
it(@"should cancel the remaining files if cancel is triggered after half of the files are uploaded", ^{
- for(int i = 0; i < 5; i += 1) {
+ for(int i = 0; i < 5; i++) {
NSString *testFileName = [NSString stringWithFormat:@"TestSmallFilesMemory%d", i];
SDLFile *testSDLFile = [SDLFile fileWithData:[@"someTextData" dataUsingEncoding:NSUTF8StringEncoding] name:testFileName fileExtension:@"bin"];
testSDLFile.overwrite = true;
diff --git a/SmartDeviceLinkTests/DevAPISpecs/SDLUploadFileOperationSpec.m b/SmartDeviceLinkTests/DevAPISpecs/SDLUploadFileOperationSpec.m
index e18bda43f..4b4b0ecf9 100644
--- a/SmartDeviceLinkTests/DevAPISpecs/SDLUploadFileOperationSpec.m
+++ b/SmartDeviceLinkTests/DevAPISpecs/SDLUploadFileOperationSpec.m
@@ -1,8 +1,10 @@
#import <Quick/Quick.h>
#import <Nimble/Nimble.h>
+#import <OCMock/OCMock.h>
#import "SDLError.h"
#import "SDLFile.h"
+#import "SDLFileManager.h"
#import "SDLFileWrapper.h"
#import "SDLGlobals.h"
#import "SDLProtocolHeader.h"
@@ -91,6 +93,7 @@ describe(@"Streaming upload of data", ^{
__block NSUInteger expectedNumberOfPutFiles = 0;
__block TestConnectionManager *testConnectionManager = nil;
+ __block SDLFileManager *mockFileManager = nil;
__block SDLUploadFileOperation *testOperation = nil;
__block BOOL successResult = NO;
@@ -112,18 +115,71 @@ describe(@"Streaming upload of data", ^{
testOperation = nil;
testConnectionManager = [[TestConnectionManager alloc] init];
+ mockFileManager = OCMClassMock([SDLFileManager class]);
successResult = NO;
bytesAvailableResult = NO;
errorResult = nil;
});
- describe(@"When uploading data", ^{
+ describe(@"when the file is already on the head unit", ^{
+ context(@"when not overwriting", ^{
+ beforeEach(^{
+ OCMStub([mockFileManager fileNeedsUpload:[OCMArg isNotNil]]).andReturn(NO);
+ });
+
+ it(@"should not send the upload RPCs and finish the operation", ^{
+ testFileName = @"TestSmallMemory";
+ testFileData = [@"test1234" dataUsingEncoding:NSUTF8StringEncoding];
+ testFile = [SDLFile fileWithData:testFileData name:testFileName fileExtension:@"bin"];
+
+ testFileWrapper = [SDLFileWrapper wrapperWithFile:testFile completionHandler:^(BOOL success, NSUInteger bytesAvailable, NSError * _Nullable error) {
+ expect(success).to(beFalse());
+ expect(bytesAvailable).to(equal(NSNotFound));
+ expect(error).toNot(beNil());
+ }];
+
+ testOperation = [[SDLUploadFileOperation alloc] initWithFile:testFileWrapper connectionManager:testConnectionManager fileManager:mockFileManager];
+ [testOperation start];
+
+ expect(testConnectionManager.receivedRequests).to(haveCount(0));
+ expect(testOperation.isFinished).to(beTrue());
+ });
+ });
+
+ context(@"when overwriting", ^{
+ beforeEach(^{
+ OCMStub([mockFileManager fileNeedsUpload:[OCMArg any]]).andReturn(YES);
+ });
+
+ it(@"should send the upload RPCs", ^{
+ testFileName = @"TestSmallMemory";
+ testFileData = [@"test1234" dataUsingEncoding:NSUTF8StringEncoding];
+ testFile = [SDLFile fileWithData:testFileData name:testFileName fileExtension:@"bin"];
+ testFile.overwrite = YES;
+
+ testFileWrapper = [SDLFileWrapper wrapperWithFile:testFile completionHandler:^(BOOL success, NSUInteger bytesAvailable, NSError * _Nullable error) {
+ expect(success).to(beFalse());
+ expect(bytesAvailable).to(equal(NSNotFound));
+ expect(error).toNot(beNil());
+ }];
+
+ testOperation = [[SDLUploadFileOperation alloc] initWithFile:testFileWrapper connectionManager:testConnectionManager fileManager:mockFileManager];
+ [testOperation start];
+
+ expect(testConnectionManager.receivedRequests).to(haveCount(1));
+ expect(testOperation.isFinished).to(beFalse());
+ });
+ });
+ });
+
+ describe(@"when uploading data", ^{
__block NSInteger spaceLeft = 0;
__block SDLPutFileResponse *successResponse = nil;
beforeEach(^{
spaceLeft = 11212512;
+ OCMStub([mockFileManager fileNeedsUpload:[OCMArg isNotNil]]).andReturn(YES);
});
context(@"data should be split into smaller packets if too large to send all at once", ^{
@@ -140,7 +196,7 @@ describe(@"Streaming upload of data", ^{
expectedNumberOfPutFiles = [UploadFileOperationSpecHelpers testNumberOfPutFiles:testFile mtuSize:testMTUSize];
- testOperation = [[SDLUploadFileOperation alloc] initWithFile:testFileWrapper connectionManager:testConnectionManager];
+ testOperation = [[SDLUploadFileOperation alloc] initWithFile:testFileWrapper connectionManager:testConnectionManager fileManager:mockFileManager];
[testOperation start];
NSArray<SDLPutFile *> *testPutFiles = testConnectionManager.receivedRequests;
@@ -174,7 +230,7 @@ describe(@"Streaming upload of data", ^{
expectedNumberOfPutFiles = [UploadFileOperationSpecHelpers testNumberOfPutFiles:testFile mtuSize:testMTUSize];
- testOperation = [[SDLUploadFileOperation alloc] initWithFile:testFileWrapper connectionManager:testConnectionManager];
+ testOperation = [[SDLUploadFileOperation alloc] initWithFile:testFileWrapper connectionManager:testConnectionManager fileManager:mockFileManager];
[testOperation start];
NSArray<SDLPutFile *> *putFiles = testConnectionManager.receivedRequests;
@@ -208,7 +264,7 @@ describe(@"Streaming upload of data", ^{
expectedNumberOfPutFiles = [UploadFileOperationSpecHelpers testNumberOfPutFiles:testFile mtuSize:testMTUSize];
- testOperation = [[SDLUploadFileOperation alloc] initWithFile:testFileWrapper connectionManager:testConnectionManager];
+ testOperation = [[SDLUploadFileOperation alloc] initWithFile:testFileWrapper connectionManager:testConnectionManager fileManager:mockFileManager];
[testOperation start];
NSArray<SDLPutFile *> *putFiles = testConnectionManager.receivedRequests;
@@ -243,7 +299,7 @@ describe(@"Streaming upload of data", ^{
expectedNumberOfPutFiles = [UploadFileOperationSpecHelpers testNumberOfPutFiles:testFile mtuSize:testMTUSize];
- testOperation = [[SDLUploadFileOperation alloc] initWithFile:testFileWrapper connectionManager:testConnectionManager];
+ testOperation = [[SDLUploadFileOperation alloc] initWithFile:testFileWrapper connectionManager:testConnectionManager fileManager:mockFileManager];
[testOperation start];
NSArray<SDLPutFile *> *putFiles = testConnectionManager.receivedRequests;
@@ -264,8 +320,9 @@ describe(@"Streaming upload of data", ^{
});
});
- describe(@"When a response to the data upload comes back", ^{
+ describe(@"when a response to the data upload comes back", ^{
beforeEach(^{
+ OCMStub([mockFileManager fileNeedsUpload:[OCMArg isNotNil]]).andReturn(YES);
testFileName = @"TestLargeMemory";
UIImage *testImage = [UIImage imageNamed:@"testImagePNG" inBundle:[NSBundle bundleForClass:[self class]] compatibleWithTraitCollection:nil];
testFileData = UIImageJPEGRepresentation(testImage, 1.0);
@@ -279,11 +336,11 @@ describe(@"Streaming upload of data", ^{
expectedNumberOfPutFiles = [UploadFileOperationSpecHelpers testNumberOfPutFiles:testFile mtuSize:testMTUSize];
testConnectionManager = [[TestConnectionManager alloc] init];
- testOperation = [[SDLUploadFileOperation alloc] initWithFile:testFileWrapper connectionManager:testConnectionManager];
+ testOperation = [[SDLUploadFileOperation alloc] initWithFile:testFileWrapper connectionManager:testConnectionManager fileManager:mockFileManager];
[testOperation start];
});
- context(@"If data was sent successfully", ^{
+ context(@"if data was sent successfully", ^{
__block NSInteger spaceLeft = 0;
__block SDLPutFileResponse *successResponse = nil;
@@ -308,7 +365,7 @@ describe(@"Streaming upload of data", ^{
});
});
- context(@"If data was not sent successfully", ^{
+ context(@"if data was not sent successfully", ^{
__block SDLPutFileResponse *response = nil;
__block NSString *responseErrorDescription = nil;
__block NSString *responseErrorReason = nil;
@@ -321,74 +378,86 @@ describe(@"Streaming upload of data", ^{
spaceLeft = 11212512;
});
- it(@"should have called the completion handler with error if the first packet was not sent successfully", ^{
- for (int i = 0; i < expectedNumberOfPutFiles; i++) {
- response = [[SDLPutFileResponse alloc] init];
- response.spaceAvailable = @(spaceLeft -= 1024);
-
- if (i == 0) {
- // Only the first packet is sent unsuccessfully
- response.success = @NO;
- responseErrorDescription = @"some description";
- responseErrorReason = @"some reason";
- error = [NSError sdl_lifecycle_unknownRemoteErrorWithDescription:responseErrorDescription andReason:responseErrorReason];
- } else {
- response.success = @YES;
- error = nil;
+ context(@"when the first packet is not successful", ^{
+ beforeEach(^{
+ for (int i = 0; i < expectedNumberOfPutFiles; i++) {
+ response = [[SDLPutFileResponse alloc] init];
+ response.spaceAvailable = @(spaceLeft -= 1024);
+
+ if (i == 0) {
+ // Only the first packet is sent unsuccessfully
+ response.success = @NO;
+ responseErrorDescription = @"some description";
+ responseErrorReason = @"some reason";
+ error = [NSError sdl_lifecycle_unknownRemoteErrorWithDescription:responseErrorDescription andReason:responseErrorReason];
+ } else {
+ response.success = @YES;
+ error = nil;
+ }
+
+ [testConnectionManager respondToRequestWithResponse:response requestNumber:i error:error];
}
+ });
- [testConnectionManager respondToRequestWithResponse:response requestNumber:i error:error];
- }
-
- expect(errorResult.localizedDescription).toEventually(match(responseErrorDescription));
- expect(errorResult.localizedFailureReason).toEventually(match(responseErrorReason));
- expect(successResult).toEventually(beFalse());
+ it(@"should have called the completion handler with error", ^{
+ expect(errorResult.localizedDescription).toEventually(match(responseErrorDescription));
+ expect(errorResult.localizedFailureReason).toEventually(match(responseErrorReason));
+ expect(successResult).toEventually(beFalse());
+ });
});
- it(@"should have called the completion handler with error if the last packet was not sent successfully", ^{
- for (int i = 0; i < expectedNumberOfPutFiles; i++) {
- response = [[SDLPutFileResponse alloc] init];
- response.spaceAvailable = @(spaceLeft -= 1024);
-
- if (i == (expectedNumberOfPutFiles - 1)) {
- // Only the last packet is sent unsuccessfully
- response.success = @NO;
- responseErrorDescription = @"some description";
- responseErrorReason = @"some reason";
- error = [NSError sdl_lifecycle_unknownRemoteErrorWithDescription:responseErrorDescription andReason:responseErrorReason];
- } else {
- response.success = @YES;
- error = nil;
+ context(@"when the last packet is not successful", ^{
+ it(@"should have called the completion handler with error", ^{
+ for (int i = 0; i < expectedNumberOfPutFiles; i++) {
+ response = [[SDLPutFileResponse alloc] init];
+ response.spaceAvailable = @(spaceLeft -= 1024);
+
+ if (i == (expectedNumberOfPutFiles - 1)) {
+ // Only the last packet is sent unsuccessfully
+ response.success = @NO;
+ responseErrorDescription = @"some description";
+ responseErrorReason = @"some reason";
+ error = [NSError sdl_lifecycle_unknownRemoteErrorWithDescription:responseErrorDescription andReason:responseErrorReason];
+ } else {
+ response.success = @YES;
+ error = nil;
+ }
+
+ [testConnectionManager respondToRequestWithResponse:response requestNumber:i error:error];
}
- [testConnectionManager respondToRequestWithResponse:response requestNumber:i error:error];
- }
-
- expect(errorResult.localizedDescription).toEventually(match(responseErrorDescription));
- expect(errorResult.localizedFailureReason).toEventually(match(responseErrorReason));
- expect(successResult).toEventually(beFalse());
+ expect(errorResult.localizedDescription).toEventually(match(responseErrorDescription));
+ expect(errorResult.localizedFailureReason).toEventually(match(responseErrorReason));
+ expect(successResult).toEventually(beFalse());
+ });
});
- it(@"should have called the completion handler with error if all packets were not sent successfully", ^{
- for (int i = 0; i < expectedNumberOfPutFiles; i++) {
- response = [[SDLPutFileResponse alloc] init];
- response.success = @NO;
- response.spaceAvailable = @(spaceLeft -= 1024);
+ context(@"when all the packets are not successful", ^{
+ it(@"should have called the completion handler with error if all packets were not sent successfully", ^{
+ for (int i = 0; i < expectedNumberOfPutFiles; i++) {
+ response = [[SDLPutFileResponse alloc] init];
+ response.success = @NO;
+ response.spaceAvailable = @(spaceLeft -= 1024);
- responseErrorDescription = @"some description";
- responseErrorReason = @"some reason";
+ responseErrorDescription = @"some description";
+ responseErrorReason = @"some reason";
- [testConnectionManager respondToRequestWithResponse:response requestNumber:i error:[NSError sdl_lifecycle_unknownRemoteErrorWithDescription:responseErrorDescription andReason:responseErrorReason]];
- }
+ [testConnectionManager respondToRequestWithResponse:response requestNumber:i error:[NSError sdl_lifecycle_unknownRemoteErrorWithDescription:responseErrorDescription andReason:responseErrorReason]];
+ }
- expect(errorResult.localizedDescription).toEventually(match(responseErrorDescription));
- expect(errorResult.localizedFailureReason).toEventually(match(responseErrorReason));
- expect(successResult).toEventually(beFalse());
+ expect(errorResult.localizedDescription).toEventually(match(responseErrorDescription));
+ expect(errorResult.localizedFailureReason).toEventually(match(responseErrorReason));
+ expect(successResult).toEventually(beFalse());
+ });
});
});
});
describe(@"when an incorrect file url is passed", ^{
+ beforeEach(^{
+ OCMStub([mockFileManager fileNeedsUpload:[OCMArg any]]).andReturn(YES);
+ });
+
it(@"should have called the completion handler with an error", ^{
NSString *fileName = @"testImagePNG";
testFileName = fileName;
@@ -402,12 +471,16 @@ describe(@"Streaming upload of data", ^{
}];
testConnectionManager = [[TestConnectionManager alloc] init];
- testOperation = [[SDLUploadFileOperation alloc] initWithFile:testFileWrapper connectionManager:testConnectionManager];
+ testOperation = [[SDLUploadFileOperation alloc] initWithFile:testFileWrapper connectionManager:testConnectionManager fileManager:mockFileManager];
[testOperation start];
});
});
describe(@"when empty data is passed", ^{
+ beforeEach(^{
+ OCMStub([mockFileManager fileNeedsUpload:[OCMArg any]]).andReturn(YES);
+ });
+
it(@"should have called the completion handler with an error", ^{
testFileName = @"TestEmptyMemory";
testFileData = [@"" dataUsingEncoding:NSUTF8StringEncoding];
@@ -419,7 +492,7 @@ describe(@"Streaming upload of data", ^{
}];
testConnectionManager = [[TestConnectionManager alloc] init];
- testOperation = [[SDLUploadFileOperation alloc] initWithFile:testFileWrapper connectionManager:testConnectionManager];
+ testOperation = [[SDLUploadFileOperation alloc] initWithFile:testFileWrapper connectionManager:testConnectionManager fileManager:mockFileManager];
[testOperation start];
});
});