summaryrefslogtreecommitdiff
path: root/SmartDeviceLinkTests/DevAPISpecs/SDLUploadFileOperationSpec.m
blob: e3fb6732f1d1a9c67213e044ac7dc6e31642dc16 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
#import <Quick/Quick.h>
#import <Nimble/Nimble.h>

#import "SDLError.h"
#import "SDLFile.h"
#import "SDLFileWrapper.h"
#import "SDLGlobals.h"
#import "SDLPutFile.h"
#import "SDLPutFileResponse.h"
#import "SDLUploadFileOperation.h"
#import "SDLVersion.h"
#import "TestConnectionManager.h"
#import <zlib.h>

QuickSpecBegin(SDLUploadFileOperationSpec)

describe(@"Streaming upload of data", ^{
    __block NSString *testFileName = nil;
    __block NSData *testFileData = nil;
    __block SDLFile *testFile = nil;
    __block SDLFileWrapper *testFileWrapper = nil;
    __block NSInteger numberOfPutFiles = 0;

    __block TestConnectionManager *testConnectionManager = nil;
    __block SDLUploadFileOperation *testOperation = nil;

    __block BOOL successResult = NO;
    __block NSUInteger bytesAvailableResult = NO;
    __block NSError *errorResult = nil;

    beforeEach(^{
        [SDLGlobals sharedGlobals].maxHeadUnitProtocolVersion = [SDLVersion versionWithString:@"2.0.0"];

        testFileName = nil;
        testFileData = nil;
        testFile = nil;
        testFileWrapper = nil;
        numberOfPutFiles = 0;

        testOperation = nil;
        testConnectionManager = nil;

        successResult = NO;
        bytesAvailableResult = NO;
        errorResult = nil;
    });

    context(@"When uploading data", ^{
        context(@"data should be split into smaller packets if too large to send all at once", ^{
            context(@"both data in memory and on disk can be uploaded", ^{
                it(@"should split the data from a short chunk of text in memory correctly", ^{
                    testFileName = @"TestSmallMemory";
                    testFileData = [@"test1234" dataUsingEncoding:NSUTF8StringEncoding];
                    testFile = [SDLFile fileWithData:testFileData name:testFileName fileExtension:@"bin"];
                });

                it(@"should split the data from a large image in memory correctly", ^{
                    testFileName = @"TestLargeMemory";
                    UIImage *testImage = [UIImage imageNamed:@"testImagePNG" inBundle:[NSBundle bundleForClass:[self class]] compatibleWithTraitCollection:nil];
                    testFileData = UIImageJPEGRepresentation(testImage, 1.0);
                    testFile = [SDLFile fileWithData:testFileData name:testFileName fileExtension:@"bin"];
                });

                it(@"should split the data from a small text file correctly", ^{
                    NSString *fileName = @"testFileJSON";
                    testFileName = fileName;
                    NSString *textFilePath = [[NSBundle bundleForClass:[self class]] pathForResource:fileName ofType:@"json"];
                    NSURL *textFileURL = [[NSURL alloc] initFileURLWithPath:textFilePath];
                    testFile = [SDLFile fileAtFileURL:textFileURL name:fileName];
                    testFileData = [[NSData alloc] initWithContentsOfURL:textFileURL];
                });

                it(@"should split the data from a large image file correctly", ^{
                    NSString *fileName = @"testImagePNG";
                    testFileName = fileName;
                    NSString *imageFilePath = [[NSBundle bundleForClass:[self class]] pathForResource:fileName ofType:@"png"];
                    NSURL *imageFileURL = [[NSURL alloc] initFileURLWithPath:imageFilePath];
                    testFile = [SDLFile fileAtFileURL:imageFileURL name:fileName];

                    // For testing: get data to check if data chunks are being created correctly
                    testFileData = [[NSData alloc] initWithContentsOfURL:imageFileURL];
                });

                afterEach(^{
                    testFileWrapper = [SDLFileWrapper wrapperWithFile:testFile completionHandler:^(BOOL success, NSUInteger bytesAvailable, NSError * _Nullable error) {
                        successResult = success;
                        bytesAvailableResult = bytesAvailable;
                        errorResult = error;
                    }];

                    numberOfPutFiles = ((([testFile fileSize] - 1) / [[SDLGlobals sharedGlobals] mtuSizeForServiceType:SDLServiceTypeBulkData]) + 1);

                    testConnectionManager = [[TestConnectionManager alloc] init];
                    testOperation = [[SDLUploadFileOperation alloc] initWithFile:testFileWrapper connectionManager:testConnectionManager];
                    [testOperation start];
                });
            });

            afterEach(^{
                expect(@(testOperation.queuePriority)).to(equal(@(NSOperationQueuePriorityNormal)));

                NSArray<SDLPutFile *> *putFiles = testConnectionManager.receivedRequests;
                expect(@(putFiles.count)).to(equal(@(numberOfPutFiles)));

                // Test all packets for offset, length, and data
                for (NSUInteger index = 0; index < numberOfPutFiles; index++) {
                    SDLPutFile *putFile = putFiles[index];

                    NSUInteger mtuSize = [[SDLGlobals sharedGlobals] mtuSizeForServiceType:SDLServiceTypeBulkData];
                    NSData *testBulkFileData = [testFileData subdataWithRange:NSMakeRange((index * mtuSize), MIN(putFile.length.unsignedIntegerValue, mtuSize))];
                    unsigned long testBulkFileDataCrc = crc32(0, testBulkFileData.bytes, (uInt)testBulkFileData.length);

                    expect(putFile.offset).to(equal(@(index * mtuSize)));
                    expect(putFile.persistentFile).to(equal(@NO));
                    expect(putFile.syncFileName).to(equal(testFileName));
                    expect(putFile.bulkData).to(equal(testBulkFileData));
                    expect(putFile.crc).to(equal([NSNumber numberWithUnsignedLong:testBulkFileDataCrc]));

                    // Length is used to inform the SDL Core of the total incoming packet size
                    if (index == 0) {
                        // The first putfile sent should have the full file size
                        expect(putFile.length).to(equal(@([testFile fileSize])));
                    } else if (index == numberOfPutFiles - 1) {
                        // The last pufile contains the remaining data size
                        expect(putFile.length).to(equal(@([testFile fileSize] - (index * mtuSize))));
                    } else {
                        // All other putfiles contain the max data size for a putfile packet
                        expect(putFile.length).to(equal(@(mtuSize)));
                    }
                }
            });
        });

        afterEach(^{
            __block SDLPutFileResponse *goodResponse = nil;

            // We must do some cleanup here otherwise the unit test cases will crash
            NSInteger spaceLeft = 11212512;
            for (int i = 0; i < numberOfPutFiles; i++) {
                spaceLeft -= 1024;
                goodResponse = [[SDLPutFileResponse alloc] init];
                goodResponse.success = @YES;
                goodResponse.spaceAvailable = @(spaceLeft);
                [testConnectionManager respondToRequestWithResponse:goodResponse requestNumber:i error:nil];
            }

            expect(@(successResult)).toEventually(equal(@YES));
            expect(@(bytesAvailableResult)).toEventually(equal(spaceLeft));
            expect(errorResult).toEventually(beNil());
            expect(@(testOperation.finished)).toEventually(equal(@YES));
            expect(@(testOperation.executing)).toEventually(equal(@NO));
        });
    });

    context(@"When a response to the data upload comes back", ^{
        beforeEach(^{
            testFileName = @"TestLargeMemory";
            UIImage *testImage = [UIImage imageNamed:@"testImagePNG" inBundle:[NSBundle bundleForClass:[self class]] compatibleWithTraitCollection:nil];
            testFileData = UIImageJPEGRepresentation(testImage, 1.0);
            testFile = [SDLFile fileWithData:testFileData name:testFileName fileExtension:@"bin"];
            testFileWrapper = [SDLFileWrapper wrapperWithFile:testFile completionHandler:^(BOOL success, NSUInteger bytesAvailable, NSError * _Nullable error) {
                successResult = success;
                bytesAvailableResult = bytesAvailable;
                errorResult = error;
            }];

            NSUInteger mtuSize = [[SDLGlobals sharedGlobals] mtuSizeForServiceType:SDLServiceTypeBulkData];

            numberOfPutFiles = ((([testFile fileSize] - 1) / mtuSize) + 1);

            testConnectionManager = [[TestConnectionManager alloc] init];
            testOperation = [[SDLUploadFileOperation alloc] initWithFile:testFileWrapper connectionManager:testConnectionManager];
            [testOperation start];
        });

        context(@"If data was sent successfully", ^{
             __block SDLPutFileResponse *goodResponse = nil;
             __block NSInteger spaceLeft = 0;

            beforeEach(^{
                goodResponse = nil;
                spaceLeft = 11212512;
            });

            it(@"should have called the completion handler with success only if all packets were sent successfully", ^{
                for (int i = 0; i < numberOfPutFiles; i++) {
                    spaceLeft -= 1024;
                    goodResponse = [[SDLPutFileResponse alloc] init];
                    goodResponse.success = @YES;
                    goodResponse.spaceAvailable = @(spaceLeft);
                    [testConnectionManager respondToRequestWithResponse:goodResponse requestNumber:i error:nil];
                }
            });

            afterEach(^{
                expect(@(successResult)).toEventually(equal(@YES));
                expect(@(bytesAvailableResult)).toEventually(equal(spaceLeft));
                expect(errorResult).toEventually(beNil());

                expect(@(testOperation.finished)).toEventually(equal(@YES));
                expect(@(testOperation.executing)).toEventually(equal(@NO));
            });
        });

        context(@"If data was not sent successfully", ^{
            __block SDLPutFileResponse *response = nil;
            __block NSString *responseErrorDescription = nil;
            __block NSString *responseErrorReason = nil;
            __block NSInteger spaceLeft = 0;

            beforeEach(^{
                response = nil;
                responseErrorDescription = nil;
                responseErrorReason = nil;
                spaceLeft = 11212512;
            });

            it(@"should have called the completion handler with error if the first packet was not sent successfully", ^{
                for (int i = 0; i < numberOfPutFiles; i++) {
                    spaceLeft -= 1024;
                    response = [[SDLPutFileResponse alloc] init];
                    response.spaceAvailable = @(spaceLeft);
                    NSError *error = nil;

                    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;
                    }

                    [testConnectionManager respondToRequestWithResponse:response requestNumber:i error:error];
                }
            });

            it(@"should have called the completion handler with error if the last packet was not sent successfully", ^{
                for (int i = 0; i < numberOfPutFiles; i++) {
                    spaceLeft -= 1024;
                    response = [[SDLPutFileResponse alloc] init];
                    response.spaceAvailable = @(spaceLeft);
                    NSError *error = nil;

                    if (i == (numberOfPutFiles - 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;
                    }

                    [testConnectionManager respondToRequestWithResponse:response requestNumber:i error:error];
                }
            });

            it(@"should have called the completion handler with error if all packets were not sent successfully", ^{
                for (int i = 0; i < numberOfPutFiles; i++) {
                    spaceLeft -= 1024;
                    response = [[SDLPutFileResponse alloc] init];
                    response.success = @NO;
                    response.spaceAvailable = @(spaceLeft);

                    responseErrorDescription = @"some description";
                    responseErrorReason = @"some reason";

                    [testConnectionManager respondToRequestWithResponse:response requestNumber:i error:[NSError sdl_lifecycle_unknownRemoteErrorWithDescription:responseErrorDescription andReason:responseErrorReason]];
                }
            });

            afterEach(^{
                expect(errorResult.localizedDescription).toEventually(match(responseErrorDescription));
                expect(errorResult.localizedFailureReason).toEventually(match(responseErrorReason));
                expect(@(successResult)).toEventually(equal(@NO));
            });
        });
    });

    context(@"When an incorrect file url is passed", ^{
        beforeEach(^{
            NSString *fileName = @"testImagePNG";
            testFileName = fileName;
            NSString *imageFilePath = [[NSBundle bundleForClass:[self class]] pathForResource:fileName ofType:@"png"];
            NSURL *imageFileURL = [[NSURL alloc] initWithString:imageFilePath]; // This will fail because local file paths need to be init with initFileURLWithPath
            testFile = [SDLFile fileAtFileURL:imageFileURL name:fileName];

            testFileWrapper = [SDLFileWrapper wrapperWithFile:testFile completionHandler:^(BOOL success, NSUInteger bytesAvailable, NSError * _Nullable error) {
                successResult = success;
                bytesAvailableResult = bytesAvailable;
                errorResult = error;
            }];

            testConnectionManager = [[TestConnectionManager alloc] init];
            testOperation = [[SDLUploadFileOperation alloc] initWithFile:testFileWrapper connectionManager:testConnectionManager];
            [testOperation start];
        });

        it(@"should have called the completion handler with an error", ^{
            expect(errorResult).toEventually(equal([NSError sdl_fileManager_fileDoesNotExistError]));
            expect(@(successResult)).toEventually(equal(@NO));
        });
    });

    context(@"When empty data is passed", ^{
        beforeEach(^{
            testFileName = @"TestEmptyMemory";
            testFileData = [@"" dataUsingEncoding:NSUTF8StringEncoding];
            testFile = [SDLFile fileWithData:testFileData name:testFileName fileExtension:@"bin"];

            testFileWrapper = [SDLFileWrapper wrapperWithFile:testFile completionHandler:^(BOOL success, NSUInteger bytesAvailable, NSError * _Nullable error) {
                successResult = success;
                bytesAvailableResult = bytesAvailable;
                errorResult = error;
            }];

            testConnectionManager = [[TestConnectionManager alloc] init];
            testOperation = [[SDLUploadFileOperation alloc] initWithFile:testFileWrapper connectionManager:testConnectionManager];
            [testOperation start];
        });

        it(@"should have called the completion handler with an error", ^{
            expect(errorResult).toEventually(equal([NSError sdl_fileManager_fileDoesNotExistError]));
            expect(@(successResult)).toEventually(equal(@NO));
        });
    });
});

QuickSpecEnd