summaryrefslogtreecommitdiff
path: root/SmartDeviceLinkTests/DevAPISpecs
diff options
context:
space:
mode:
authorJoel Fischer <joeljfischer@gmail.com>2021-07-28 12:00:14 -0400
committerJoel Fischer <joeljfischer@gmail.com>2021-07-28 12:00:14 -0400
commit4d7d2713824f05d0f38e8a25210d236d7768cefe (patch)
treef81732146a6e7f0f5929a3f52257e6d8716f0294 /SmartDeviceLinkTests/DevAPISpecs
parent3e238f0ecebfb067c4dae10ff6b35364f4c6243a (diff)
parent8efb1824d61584eaa292990eaf0f72efdde411ee (diff)
downloadsdl_ios-4d7d2713824f05d0f38e8a25210d236d7768cefe.tar.gz
Merge branch 'develop' into feature/issue-1898-menu-manager-refactor
# Conflicts: # SmartDeviceLink-iOS.xcodeproj/project.pbxproj # SmartDeviceLink-iOS.xcodeproj/xcshareddata/xcschemes/SmartDeviceLink-Example-ObjC.xcscheme # SmartDeviceLink-iOS.xcodeproj/xcshareddata/xcschemes/SmartDeviceLink.xcscheme # SmartDeviceLink-iOS.xcodeproj/xcshareddata/xcschemes/SmartDeviceLinkSwift.xcscheme # SmartDeviceLink/private/SDLMenuManager.m # SmartDeviceLink/public/SDLMenuCell.m # SmartDeviceLinkTests/DevAPISpecs/SDLMenuManagerSpec.m
Diffstat (limited to 'SmartDeviceLinkTests/DevAPISpecs')
-rw-r--r--SmartDeviceLinkTests/DevAPISpecs/NSArray+ExtensionsSpec.m60
-rw-r--r--SmartDeviceLinkTests/DevAPISpecs/NSMutableDictionary+StoreSpec.m28
-rw-r--r--SmartDeviceLinkTests/DevAPISpecs/SDLAsynchronousRPCRequestOperationSpec.m8
-rw-r--r--SmartDeviceLinkTests/DevAPISpecs/SDLAudioStreamManagerSpec.m14
-rw-r--r--SmartDeviceLinkTests/DevAPISpecs/SDLChoiceCellSpec.m3
-rw-r--r--SmartDeviceLinkTests/DevAPISpecs/SDLChoiceSetManagerSpec.m669
-rw-r--r--SmartDeviceLinkTests/DevAPISpecs/SDLChoiceSetSpec.m107
-rw-r--r--SmartDeviceLinkTests/DevAPISpecs/SDLFileManagerSpec.m209
-rw-r--r--SmartDeviceLinkTests/DevAPISpecs/SDLLifecycleManagerSpec.m471
-rw-r--r--SmartDeviceLinkTests/DevAPISpecs/SDLLifecycleProtocolHandlerSpec.m20
-rw-r--r--SmartDeviceLinkTests/DevAPISpecs/SDLMenuCellSpec.m94
-rw-r--r--SmartDeviceLinkTests/DevAPISpecs/SDLPreloadChoicesOperationSpec.m156
-rw-r--r--SmartDeviceLinkTests/DevAPISpecs/SDLPresentChoiceSetOperationSpec.m29
-rw-r--r--SmartDeviceLinkTests/DevAPISpecs/SDLPresentKeyboardOperationSpec.m36
-rw-r--r--SmartDeviceLinkTests/DevAPISpecs/SDLSequentialRPCRequestOperationSpec.m15
-rw-r--r--SmartDeviceLinkTests/DevAPISpecs/SDLSoftButtonManagerSpec.m154
-rw-r--r--SmartDeviceLinkTests/DevAPISpecs/SDLStreamingAudioLifecycleManagerSpec.m25
-rw-r--r--SmartDeviceLinkTests/DevAPISpecs/SDLStreamingMediaConfigurationSpec.m52
-rw-r--r--SmartDeviceLinkTests/DevAPISpecs/SDLStreamingVideoLifecycleManagerSpec.m1097
-rw-r--r--SmartDeviceLinkTests/DevAPISpecs/SDLSystemInfoSpec.m83
-rw-r--r--SmartDeviceLinkTests/DevAPISpecs/SDLTextAndGraphicManagerSpec.m16
-rw-r--r--SmartDeviceLinkTests/DevAPISpecs/SDLUploadFileOperationSpec.m208
-rw-r--r--SmartDeviceLinkTests/DevAPISpecs/SDLVideoStreamingRangeSpec.m221
-rw-r--r--SmartDeviceLinkTests/DevAPISpecs/SDLVoiceCommandManagerSpec.m112
-rw-r--r--SmartDeviceLinkTests/DevAPISpecs/SDLVoiceCommandSpec.m10
-rw-r--r--SmartDeviceLinkTests/DevAPISpecs/SDLVoiceCommandUpdateOperationSpec.m144
26 files changed, 3282 insertions, 759 deletions
diff --git a/SmartDeviceLinkTests/DevAPISpecs/NSArray+ExtensionsSpec.m b/SmartDeviceLinkTests/DevAPISpecs/NSArray+ExtensionsSpec.m
new file mode 100644
index 000000000..7a8bf1277
--- /dev/null
+++ b/SmartDeviceLinkTests/DevAPISpecs/NSArray+ExtensionsSpec.m
@@ -0,0 +1,60 @@
+//
+// NSArray+ExtensionsSpec.m
+// SmartDeviceLinkTests
+//
+// Created by Joel Fischer on 2/22/21.
+// Copyright © 2021 smartdevicelink. All rights reserved.
+//
+
+#import "NSArray+Extensions.h"
+
+#import <Quick/Quick.h>
+#import <Nimble/Nimble.h>
+
+QuickSpecBegin(NSArray_ExtensionsSpec)
+
+describe(@"checking the dynamic hash of an array", ^{
+ __block NSArray *testArray = nil;
+
+ beforeEach(^{
+ testArray = nil;
+ });
+
+ context(@"when the array has no objects", ^{
+ beforeEach(^{
+ testArray = @[];
+ });
+
+ it(@"should return a dynamic hash of 0", ^{
+ expect(testArray.dynamicHash).to(equal(0));
+ });
+ });
+
+ context(@"when the array contains one string", ^{
+ beforeEach(^{
+ testArray = @[@"test string"];
+ });
+
+ it(@"should return a consistent dynamic hash", ^{
+ expect(testArray.dynamicHash).to(equal(testArray.dynamicHash));
+ });
+
+ it(@"should return a different hash than the normal hash function", ^{
+ expect(testArray.dynamicHash).toNot(equal(testArray.hash));
+ });
+ });
+
+ context(@"when the array contains multiple strings", ^{
+ it(@"should return different numbers depending on where the strings are in the array", ^{
+ testArray = @[@"test string", @"test string 2"];
+ NSUInteger hash1 = testArray.dynamicHash;
+
+ testArray = @[@"test string 2", @"test string"];
+ NSUInteger hash2 = testArray.dynamicHash;
+
+ expect(hash1).toNot(equal(hash2));
+ });
+ });
+});
+
+QuickSpecEnd
diff --git a/SmartDeviceLinkTests/DevAPISpecs/NSMutableDictionary+StoreSpec.m b/SmartDeviceLinkTests/DevAPISpecs/NSMutableDictionary+StoreSpec.m
index 459e14ac6..d9921b197 100644
--- a/SmartDeviceLinkTests/DevAPISpecs/NSMutableDictionary+StoreSpec.m
+++ b/SmartDeviceLinkTests/DevAPISpecs/NSMutableDictionary+StoreSpec.m
@@ -43,11 +43,12 @@ describe(@"A mutable dictionary with the storing category imported", ^{
});
it(@"should return string when called correctly", ^{
- expect([testDictionary sdl_objectForName:testKey ofClass:[NSString class]]).to(equal(testObject));
+ expect([testDictionary sdl_objectForName:testKey ofClass:[NSString class] error:nil]).to(equal(testObject));
});
+
it(@"should raise an exception when called with wrong class type", ^{
expectAction(^{
- expect([testDictionary sdl_objectForName:testKey ofClass:[NSNumber class]]).to(beNil());
+ [testDictionary sdl_objectForName:testKey ofClass:[NSNumber class] error:nil];
}).to(raiseException());
});
});
@@ -61,7 +62,7 @@ describe(@"A mutable dictionary with the storing category imported", ^{
it(@"should return nil and raise an exception when retrieved as an array", ^{
expectAction(^{
- expect([testDictionary sdl_objectsForName:testKey ofClass:[NSString class] error:nil]).to(beNil());
+ [testDictionary sdl_objectsForName:testKey ofClass:[NSString class] error:nil];
}).to(raiseException());
});
});
@@ -75,11 +76,12 @@ describe(@"A mutable dictionary with the storing category imported", ^{
});
it(@"should return the object correctly when retrieved correctly", ^{
- expect([testDictionary sdl_objectForName:testKey ofClass:[TestObject class]]).to(equal(testObject));
+ expect([testDictionary sdl_objectForName:testKey ofClass:[TestObject class] error:nil]).to(equal(testObject));
});
+
it(@"should raise an exception when retrieved as an NSNumber", ^{
expectAction(^{
- expect([testDictionary sdl_objectForName:testKey ofClass:[NSNumber class]]).to(beNil());
+ [testDictionary sdl_objectForName:testKey ofClass:[NSNumber class] error:nil];
}).to(raiseException());
});
});
@@ -94,17 +96,18 @@ describe(@"A mutable dictionary with the storing category imported", ^{
it(@"should raise an exception when retrieved as an NSString", ^{
expectAction(^{
- expect([testDictionary sdl_objectForName:testKey ofClass:[NSString class]]).to(equal(testObject));
+ [testDictionary sdl_objectForName:testKey ofClass:[NSString class] error:nil];
}).to(raiseException());
});
it(@"should return correctly when retrieved correctly", ^{
expect([testDictionary sdl_objectsForName:testKey ofClass:[NSString class] error:nil]).to(equal(testObjectArray));
});
+
it(@"should raise an exception when retrieved as an NSNumber", ^{
expectAction(^{
- expect([testDictionary sdl_objectForName:testKey ofClass:[NSNumber class] error:nil]).to(beNil());
- expect([testDictionary sdl_objectsForName:testKey ofClass:[NSNumber class] error:nil]).to(beNil());
+ [testDictionary sdl_objectForName:testKey ofClass:[NSNumber class] error:nil];
+ [testDictionary sdl_objectsForName:testKey ofClass:[NSNumber class] error:nil];
}).to(raiseException());
});
});
@@ -123,9 +126,10 @@ describe(@"A mutable dictionary with the storing category imported", ^{
it(@"should return correctly when retrieved correctly", ^{
expect([testDictionary sdl_objectsForName:testKey ofClass:[TestObject class] error:nil]).to(equal(testObjectArray));
});
+
it(@"should raise an exception when retrieved as an NSNumber", ^{
expectAction(^{
- expect([testDictionary sdl_objectsForName:testKey ofClass:[NSNumber class] error:nil]).to(beNil());
+ [testDictionary sdl_objectsForName:testKey ofClass:[NSNumber class] error:nil];
}).to(raiseException());
});
});
@@ -140,9 +144,10 @@ describe(@"A mutable dictionary with the storing category imported", ^{
it(@"should return correctly when retrieved correctly", ^{
expect([testDictionary sdl_enumForName:testKey error:nil]).to(equal(testObject));
});
+
it(@"should raise an exception when retrieved as an array", ^{
expectAction(^{
- expect([testDictionary sdl_enumsForName:testKey error:nil]).to(beNil());
+ [testDictionary sdl_enumsForName:testKey error:nil];
}).to(raiseException());
});
});
@@ -159,9 +164,10 @@ describe(@"A mutable dictionary with the storing category imported", ^{
it(@"should return an array of SDLEnum when retrieved correctly", ^{
expect([testDictionary sdl_enumsForName:testKey error:nil]).to(equal(testObjectArray));
});
+
it(@"should raise an exception when retrieved as an NSNumber", ^{
expectAction(^{
- expect([testDictionary sdl_objectForName:testKey ofClass:[NSNumber class]]).to(beNil());
+ [testDictionary sdl_objectForName:testKey ofClass:[NSNumber class] error:nil];
}).to(raiseException());
});
});
diff --git a/SmartDeviceLinkTests/DevAPISpecs/SDLAsynchronousRPCRequestOperationSpec.m b/SmartDeviceLinkTests/DevAPISpecs/SDLAsynchronousRPCRequestOperationSpec.m
index 9a80d5fd4..8376a8922 100644
--- a/SmartDeviceLinkTests/DevAPISpecs/SDLAsynchronousRPCRequestOperationSpec.m
+++ b/SmartDeviceLinkTests/DevAPISpecs/SDLAsynchronousRPCRequestOperationSpec.m
@@ -56,10 +56,9 @@ describe(@"sending asynchronous requests", ^{
}];
[testOperationQueue addOperation:testOperation];
- [NSThread sleepForTimeInterval:0.5];
- expect(testSuccess).toEventually(beTruthy());
- expect(testError).toEventually(beNil());
+ expect(testSuccess).withTimeout(3.0).toEventually(beTrue());
+ expect(testError).withTimeout(3.0).toEventually(beNil());
});
});
@@ -120,11 +119,10 @@ describe(@"sending asynchronous requests", ^{
[resultResponses addObject:response];
} completionHandler:^(BOOL success) {
expect(resultResponses).to(haveCount(3));
- expect(success).to(beFalsy());
+ expect(success).to(beFalse());
}];
[testOperationQueue addOperation:testOperation];
- [NSThread sleepForTimeInterval:0.5];
});
});
});
diff --git a/SmartDeviceLinkTests/DevAPISpecs/SDLAudioStreamManagerSpec.m b/SmartDeviceLinkTests/DevAPISpecs/SDLAudioStreamManagerSpec.m
index 373003311..3c2bcc6dd 100644
--- a/SmartDeviceLinkTests/DevAPISpecs/SDLAudioStreamManagerSpec.m
+++ b/SmartDeviceLinkTests/DevAPISpecs/SDLAudioStreamManagerSpec.m
@@ -39,13 +39,11 @@ describe(@"the audio stream manager", ^{
beforeEach(^{
[mockAudioManager clearData];
[testManager playNextWhenReady];
- // playNextWhenReady dispatches to a new thread so the test can sometimes fail due to timing issues even when using `toEventually`.
- [NSThread sleepForTimeInterval:0.1];
});
it(@"should fail to send data", ^{
- expect(mockAudioManager.dataSinceClear.length).toEventually(equal(0));
- expect(mockAudioManager.error.code).toEventually(equal(SDLAudioStreamManagerErrorNotConnected));
+ expect(mockAudioManager.dataSinceClear.length).withTimeout(3.0).toEventually(equal(0));
+ expect(mockAudioManager.error.code).withTimeout(3.0).toEventually(equal(SDLAudioStreamManagerErrorNotConnected));
});
});
});
@@ -89,13 +87,7 @@ describe(@"the audio stream manager", ^{
it(@"should be sending data", ^{
expect(testManager.isPlaying).toEventually(beTrue());
expect(mockAudioManager.dataSinceClear.length).toEventually(equal(34380));
-
- // wait for the delegate to be called when the audio finishes
- float waitTime = 1.1 + 0.25; // length of audio in testAudioFileURL + 0.25 buffer
- NSLog(@"Please wait %f for audio file to finish playing...", waitTime);
- [NSThread sleepForTimeInterval:waitTime];
-
- expect(mockAudioManager.finishedPlaying).toEventually(beTrue());
+ expect(mockAudioManager.finishedPlaying).withTimeout(3.0).toEventually(beTrue());
});
});
diff --git a/SmartDeviceLinkTests/DevAPISpecs/SDLChoiceCellSpec.m b/SmartDeviceLinkTests/DevAPISpecs/SDLChoiceCellSpec.m
index 1f4b6d469..47b6c675c 100644
--- a/SmartDeviceLinkTests/DevAPISpecs/SDLChoiceCellSpec.m
+++ b/SmartDeviceLinkTests/DevAPISpecs/SDLChoiceCellSpec.m
@@ -43,6 +43,7 @@ describe(@"an SDLChoiceCell", ^{
expect(testCell.artwork).to(beNil());
expect(testCell.secondaryArtwork).to(beNil());
expect(@(testCell.choiceId)).to(equal(@(UINT16_MAX)));
+ expect(testCell.uniqueText).to(equal(testText));
});
it(@"should initialize properly with initWithText:artwork:voiceCommands:", ^{
@@ -55,6 +56,7 @@ describe(@"an SDLChoiceCell", ^{
expect(testCell.artwork).to(equal(testArtwork));
expect(testCell.secondaryArtwork).to(beNil());
expect(@(testCell.choiceId)).to(equal(@(UINT16_MAX)));
+ expect(testCell.uniqueText).to(equal(testText));
});
it(@"should initialize properly with initWithText:secondaryText:tertiaryText:voiceCommands:artwork:secondaryArtwork:", ^{
@@ -67,6 +69,7 @@ describe(@"an SDLChoiceCell", ^{
expect(testCell.artwork).to(equal(testArtwork));
expect(testCell.secondaryArtwork).to(equal(testSecondaryArtwork));
expect(@(testCell.choiceId)).to(equal(@(UINT16_MAX)));
+ expect(testCell.uniqueText).to(equal(testText));
});
});
diff --git a/SmartDeviceLinkTests/DevAPISpecs/SDLChoiceSetManagerSpec.m b/SmartDeviceLinkTests/DevAPISpecs/SDLChoiceSetManagerSpec.m
index 321a2dde6..30c97f00a 100644
--- a/SmartDeviceLinkTests/DevAPISpecs/SDLChoiceSetManagerSpec.m
+++ b/SmartDeviceLinkTests/DevAPISpecs/SDLChoiceSetManagerSpec.m
@@ -2,39 +2,43 @@
#import <Nimble/Nimble.h>
#import <OCMock/OCMock.h>
+#import <SmartDeviceLink/SmartDeviceLink.h>
#import "SDLChoiceSetManager.h"
#import "SDLCheckChoiceVROptionalOperation.h"
-#import "SDLChoiceCell.h"
-#import "SDLChoiceSet.h"
-#import "SDLChoiceSetDelegate.h"
#import "SDLDeleteChoicesOperation.h"
-#import "SDLDisplayCapability.h"
-#import "SDLFileManager.h"
+#import "SDLError.h"
#import "SDLGlobals.h"
-#import "SDLHMILevel.h"
-#import "SDLKeyboardDelegate.h"
-#import "SDLKeyboardProperties.h"
-#import "SDLNotificationConstants.h"
-#import "SDLOnHMIStatus.h"
#import "SDLPreloadChoicesOperation.h"
#import "SDLPresentChoiceSetOperation.h"
#import "SDLPresentKeyboardOperation.h"
#import "SDLRPCNotificationNotification.h"
#import "SDLStateMachine.h"
-#import "SDLSystemContext.h"
-#import "SDLSystemCapability.h"
-#import "SDLSystemCapabilityManager.h"
-#import "SDLTextField.h"
#import "SDLVersion.h"
#import "SDLWindowCapability.h"
#import "TestConnectionManager.h"
+@interface SDLPreloadChoicesOperation()
+
+@property (copy, nonatomic, nullable) NSError *internalError;
+@property (strong, nonatomic, nullable) NSMutableArray<NSNumber *> *failedChoiceUploadIDs;
+
+@end
+
+@interface SDLChoiceCell()
+
+@property (assign, nonatomic) UInt16 choiceId;
+
+@end
@interface SDLPresentChoiceSetOperation()
@property (copy, nonatomic, nullable) NSError *internalError;
+@property (assign, nonatomic) UInt16 cancelId;
+@property (strong, nonatomic, readwrite, nullable) SDLChoiceCell *selectedCell;
+@property (strong, nonatomic, readwrite, nullable) SDLTriggerSource selectedTriggerSource;
+@property (assign, nonatomic, readwrite) NSUInteger selectedCellRow;
@end
@@ -48,6 +52,7 @@
@property (strong, nonatomic, readonly) SDLStateMachine *stateMachine;
@property (strong, nonatomic) NSOperationQueue *transactionQueue;
+@property (assign, nonatomic) UInt16 nextCancelId;
@property (copy, nonatomic, nullable) SDLHMILevel currentHMILevel;
@property (copy, nonatomic, nullable) SDLSystemContext currentSystemContext;
@@ -62,7 +67,8 @@
@property (assign, nonatomic, getter=isVROptional) BOOL vrOptional;
- (void)sdl_hmiStatusNotification:(SDLRPCNotificationNotification *)notification;
-- (void)sdl_displayCapabilityDidUpdate:(SDLSystemCapability *)systemCapability;
+- (void)sdl_displayCapabilityDidUpdate;
+- (void)sdl_addUniqueNamesToCells:(NSMutableSet<SDLChoiceCell *> *)choices;
@end
@@ -77,11 +83,16 @@ describe(@"choice set manager tests", ^{
__block SDLWindowCapability *enabledWindowCapability = nil;
__block SDLWindowCapability *disabledWindowCapability = nil;
- __block SDLWindowCapability *blankWindowCapability = nil;
+ __block SDLWindowCapability *primaryTextOnlyCapability = nil;
__block SDLChoiceCell *testCell1 = nil;
__block SDLChoiceCell *testCell2 = nil;
__block SDLChoiceCell *testCell3 = nil;
+ __block SDLChoiceCell *testCell4 = nil;
+ __block SDLChoiceCell *testCell1Duplicate = nil;
+ __block SDLChoiceCell *testCell1Similar = nil;
+ __block SDLVersion *choiceSetUniquenessActiveVersion = nil;
+ __block SDLArtwork *testArtwork = nil;
beforeEach(^{
testConnectionManager = [[TestConnectionManager alloc] init];
@@ -90,22 +101,37 @@ describe(@"choice set manager tests", ^{
testManager = [[SDLChoiceSetManager alloc] initWithConnectionManager:testConnectionManager fileManager:testFileManager systemCapabilityManager:testSystemCapabilityManager];
+ testArtwork = [[SDLArtwork alloc] initWithStaticIcon:SDLStaticIconNameKey];
testCell1 = [[SDLChoiceCell alloc] initWithText:@"test1"];
testCell2 = [[SDLChoiceCell alloc] initWithText:@"test2"];
testCell3 = [[SDLChoiceCell alloc] initWithText:@"test3"];
+ testCell4 = [[SDLChoiceCell alloc] initWithText:@"test4"];
+ testCell1Duplicate = [[SDLChoiceCell alloc] initWithText:@"test1"];
+ testCell1Similar = [[SDLChoiceCell alloc] initWithText:@"test1" secondaryText:@"secondary" tertiaryText:nil voiceCommands:nil artwork:nil secondaryArtwork:nil];
enabledWindowCapability = [[SDLWindowCapability alloc] init];
- enabledWindowCapability.textFields = @[[[SDLTextField alloc] initWithName:SDLTextFieldNameMenuName characterSet:SDLCharacterSetUtf8 width:500 rows:1]];
+ enabledWindowCapability.textFields = @[
+ [[SDLTextField alloc] initWithName:SDLTextFieldNameMenuName characterSet:SDLCharacterSetUtf8 width:500 rows:1],
+ [[SDLTextField alloc] initWithName:SDLTextFieldNameSecondaryText characterSet:SDLCharacterSetUtf8 width:500 rows:1],
+ [[SDLTextField alloc] initWithName:SDLTextFieldNameTertiaryText characterSet:SDLCharacterSetUtf8 width:500 rows:1]
+ ];
+ enabledWindowCapability.imageFields = @[
+ [[SDLImageField alloc] initWithName:SDLImageFieldNameChoiceImage imageTypeSupported:@[SDLFileTypePNG] imageResolution:nil],
+ [[SDLImageField alloc] initWithName:SDLImageFieldNameChoiceSecondaryImage imageTypeSupported:@[SDLFileTypePNG] imageResolution:nil]
+ ];
disabledWindowCapability = [[SDLWindowCapability alloc] init];
disabledWindowCapability.textFields = @[];
- blankWindowCapability = [[SDLWindowCapability alloc] init];
- blankWindowCapability.textFields = @[];
+ primaryTextOnlyCapability = [[SDLWindowCapability alloc] init];
+ primaryTextOnlyCapability.textFields = @[
+ [[SDLTextField alloc] initWithName:SDLTextFieldNameMenuName characterSet:SDLCharacterSetUtf8 width:500 rows:1],
+ ];
+ choiceSetUniquenessActiveVersion = [[SDLVersion alloc] initWithMajor:7 minor:1 patch:0];
});
it(@"should be in the correct startup state", ^{
expect(testManager.currentState).to(equal(SDLChoiceManagerStateShutdown));
- SDLKeyboardProperties *defaultProperties = [[SDLKeyboardProperties alloc] initWithLanguage:SDLLanguageEnUs keyboardLayout:SDLKeyboardLayoutQWERTY keypressMode:SDLKeypressModeResendCurrentEntry limitedCharacterList:nil autoCompleteList:nil];
+ SDLKeyboardProperties *defaultProperties = [[SDLKeyboardProperties alloc] initWithLanguage:SDLLanguageEnUs keyboardLayout:SDLKeyboardLayoutQWERTY keypressMode:SDLKeypressModeResendCurrentEntry limitedCharacterList:nil autoCompleteList:nil maskInputCharacters:nil customKeys:nil];
expect(testManager.keyboardConfiguration).to(equal(defaultProperties));
});
@@ -134,9 +160,8 @@ describe(@"choice set manager tests", ^{
it(@"should enable the queue when receiving a good window capability", ^{
testManager.currentWindowCapability = disabledWindowCapability;
-
- SDLDisplayCapability *displayCapability = [[SDLDisplayCapability alloc] initWithDisplayName:@"TEST" windowCapabilities:@[enabledWindowCapability] windowTypeSupported:nil];
- [testManager sdl_displayCapabilityDidUpdate:[[SDLSystemCapability alloc] initWithDisplayCapabilities:@[displayCapability]]];
+ OCMStub([testSystemCapabilityManager defaultMainWindowCapability]).andReturn(enabledWindowCapability);
+ [testManager sdl_displayCapabilityDidUpdate];
expect(testManager.transactionQueue.isSuspended).to(beFalse());
});
@@ -158,15 +183,15 @@ describe(@"choice set manager tests", ^{
});
it(@"should suspend the queue when receiving a bad display capability", ^{
- SDLDisplayCapability *displayCapability = [[SDLDisplayCapability alloc] initWithDisplayName:@"TEST" windowCapabilities:@[disabledWindowCapability] windowTypeSupported:nil];
- [testManager sdl_displayCapabilityDidUpdate:[[SDLSystemCapability alloc] initWithDisplayCapabilities:@[displayCapability]]];
+ OCMStub([testSystemCapabilityManager defaultMainWindowCapability]).andReturn(disabledWindowCapability);
+ [testManager sdl_displayCapabilityDidUpdate];
expect(testManager.transactionQueue.isSuspended).to(beTrue());
});
it(@"should not suspend the queue when receiving an empty display capability", ^{
- SDLDisplayCapability *displayCapability = [[SDLDisplayCapability alloc] initWithDisplayName:@"TEST" windowCapabilities:@[blankWindowCapability] windowTypeSupported:nil];
- [testManager sdl_displayCapabilityDidUpdate:[[SDLSystemCapability alloc] initWithDisplayCapabilities:@[displayCapability]]];
+ OCMStub([testSystemCapabilityManager defaultMainWindowCapability]).andReturn(disabledWindowCapability);
+ [testManager sdl_displayCapabilityDidUpdate];
expect(testManager.transactionQueue.isSuspended).to(beTrue());
});
@@ -246,7 +271,7 @@ describe(@"choice set manager tests", ^{
expect(testManager.transactionQueue.operations.firstObject).to(beAnInstanceOf([SDLPreloadChoicesOperation class]));
SDLPreloadChoicesOperation *testOp = testManager.transactionQueue.operations.firstObject;
- testOp.completionBlock();
+ [testOp finishOperation];
expect(testManager.preloadedChoices).to(contain(testCell1));
expect(testManager.preloadedChoices).to(contain(testCell2));
@@ -255,6 +280,77 @@ describe(@"choice set manager tests", ^{
});
});
+ context(@"when some choices are already uploaded with duplicate titles version >= 7.1.0", ^{
+ beforeEach(^{
+ [SDLGlobals sharedGlobals].rpcVersion = choiceSetUniquenessActiveVersion;
+ });
+
+ context(@"if there are duplicate cells once you strip unused cell properties", ^{
+ beforeEach(^{
+ testManager.currentWindowCapability = primaryTextOnlyCapability;
+ [testManager preloadChoices:@[testCell1, testCell1Similar] withCompletionHandler:^(NSError * _Nullable error) { }];
+ });
+
+ it(@"should update the choiceCells' unique title", ^{
+ SDLPreloadChoicesOperation *testOp = testManager.transactionQueue.operations.firstObject;
+ [testOp finishOperation];
+ NSArray <SDLChoiceCell *> *testArrays = testManager.preloadedChoices.allObjects;
+ for (SDLChoiceCell *choiceCell in testArrays) {
+ if (choiceCell.secondaryText) {
+ expect(choiceCell.uniqueText).to(equal("test1 (2)"));
+ } else {
+ expect(choiceCell.uniqueText).to(equal("test1"));
+ }
+ }
+ expect(testManager.preloadedChoices).to(haveCount(2));
+ expect(testManager.preloadedChoices).to(contain(testCell1));
+ expect(testManager.preloadedChoices).to(contain(testCell1Duplicate));
+ });
+ });
+
+ context(@"if all cell properties are used", ^{
+ beforeEach(^{
+ testManager.currentWindowCapability = enabledWindowCapability;
+ [testManager preloadChoices:@[testCell1, testCell1Similar] withCompletionHandler:^(NSError * _Nullable error) { }];
+ });
+
+ it(@"should not update the choiceCells' unique title", ^{
+ SDLPreloadChoicesOperation *testOp = testManager.transactionQueue.operations.firstObject;
+ [testOp finishOperation];
+ NSArray <SDLChoiceCell *> *testArrays = testManager.preloadedChoices.allObjects;
+ for (SDLChoiceCell *choiceCell in testArrays) {
+ expect(choiceCell.uniqueText).to(equal("test1"));
+ }
+ expect(testManager.preloadedChoices).to(haveCount(2));
+ expect(testManager.preloadedChoices).to(contain(testCell1));
+ expect(testManager.preloadedChoices).to(contain(testCell1Duplicate));
+ });
+ });
+ });
+
+ context(@"when some choices are already uploaded with duplicate titles version <= 7.1.0", ^{
+ beforeEach(^{
+ [SDLGlobals sharedGlobals].rpcVersion = [[SDLVersion alloc] initWithMajor:7 minor:0 patch:0];
+ [testManager preloadChoices:@[testCell1, testCell1Similar] withCompletionHandler:^(NSError * _Nullable error) { }];
+ });
+
+ it(@"append a number to the unique text for choice set cells", ^{
+ SDLPreloadChoicesOperation *testOp = testManager.transactionQueue.operations.firstObject;
+ [testOp finishOperation];
+ NSArray <SDLChoiceCell *> *testArrays = testManager.preloadedChoices.allObjects;
+ for (SDLChoiceCell *choiceCell in testArrays) {
+ if (choiceCell.secondaryText) {
+ expect(choiceCell.uniqueText).to(equal("test1 (2)"));
+ } else {
+ expect(choiceCell.uniqueText).to(equal("test1"));
+ }
+ }
+ expect(testManager.preloadedChoices).to(haveCount(2));
+ expect(testManager.preloadedChoices).to(contain(testCell1));
+ expect(testManager.preloadedChoices).to(contain(testCell1Duplicate));
+ });
+ });
+
context(@"when some choices are already pending", ^{
beforeEach(^{
testManager.pendingMutablePreloadChoices = [NSMutableSet setWithArray:@[testCell1]];
@@ -270,7 +366,7 @@ describe(@"choice set manager tests", ^{
expect(testManager.transactionQueue.operations.firstObject).to(beAnInstanceOf([SDLPreloadChoicesOperation class]));
SDLPreloadChoicesOperation *testOp = testManager.transactionQueue.operations.firstObject;
- testOp.completionBlock();
+ [testOp finishOperation];
expect(testManager.preloadedChoices).toNot(contain(testCell1));
expect(testManager.preloadedChoices).to(contain(testCell2));
@@ -283,8 +379,7 @@ describe(@"choice set manager tests", ^{
beforeEach(^{
testManager.pendingMutablePreloadChoices = [NSMutableSet setWithArray:@[testCell1]];
- [testManager preloadChoices:@[testCell1, testCell2, testCell3] withCompletionHandler:^(NSError * _Nullable error) {
- }];
+ [testManager preloadChoices:@[testCell1, testCell2, testCell3] withCompletionHandler:^(NSError * _Nullable error) {}];
});
it(@"should leave the list of pending and uploaded choice items empty when the operation finishes", ^{
@@ -298,7 +393,7 @@ describe(@"choice set manager tests", ^{
testManager.preloadedMutableChoices = [NSMutableSet set];
SDLPreloadChoicesOperation *testOp = testManager.transactionQueue.operations.firstObject;
- testOp.completionBlock();
+ [testOp finishOperation];
expect(testManager.preloadedMutableChoices).to(beEmpty());
expect(testManager.preloadedChoices).to(beEmpty());
@@ -392,98 +487,392 @@ describe(@"choice set manager tests", ^{
describe(@"presenting a choice set", ^{
__block SDLChoiceSet *testChoiceSet = nil;
+ __block SDLChoiceSet *testFailedChoiceSet = nil;
__block NSString *testTitle = @"test title";
__block id<SDLChoiceSetDelegate> choiceDelegate = nil;
__block id<SDLKeyboardDelegate> keyboardDelegate = nil;
__block SDLInteractionMode testMode = SDLInteractionModeBoth;
__block SDLPresentKeyboardOperation *pendingPresentOp = nil;
+ __block id strickMockOperationQueue = nil;
+ __block SDLChoiceCell *testSelectedCell = nil;
+ __block NSError *testError = nil;
+ NSUInteger testSelectedCellRow = 1;
beforeEach(^{
keyboardDelegate = OCMProtocolMock(@protocol(SDLKeyboardDelegate));
choiceDelegate = OCMProtocolMock(@protocol(SDLChoiceSetDelegate));
testChoiceSet = [[SDLChoiceSet alloc] initWithTitle:testTitle delegate:choiceDelegate choices:@[testCell1, testCell2, testCell3]];
+ testFailedChoiceSet = [[SDLChoiceSet alloc] initWithTitle:testTitle delegate:choiceDelegate choices:@[testCell1, testCell2, testCell3, testCell4]];
+ testSelectedCell = testChoiceSet.choices[1];
+ testError = [NSError sdl_choiceSetManager_failedToCreateMenuItems];
pendingPresentOp = OCMClassMock([SDLPresentKeyboardOperation class]);
testManager.pendingPresentOperation = pendingPresentOp;
testManager.pendingPresentationSet = [[SDLChoiceSet alloc] init];
+
+ strickMockOperationQueue = OCMStrictClassMock([NSOperationQueue class]);
+ [strickMockOperationQueue setExpectationOrderMatters:YES];
+ testManager.transactionQueue = strickMockOperationQueue;
});
context(@"searchable", ^{
- beforeEach(^{
+ it(@"should notify the choice delegate when a choice item is selected", ^{
+ OCMExpect([strickMockOperationQueue addOperation:[OCMArg checkWithBlock:^BOOL(id value) {
+ SDLPreloadChoicesOperation *preloadChoicesOperation = (SDLPreloadChoicesOperation *)value;
+ expect(testManager.pendingPresentationSet).to(equal(testChoiceSet));
+ [preloadChoicesOperation finishOperation];
+ return [value isKindOfClass:[SDLPreloadChoicesOperation class]];
+ }]]);
+ OCMExpect([strickMockOperationQueue addOperation:[OCMArg checkWithBlock:^BOOL(id value) {
+ SDLPresentChoiceSetOperation *presentChoicesOperation = (SDLPresentChoiceSetOperation *)value;
+ presentChoicesOperation.selectedCell = testSelectedCell;
+ presentChoicesOperation.selectedTriggerSource = testMode;
+ presentChoicesOperation.selectedCellRow = testSelectedCellRow;
+ presentChoicesOperation.internalError = nil;
+ presentChoicesOperation.completionBlock();
+ return [value isKindOfClass:[SDLPresentChoiceSetOperation class]];
+ }]]);
+ OCMExpect([choiceDelegate choiceSet:testChoiceSet didSelectChoice:testSelectedCell withSource:testMode atRowIndex:testSelectedCellRow]);
+
[testManager presentChoiceSet:testChoiceSet mode:testMode withKeyboardDelegate:keyboardDelegate];
+
+ OCMVerifyAllWithDelay(strickMockOperationQueue, 0.5);
+ OCMVerifyAllWithDelay(choiceDelegate, 0.5);
+
+ expect(testManager.pendingPresentationSet).to(beNil());
+ expect(testManager.pendingPresentOperation).to(beNil());
+
+ expect(testManager.preloadedMutableChoices.count).to(equal(3));
+ expect(testManager.preloadedMutableChoices).to(contain(testChoiceSet.choices[0]));
+ expect(testManager.preloadedMutableChoices).to(contain(testChoiceSet.choices[1]));
+ expect(testManager.preloadedMutableChoices).to(contain(testChoiceSet.choices[2]));
+ expect(testManager.pendingMutablePreloadChoices).to(beEmpty());
});
- it(@"should properly start the presentation", ^{
- OCMVerify([pendingPresentOp cancel]);
- expect(testManager.pendingPresentationSet).to(equal(testChoiceSet));
- expect(testManager.transactionQueue.operations).to(haveCount(2));
- expect(testManager.transactionQueue.operations.firstObject).to(beAnInstanceOf([SDLPreloadChoicesOperation class]));
- expect(testManager.transactionQueue.operations.lastObject).to(beAnInstanceOf([SDLPresentChoiceSetOperation class]));
+ it(@"should notify the choice delegate if an error occured during presentation", ^{
+ OCMExpect([strickMockOperationQueue addOperation:[OCMArg checkWithBlock:^BOOL(id value) {
+ SDLPreloadChoicesOperation *preloadChoicesOperation = (SDLPreloadChoicesOperation *)value;
+ expect(testManager.pendingPresentationSet).to(equal(testChoiceSet));
+ [preloadChoicesOperation finishOperation];
+ return [value isKindOfClass:[SDLPreloadChoicesOperation class]];
+ }]]);
+ OCMExpect([strickMockOperationQueue addOperation:[OCMArg checkWithBlock:^BOOL(id value) {
+ SDLPresentChoiceSetOperation *presentChoicesOperation = (SDLPresentChoiceSetOperation *)value;
+ presentChoicesOperation.internalError = testError;
+ presentChoicesOperation.completionBlock();
+ return [value isKindOfClass:[SDLPresentChoiceSetOperation class]];
+ }]]);
+ OCMExpect([choiceDelegate choiceSet:[OCMArg any] didReceiveError:testError]);
+
+ [testManager presentChoiceSet:testChoiceSet mode:testMode withKeyboardDelegate:keyboardDelegate];
+
+ OCMVerifyAllWithDelay(strickMockOperationQueue, 0.5);
+ OCMVerifyAllWithDelay(choiceDelegate, 0.5);
+
+ expect(testManager.pendingPresentationSet).to(beNil());
+ expect(testManager.pendingPresentOperation).to(beNil());
+
+ expect(testManager.preloadedMutableChoices.count).to(equal(3));
+ expect(testManager.preloadedMutableChoices).to(contain(testChoiceSet.choices[0]));
+ expect(testManager.preloadedMutableChoices).to(contain(testChoiceSet.choices[1]));
+ expect(testManager.preloadedMutableChoices).to(contain(testChoiceSet.choices[2]));
+ expect(testManager.pendingMutablePreloadChoices).to(beEmpty());
});
- describe(@"after the completion handler is called", ^{
- context(@"with an error", ^{
- beforeEach(^{
- SDLPresentChoiceSetOperation *op = testManager.transactionQueue.operations.lastObject;
- op.internalError = [[NSError alloc] init];
- op.completionBlock();
- });
-
- it(@"should call the error handler", ^{
- OCMVerify([choiceDelegate choiceSet:[OCMArg any] didReceiveError:[OCMArg isNotNil]]);
- expect(testManager.pendingPresentationSet).to(beNil());
- expect(testManager.pendingPresentOperation).to(beNil());
- });
- });
+ it(@"should not add a choice item that fails to the list of preloaded choices", ^{
+ NSMutableDictionary<SDLRPCRequest *, NSError *> *testErrors = [NSMutableDictionary dictionary];
+ SDLCreateInteractionChoiceSet *failedChoiceSet = [[SDLCreateInteractionChoiceSet alloc] initWithId:0 choiceSet:@[[[SDLChoice alloc] initWithId:1 menuName:@"1" vrCommands:nil]]];
+ testErrors[failedChoiceSet] = [NSError sdl_choiceSetManager_choiceUploadFailed:[NSDictionary dictionary]];
+ NSError *testInternalError = [NSError sdl_choiceSetManager_choiceUploadFailed:testErrors];
+
+ OCMExpect([strickMockOperationQueue addOperation:[OCMArg checkWithBlock:^BOOL(id value) {
+ SDLPreloadChoicesOperation *preloadChoicesOperation = (SDLPreloadChoicesOperation *)value;
+ expect(testManager.pendingMutablePreloadChoices.count).to(equal(4));
+ expect(testManager.pendingMutablePreloadChoices).to(contain(testFailedChoiceSet.choices[0]));
+ expect(testManager.pendingMutablePreloadChoices).to(contain(testFailedChoiceSet.choices[1]));
+ expect(testManager.pendingMutablePreloadChoices).to(contain(testFailedChoiceSet.choices[2]));
+ expect(testManager.pendingMutablePreloadChoices).to(contain(testFailedChoiceSet.choices[3]));
+ preloadChoicesOperation.internalError = testInternalError;
+ preloadChoicesOperation.failedChoiceUploadIDs = [[NSMutableArray alloc] initWithArray:(@[@1])];
+ [preloadChoicesOperation finishOperation];
+ return [value isKindOfClass:[SDLPreloadChoicesOperation class]];
+ }]]);
+ OCMReject([strickMockOperationQueue addOperation:[OCMArg isKindOfClass:SDLPresentChoiceSetOperation.class]]);
+ OCMExpect([choiceDelegate choiceSet:[OCMArg any] didReceiveError:testInternalError]);
+
+ [testManager presentChoiceSet:testFailedChoiceSet mode:testMode withKeyboardDelegate:keyboardDelegate];
+
+ OCMVerifyAllWithDelay(strickMockOperationQueue, 0.5);
+ OCMVerifyAllWithDelay(choiceDelegate, 0.5);
+
+ expect(testManager.pendingPresentationSet).toNot(beNil());
+ expect(testManager.pendingPresentOperation).toNot(beNil());
+
+ expect(testManager.preloadedMutableChoices.count).to(equal(3));
+ expect(testManager.preloadedMutableChoices).toNot(contain(testFailedChoiceSet.choices[0]));
+ expect(testManager.preloadedMutableChoices).to(contain(testFailedChoiceSet.choices[1]));
+ expect(testManager.preloadedMutableChoices).to(contain(testFailedChoiceSet.choices[2]));
+ expect(testManager.preloadedMutableChoices).to(contain(testFailedChoiceSet.choices[3]));
+
+ expect(testManager.pendingMutablePreloadChoices).to(beEmpty());
+ });
+
+ it(@"should not add any of choice items if they all fail to upload to the list of preloaded choices", ^{
+ NSMutableDictionary<SDLRPCRequest *, NSError *> *testErrors = [NSMutableDictionary dictionary];
+ SDLCreateInteractionChoiceSet *failedChoiceSet1 = [[SDLCreateInteractionChoiceSet alloc] initWithId:0 choiceSet:@[[[SDLChoice alloc] initWithId:1 menuName:@"1" vrCommands:nil]]];
+ SDLCreateInteractionChoiceSet *failedChoiceSet2 = [[SDLCreateInteractionChoiceSet alloc] initWithId:0 choiceSet:@[[[SDLChoice alloc] initWithId:2 menuName:@"2" vrCommands:nil]]];
+ SDLCreateInteractionChoiceSet *failedChoiceSet3 = [[SDLCreateInteractionChoiceSet alloc] initWithId:0 choiceSet:@[[[SDLChoice alloc] initWithId:3 menuName:@"3" vrCommands:nil]]];
+ SDLCreateInteractionChoiceSet *failedChoiceSet4 = [[SDLCreateInteractionChoiceSet alloc] initWithId:0 choiceSet:@[[[SDLChoice alloc] initWithId:4 menuName:@"4" vrCommands:nil]]];
+ testErrors[failedChoiceSet1] = [NSError sdl_choiceSetManager_choiceUploadFailed:[NSDictionary dictionary]];
+ testErrors[failedChoiceSet2] = [NSError sdl_choiceSetManager_choiceUploadFailed:[NSDictionary dictionary]];
+ testErrors[failedChoiceSet3] = [NSError sdl_choiceSetManager_choiceUploadFailed:[NSDictionary dictionary]];
+ testErrors[failedChoiceSet4] = [NSError sdl_choiceSetManager_choiceUploadFailed:[NSDictionary dictionary]];
+ NSError *testInternalError = [NSError sdl_choiceSetManager_choiceUploadFailed:testErrors];
+
+ OCMExpect([strickMockOperationQueue addOperation:[OCMArg checkWithBlock:^BOOL(id value) {
+ SDLPreloadChoicesOperation *preloadChoicesOperation = (SDLPreloadChoicesOperation *)value;
+ expect(testManager.pendingMutablePreloadChoices.count).to(equal(4));
+ expect(testManager.pendingMutablePreloadChoices).to(contain(testFailedChoiceSet.choices[0]));
+ expect(testManager.pendingMutablePreloadChoices).to(contain(testFailedChoiceSet.choices[1]));
+ expect(testManager.pendingMutablePreloadChoices).to(contain(testFailedChoiceSet.choices[2]));
+ expect(testManager.pendingMutablePreloadChoices).to(contain(testFailedChoiceSet.choices[3]));
+ preloadChoicesOperation.internalError = testInternalError;
+ preloadChoicesOperation.failedChoiceUploadIDs = [[NSMutableArray alloc] initWithArray:(@[@1, @2, @3, @4])];
+ [preloadChoicesOperation finishOperation];
+ return [value isKindOfClass:[SDLPreloadChoicesOperation class]];
+ }]]);
+ OCMReject([strickMockOperationQueue addOperation:[OCMArg isKindOfClass:SDLPresentChoiceSetOperation.class]]);
+ OCMExpect([choiceDelegate choiceSet:[OCMArg any] didReceiveError:testInternalError]);
+
+ [testManager presentChoiceSet:testFailedChoiceSet mode:testMode withKeyboardDelegate:keyboardDelegate];
+
+ OCMVerifyAllWithDelay(strickMockOperationQueue, 1.0);
+ OCMVerifyAllWithDelay(choiceDelegate, 0.5);
+
+ expect(testManager.pendingPresentationSet).toNot(beNil());
+ expect(testManager.pendingPresentOperation).toNot(beNil());
+
+ expect(testManager.preloadedMutableChoices).to(beEmpty());
+ expect(testManager.pendingMutablePreloadChoices).to(beEmpty());
});
});
+ it(@"should skip preloading the choices if all choice items have already been uploaded", ^{
+ OCMExpect([strickMockOperationQueue addOperation:[OCMArg checkWithBlock:^BOOL(id value) {
+ SDLPreloadChoicesOperation *preloadChoicesOperation = (SDLPreloadChoicesOperation *)value;
+ expect(testManager.pendingMutablePreloadChoices.count).to(equal(3));
+ expect(testManager.pendingMutablePreloadChoices).to(contain(testChoiceSet.choices[0]));
+ expect(testManager.pendingMutablePreloadChoices).to(contain(testChoiceSet.choices[1]));
+ expect(testManager.pendingMutablePreloadChoices).to(contain(testChoiceSet.choices[2]));
+ [preloadChoicesOperation finishOperation];
+ return [value isKindOfClass:[SDLPreloadChoicesOperation class]];
+ }]]);
+ OCMExpect([strickMockOperationQueue addOperation:[OCMArg checkWithBlock:^BOOL(id value) {
+ SDLPresentChoiceSetOperation *presentChoicesOperation = (SDLPresentChoiceSetOperation *)value;
+ presentChoicesOperation.selectedCell = testSelectedCell;
+ presentChoicesOperation.selectedTriggerSource = testMode;
+ presentChoicesOperation.selectedCellRow = testSelectedCellRow;
+ presentChoicesOperation.internalError = nil;
+ presentChoicesOperation.completionBlock();
+ return [value isKindOfClass:[SDLPresentChoiceSetOperation class]];
+ }]]);
+ OCMExpect([choiceDelegate choiceSet:testChoiceSet didSelectChoice:testSelectedCell withSource:testMode atRowIndex:testSelectedCellRow]);
+
+ [testManager presentChoiceSet:testChoiceSet mode:testMode withKeyboardDelegate:keyboardDelegate];
+
+ OCMVerifyAllWithDelay(strickMockOperationQueue, 0.5);
+ OCMVerifyAllWithDelay(choiceDelegate, 0.5);
+
+ expect(testManager.pendingPresentationSet).to(beNil());
+ expect(testManager.pendingPresentOperation).to(beNil());
+
+ expect(testManager.preloadedMutableChoices.count).to(equal(3));
+ expect(testManager.pendingMutablePreloadChoices).to(beEmpty());
+
+ // Present the exact same choices again
+ OCMReject([strickMockOperationQueue addOperation:[OCMArg isKindOfClass:SDLPreloadChoicesOperation.class]]);
+ OCMExpect([strickMockOperationQueue addOperation:[OCMArg checkWithBlock:^BOOL(id value) {
+ SDLPresentChoiceSetOperation *presentChoicesOperation = (SDLPresentChoiceSetOperation *)value;
+ presentChoicesOperation.selectedCell = testSelectedCell;
+ presentChoicesOperation.selectedTriggerSource = testMode;
+ presentChoicesOperation.selectedCellRow = testSelectedCellRow;
+ presentChoicesOperation.internalError = nil;
+ presentChoicesOperation.completionBlock();
+ return [value isKindOfClass:[SDLPresentChoiceSetOperation class]];
+ }]]);
+ OCMExpect([choiceDelegate choiceSet:testChoiceSet didSelectChoice:testSelectedCell withSource:testMode atRowIndex:testSelectedCellRow]);
+
+ [testManager presentChoiceSet:testChoiceSet mode:testMode withKeyboardDelegate:keyboardDelegate];
+
+ OCMVerifyAllWithDelay(strickMockOperationQueue, 0.5);
+ OCMVerifyAllWithDelay(choiceDelegate, 0.5);
+ });
+
+ it(@"should upload choices that failed to upload in previous presentations", ^{
+ NSMutableDictionary<SDLRPCRequest *, NSError *> *testErrors = [NSMutableDictionary dictionary];
+ SDLCreateInteractionChoiceSet *failedChoiceSet = [[SDLCreateInteractionChoiceSet alloc] initWithId:0 choiceSet:@[[[SDLChoice alloc] initWithId:1 menuName:@"1" vrCommands:nil]]];
+ testErrors[failedChoiceSet] = [NSError sdl_choiceSetManager_choiceUploadFailed:[NSDictionary dictionary]];
+ NSError *testInternalError = [NSError sdl_choiceSetManager_choiceUploadFailed:testErrors];
+
+ OCMExpect([strickMockOperationQueue addOperation:[OCMArg checkWithBlock:^BOOL(id value) {
+ SDLPreloadChoicesOperation *preloadChoicesOperation = (SDLPreloadChoicesOperation *)value;
+ expect(testManager.pendingMutablePreloadChoices.count).to(equal(4));
+ expect(testManager.pendingMutablePreloadChoices).to(contain(testFailedChoiceSet.choices[0]));
+ expect(testManager.pendingMutablePreloadChoices).to(contain(testFailedChoiceSet.choices[1]));
+ expect(testManager.pendingMutablePreloadChoices).to(contain(testFailedChoiceSet.choices[2]));
+ expect(testManager.pendingMutablePreloadChoices).to(contain(testFailedChoiceSet.choices[3]));
+ expect(testManager.pendingPresentationSet).to(equal(testFailedChoiceSet));
+ preloadChoicesOperation.internalError = testInternalError;
+ preloadChoicesOperation.failedChoiceUploadIDs = [[NSMutableArray alloc] initWithArray:(@[@1])];
+ [preloadChoicesOperation finishOperation];
+ return [value isKindOfClass:[SDLPreloadChoicesOperation class]];
+ }]]);
+ OCMExpect([choiceDelegate choiceSet:[OCMArg any] didReceiveError:testInternalError]);
+
+ [testManager presentChoiceSet:testFailedChoiceSet mode:testMode withKeyboardDelegate:keyboardDelegate];
+
+ OCMVerifyAllWithDelay(strickMockOperationQueue, 1.0);
+ OCMVerifyAllWithDelay(choiceDelegate, 0.5);
+
+ expect(testManager.pendingPresentationSet).toNot(beNil());
+ expect(testManager.pendingPresentOperation).toNot(beNil());
+
+ expect(testManager.preloadedMutableChoices.count).to(equal(3));
+ expect(testManager.preloadedMutableChoices).toNot(contain(testFailedChoiceSet.choices[0]));
+ expect(testManager.preloadedMutableChoices).to(contain(testFailedChoiceSet.choices[1]));
+ expect(testManager.preloadedMutableChoices).to(contain(testFailedChoiceSet.choices[2]));
+ expect(testManager.preloadedMutableChoices).to(contain(testFailedChoiceSet.choices[3]));
+ expect(testManager.pendingMutablePreloadChoices).to(beEmpty());
+
+ // Present the exact same choices again
+ OCMExpect([strickMockOperationQueue addOperation:[OCMArg checkWithBlock:^BOOL(id value) {
+ SDLPreloadChoicesOperation *preloadChoicesOperation = (SDLPreloadChoicesOperation *)value;
+ expect(testManager.pendingMutablePreloadChoices.count).to(equal(1));
+ expect(testManager.pendingMutablePreloadChoices).to(contain(testFailedChoiceSet.choices[0]));
+ [preloadChoicesOperation finishOperation];
+ return [value isKindOfClass:[SDLPreloadChoicesOperation class]];
+ }]]);
+ OCMExpect([strickMockOperationQueue addOperation:[OCMArg checkWithBlock:^BOOL(id value) {
+ SDLPresentChoiceSetOperation *presentChoicesOperation = (SDLPresentChoiceSetOperation *)value;
+ presentChoicesOperation.selectedCell = testSelectedCell;
+ presentChoicesOperation.selectedTriggerSource = testMode;
+ presentChoicesOperation.selectedCellRow = testSelectedCellRow;
+ presentChoicesOperation.internalError = nil;
+ presentChoicesOperation.completionBlock();
+ return [value isKindOfClass:[SDLPresentChoiceSetOperation class]];
+ }]]);
+ OCMExpect([choiceDelegate choiceSet:testChoiceSet didSelectChoice:testSelectedCell withSource:testMode atRowIndex:testSelectedCellRow]);
+
+ [testManager presentChoiceSet:testChoiceSet mode:testMode withKeyboardDelegate:keyboardDelegate];
+
+ OCMVerifyAllWithDelay(strickMockOperationQueue, 0.5);
+ OCMVerifyAllWithDelay(choiceDelegate, 0.5);
+
+ expect(testManager.pendingPresentationSet).to(beNil());
+ expect(testManager.pendingPresentOperation).to(beNil());
+
+ expect(testManager.preloadedMutableChoices.count).to(equal(4));
+ expect(testManager.preloadedMutableChoices).to(contain(testFailedChoiceSet.choices[0]));
+ expect(testManager.preloadedMutableChoices).to(contain(testFailedChoiceSet.choices[1]));
+ expect(testManager.preloadedMutableChoices).to(contain(testFailedChoiceSet.choices[2]));
+ expect(testManager.preloadedMutableChoices).to(contain(testFailedChoiceSet.choices[3]));
+ expect(testManager.pendingMutablePreloadChoices).to(beEmpty());
+ });
+
+ it(@"should not present choices if the manager shuts down after the choices are uploaded but before presentation", ^{
+ OCMExpect([strickMockOperationQueue addOperation:[OCMArg checkWithBlock:^BOOL(id value) {
+ SDLPreloadChoicesOperation *preloadChoicesOperation = (SDLPreloadChoicesOperation *)value;
+ expect(testManager.pendingPresentationSet).to(equal(testChoiceSet));
+ [preloadChoicesOperation finishOperation];
+ [testManager.stateMachine setToState:SDLChoiceManagerStateShutdown fromOldState:nil callEnterTransition:NO];
+ return [value isKindOfClass:[SDLPreloadChoicesOperation class]];
+ }]]);
+ OCMReject([strickMockOperationQueue addOperation:[OCMArg isKindOfClass:SDLPresentChoiceSetOperation.class]]);
+
+ [testManager presentChoiceSet:testChoiceSet mode:testMode withKeyboardDelegate:nil];
+
+ OCMVerifyAllWithDelay(strickMockOperationQueue, 0.5);
+
+ expect(testManager.pendingPresentOperation).toEventually(beNil());
+ expect(testManager.pendingPresentationSet).toEventually(beNil());
+ });
+
context(@"non-searchable", ^{
- beforeEach(^{
+ it(@"should notify the choice delegate when a choice item is selected", ^{
+ OCMExpect([strickMockOperationQueue addOperation:[OCMArg checkWithBlock:^BOOL(id value) {
+ SDLPreloadChoicesOperation *preloadChoicesOperation = (SDLPreloadChoicesOperation *)value;
+ expect(testManager.pendingPresentationSet).to(equal(testChoiceSet));
+ [preloadChoicesOperation finishOperation];
+ return [value isKindOfClass:[SDLPreloadChoicesOperation class]];
+ }]]);
+ OCMExpect([strickMockOperationQueue addOperation:[OCMArg checkWithBlock:^BOOL(id value) {
+ SDLPresentChoiceSetOperation *presentChoicesOperation = (SDLPresentChoiceSetOperation *)value;
+ presentChoicesOperation.selectedCell = testSelectedCell;
+ presentChoicesOperation.selectedTriggerSource = testMode;
+ presentChoicesOperation.selectedCellRow = testSelectedCellRow;
+ presentChoicesOperation.internalError = nil;
+ presentChoicesOperation.completionBlock();
+ return [value isKindOfClass:[SDLPresentChoiceSetOperation class]];
+ }]]);
+ OCMExpect([choiceDelegate choiceSet:testChoiceSet didSelectChoice:testSelectedCell withSource:testMode atRowIndex:testSelectedCellRow]);
+
[testManager presentChoiceSet:testChoiceSet mode:testMode withKeyboardDelegate:nil];
- });
- it(@"should properly start the presentation", ^{
- OCMVerify([pendingPresentOp cancel]);
- expect(testManager.pendingPresentationSet).to(equal(testChoiceSet));
- expect(testManager.transactionQueue.operations).to(haveCount(2));
- expect(testManager.transactionQueue.operations.firstObject).to(beAnInstanceOf([SDLPreloadChoicesOperation class]));
- expect(testManager.transactionQueue.operations.lastObject).to(beAnInstanceOf([SDLPresentChoiceSetOperation class]));
+ OCMVerifyAllWithDelay(strickMockOperationQueue, 0.5);
+ OCMVerifyAllWithDelay(choiceDelegate, 0.5);
+
+ expect(testManager.pendingPresentationSet).to(beNil());
+ expect(testManager.pendingPresentOperation).to(beNil());
});
- describe(@"after the completion handler is called", ^{
- context(@"with an error", ^{
- beforeEach(^{
- SDLPresentChoiceSetOperation *op = testManager.transactionQueue.operations.lastObject;
- op.internalError = [[NSError alloc] init];
- op.completionBlock();
- });
-
- it(@"should call the error handler", ^{
- OCMVerify([choiceDelegate choiceSet:[OCMArg any] didReceiveError:[OCMArg isNotNil]]);
- expect(testManager.pendingPresentationSet).to(beNil());
- expect(testManager.pendingPresentOperation).to(beNil());
- });
- });
+ it(@"should notify the choice delegate if an error occured during presentation", ^{
+ OCMExpect([strickMockOperationQueue addOperation:[OCMArg checkWithBlock:^BOOL(id value) {
+ SDLPreloadChoicesOperation *preloadChoicesOperation = (SDLPreloadChoicesOperation *)value;
+ expect(testManager.pendingPresentationSet).to(equal(testChoiceSet));
+ [preloadChoicesOperation finishOperation];
+ return [value isKindOfClass:[SDLPreloadChoicesOperation class]];
+ }]]);
+ OCMExpect([strickMockOperationQueue addOperation:[OCMArg checkWithBlock:^BOOL(id value) {
+ SDLPresentChoiceSetOperation *presentChoicesOperation = (SDLPresentChoiceSetOperation *)value;
+ presentChoicesOperation.internalError = testError;
+ presentChoicesOperation.completionBlock();
+ return [value isKindOfClass:[SDLPresentChoiceSetOperation class]];
+ }]]);
+ OCMExpect([choiceDelegate choiceSet:[OCMArg any] didReceiveError:testError]);
+
+ [testManager presentChoiceSet:testChoiceSet mode:testMode withKeyboardDelegate:nil];
+
+ OCMVerifyAllWithDelay(strickMockOperationQueue, 0.5);
+ OCMVerifyAllWithDelay(choiceDelegate, 0.5);
+
+ expect(testManager.pendingPresentationSet).to(beNil());
+ expect(testManager.pendingPresentOperation).to(beNil());
});
});
describe(@"when the manager shuts down during presentation", ^{
- beforeEach(^{
- [testManager presentChoiceSet:testChoiceSet mode:testMode withKeyboardDelegate:keyboardDelegate];
- });
+ __block SDLPresentChoiceSetOperation *presentChoicesOperation = nil;
it(@"should leave the list of pending and uploaded choice items empty when the operation finishes", ^{
- expect(testManager.pendingPresentationSet).to(equal(testChoiceSet));
- expect(testManager.transactionQueue.operations).to(haveCount(2));
- expect(testManager.transactionQueue.operations.firstObject).to(beAnInstanceOf([SDLPreloadChoicesOperation class]));
- expect(testManager.transactionQueue.operations.lastObject).to(beAnInstanceOf([SDLPresentChoiceSetOperation class]));
-
- [testManager.stateMachine setToState:SDLChoiceManagerStateShutdown fromOldState:nil callEnterTransition:NO];
- testManager.pendingMutablePreloadChoices = [NSMutableSet set];
- testManager.preloadedMutableChoices = [NSMutableSet set];
+ OCMExpect([strickMockOperationQueue addOperation:[OCMArg checkWithBlock:^BOOL(id value) {
+ SDLPreloadChoicesOperation *preloadChoicesOperation = (SDLPreloadChoicesOperation *)value;
+ expect(testManager.pendingPresentationSet).to(equal(testChoiceSet));
+ [preloadChoicesOperation finishOperation];
+ return [value isKindOfClass:[SDLPreloadChoicesOperation class]];
+ }]]);
+ OCMExpect([strickMockOperationQueue addOperation:[OCMArg checkWithBlock:^BOOL(id value) {
+ presentChoicesOperation = (SDLPresentChoiceSetOperation *)value;
+ presentChoicesOperation.internalError = nil;
+ testManager.pendingMutablePreloadChoices = [NSMutableSet set];
+ testManager.preloadedMutableChoices = [NSMutableSet set];
+
+ [testManager.stateMachine setToState:SDLChoiceManagerStateShutdown fromOldState:nil callEnterTransition:NO];
+ presentChoicesOperation.completionBlock();
+ return [value isKindOfClass:[SDLPresentChoiceSetOperation class]];
+ }]]);
- testManager.transactionQueue.operations.lastObject.completionBlock();
+ [testManager presentChoiceSet:testChoiceSet mode:testMode withKeyboardDelegate:keyboardDelegate];
+ OCMVerifyAllWithDelay(strickMockOperationQueue, 0.5);
expect(testManager.preloadedMutableChoices).to(beEmpty());
expect(testManager.preloadedChoices).to(beEmpty());
@@ -491,6 +880,10 @@ describe(@"choice set manager tests", ^{
expect(testManager.pendingPreloadChoices).to(beEmpty());
});
});
+
+ afterEach(^{
+ [strickMockOperationQueue stopMocking];
+ });
});
describe(@"presenting a keyboard", ^{
@@ -526,17 +919,93 @@ describe(@"choice set manager tests", ^{
});
});
+ describe(@"generating a cancel id", ^{
+ __block SDLChoiceSet *testChoiceSet = nil;
+ __block SDLChoiceSet *testChoiceSet2 = nil;
+ __block id<SDLChoiceSetDelegate> testChoiceDelegate = nil;
+ __block id strickMockOperationQueue = nil;
+
+ beforeEach(^{
+ testChoiceDelegate = OCMProtocolMock(@protocol(SDLChoiceSetDelegate));
+ testChoiceSet = [[SDLChoiceSet alloc] initWithTitle:@"choice set 1" delegate:testChoiceDelegate choices:@[testCell1]];
+ testChoiceSet2 = [[SDLChoiceSet alloc] initWithTitle:@"choice set 2" delegate:testChoiceDelegate choices:@[testCell2]];
+ testManager = [[SDLChoiceSetManager alloc] initWithConnectionManager:testConnectionManager fileManager:testFileManager systemCapabilityManager:testSystemCapabilityManager];
+ [testManager.stateMachine setToState:SDLChoiceManagerStateReady fromOldState:SDLChoiceManagerStateCheckingVoiceOptional callEnterTransition:NO];
+ strickMockOperationQueue = OCMStrictClassMock([NSOperationQueue class]);
+ testManager.transactionQueue = strickMockOperationQueue;
+ });
+
+ it(@"should set the first cancelID correctly", ^{
+ OCMExpect([strickMockOperationQueue addOperation:[OCMArg checkWithBlock:^BOOL(id value) {
+ SDLPreloadChoicesOperation *preloadChoicesOperation = (SDLPreloadChoicesOperation *)value;
+ [preloadChoicesOperation finishOperation];
+ return [value isKindOfClass:[SDLPreloadChoicesOperation class]];
+ }]]);
+
+ OCMExpect([strickMockOperationQueue addOperation:[OCMArg checkWithBlock:^BOOL(id value) {
+ SDLPresentChoiceSetOperation *presentChoicesOperation = (SDLPresentChoiceSetOperation *)value;
+ expect(@(presentChoicesOperation.cancelId)).to(equal(101));
+ presentChoicesOperation.completionBlock();
+ return [value isKindOfClass:[SDLPresentChoiceSetOperation class]];
+ }]]);
+
+ [testManager presentChoiceSet:testChoiceSet mode:SDLInteractionModeBoth withKeyboardDelegate:nil];
+
+ OCMVerifyAllWithDelay(strickMockOperationQueue, 0.5);
+ });
+
+ it(@"should reset the cancelID correctly once the max has been reached", ^{
+ testManager.nextCancelId = 200; // set the max cancelID
+
+ OCMExpect([strickMockOperationQueue addOperation:[OCMArg checkWithBlock:^BOOL(id value) {
+ SDLPreloadChoicesOperation *preloadChoicesOperation = (SDLPreloadChoicesOperation *)value;
+ [preloadChoicesOperation finishOperation];
+ return [value isKindOfClass:[SDLPreloadChoicesOperation class]];
+ }]]);
+ OCMExpect([strickMockOperationQueue addOperation:[OCMArg checkWithBlock:^BOOL(id value) {
+ SDLPresentChoiceSetOperation *presentChoicesOperation = (SDLPresentChoiceSetOperation *)value;
+ expect(@(presentChoicesOperation.cancelId)).to(equal(200));
+ presentChoicesOperation.completionBlock();
+ return [value isKindOfClass:[SDLPresentChoiceSetOperation class]];
+ }]]);
+
+ [testManager presentChoiceSet:testChoiceSet mode:SDLInteractionModeBoth withKeyboardDelegate:nil];
+
+ OCMVerifyAllWithDelay(strickMockOperationQueue, 0.5);
+
+ OCMExpect([strickMockOperationQueue addOperation:[OCMArg checkWithBlock:^BOOL(id value) {
+ SDLPreloadChoicesOperation *preloadChoicesOperation = (SDLPreloadChoicesOperation *)value;
+ [preloadChoicesOperation finishOperation];
+ return [value isKindOfClass:[SDLPreloadChoicesOperation class]];
+ }]]);
+ OCMExpect([strickMockOperationQueue addOperation:[OCMArg checkWithBlock:^BOOL(id value) {
+ SDLPresentChoiceSetOperation *presentChoicesOperation = (SDLPresentChoiceSetOperation *)value;
+ expect(@(presentChoicesOperation.cancelId)).to(equal(101));
+ presentChoicesOperation.completionBlock();
+ return [value isKindOfClass:[SDLPresentChoiceSetOperation class]];
+ }]]);
+
+ [testManager presentChoiceSet:testChoiceSet2 mode:SDLInteractionModeBoth withKeyboardDelegate:nil];
+
+ OCMVerifyAllWithDelay(strickMockOperationQueue, 0.5);
+ });
+
+ afterEach(^{
+ [strickMockOperationQueue stopMocking];
+ });
+ });
+
describe(@"dismissing the keyboard", ^{
- __block SDLPresentKeyboardOperation *mockKeyboardOp1 = nil;
- __block SDLPresentKeyboardOperation *mockKeyboardOp2 = nil;
+ __block id mockKeyboardOp1 = nil;
+ __block id mockKeyboardOp2 = nil;
__block NSOperationQueue *mockQueue = nil;
__block UInt16 testCancelId = 387;
beforeEach(^{
- mockKeyboardOp1 = OCMPartialMock([[SDLPresentKeyboardOperation alloc] init]);
+ mockKeyboardOp1 = OCMStrictClassMock([SDLPresentKeyboardOperation class]);
OCMStub([mockKeyboardOp1 cancelId]).andReturn(88);
- mockKeyboardOp2 = OCMPartialMock([[SDLPresentKeyboardOperation alloc] init]);
+ mockKeyboardOp2 = OCMStrictClassMock([SDLPresentKeyboardOperation class]);
OCMStub([mockKeyboardOp2 cancelId]).andReturn(testCancelId);
mockQueue = OCMPartialMock([[NSOperationQueue alloc] init]);
@@ -547,19 +1016,29 @@ describe(@"choice set manager tests", ^{
});
it(@"should dismiss the keyboard operation with the matching cancelID if it is executing", ^{
+ OCMStub([mockKeyboardOp1 isExecuting]).andReturn(true);
OCMStub([mockKeyboardOp2 isExecuting]).andReturn(true);
- [testManager dismissKeyboardWithCancelID:@(testCancelId)];
OCMReject([mockKeyboardOp1 dismissKeyboard]);
- OCMVerify([mockKeyboardOp2 dismissKeyboard]);
+ OCMExpect([mockKeyboardOp2 dismissKeyboard]);
+
+ [testManager dismissKeyboardWithCancelID:@(testCancelId)];
+
+ OCMVerifyAllWithDelay(mockKeyboardOp1, 0.5);
+ OCMVerifyAllWithDelay(mockKeyboardOp2, 0.5);
});
it(@"should dismiss the keyboard operation with the matching cancelID if it is not executing", ^{
+ OCMStub([mockKeyboardOp1 isExecuting]).andReturn(false);
OCMStub([mockKeyboardOp2 isExecuting]).andReturn(false);
- [testManager dismissKeyboardWithCancelID:@(testCancelId)];
OCMReject([mockKeyboardOp1 dismissKeyboard]);
- OCMVerify([mockKeyboardOp2 dismissKeyboard]);
+ OCMExpect([mockKeyboardOp2 dismissKeyboard]);
+
+ [testManager dismissKeyboardWithCancelID:@(testCancelId)];
+
+ OCMVerifyAllWithDelay(mockKeyboardOp1, 0.5);
+ OCMVerifyAllWithDelay(mockKeyboardOp2, 0.5);
});
});
});
diff --git a/SmartDeviceLinkTests/DevAPISpecs/SDLChoiceSetSpec.m b/SmartDeviceLinkTests/DevAPISpecs/SDLChoiceSetSpec.m
index 76d1e4338..110a6c53a 100644
--- a/SmartDeviceLinkTests/DevAPISpecs/SDLChoiceSetSpec.m
+++ b/SmartDeviceLinkTests/DevAPISpecs/SDLChoiceSetSpec.m
@@ -8,6 +8,7 @@
#import "SDLChoiceSetDelegate.h"
#import "SDLTTSChunk.h"
#import "SDLVrHelpItem.h"
+#import "SDLArtwork.h"
@interface SDLChoiceSet()
@@ -134,12 +135,14 @@ describe(@"an SDLChoiceSet", ^{
expect(testChoiceSet).to(beNil());
});
- it(@"should return nil with too short or too long timeout", ^{
+ it(@"should cap the timeout when too long or too short", ^{
testChoiceSet = [[SDLChoiceSet alloc] initWithTitle:testTitle delegate:testDelegate layout:testLayout timeout:4.9 initialPromptString:nil timeoutPromptString:nil helpPromptString:nil vrHelpList:nil choices:@[testCell]];
- expect(testChoiceSet).to(beNil());
+ expect(testChoiceSet).toNot(beNil());
+ expect(testChoiceSet.timeout).to(beCloseTo(5.0));
testChoiceSet = [[SDLChoiceSet alloc] initWithTitle:testTitle delegate:testDelegate layout:testLayout timeout:100.1 initialPromptString:nil timeoutPromptString:nil helpPromptString:nil vrHelpList:nil choices:@[testCell]];
- expect(testChoiceSet).to(beNil());
+ expect(testChoiceSet).toNot(beNil());
+ expect(testChoiceSet.timeout).to(beCloseTo(100.0));
});
it(@"should return nil with too short or too long title", ^{
@@ -156,23 +159,24 @@ describe(@"an SDLChoiceSet", ^{
expect(testChoiceSet).to(beNil());
});
- it(@"should return nil with equivalent cell text", ^{
+ it(@"should return nil when 2 or more cells are identical", ^{
+ // Cells cannot be identical
+ SDLArtwork *testArtwork = [[SDLArtwork alloc] initWithStaticIcon:SDLStaticIconNameKey];
+ SDLChoiceCell *equalCell = [[SDLChoiceCell alloc] initWithText:@"Text" secondaryText:@"Text 2" tertiaryText:nil voiceCommands:nil artwork:nil secondaryArtwork:testArtwork];
+ SDLChoiceCell *equalCell2 = [[SDLChoiceCell alloc] initWithText:@"Text" secondaryText:@"Text 2" tertiaryText:nil voiceCommands:nil artwork:nil secondaryArtwork:testArtwork];
+ testChoiceSet = [[SDLChoiceSet alloc] initWithTitle:testTitle delegate:testDelegate choices:@[equalCell, equalCell2]];
+ expect(testChoiceSet).to(beNil());
+ });
+
+ it(@"should return nil when 2 or more cells voice commands are identical", ^{
// Cell `text` cannot be equal
- SDLChoiceCell *equalCell = [[SDLChoiceCell alloc] initWithText:@"Text"];
- SDLChoiceCell *equalCell2 = [[SDLChoiceCell alloc] initWithText:@"Text"];
+ SDLChoiceCell *equalCell = [[SDLChoiceCell alloc] initWithText:@"Text" artwork:nil voiceCommands:@[@"Kit", @"Kat"]];
+ SDLChoiceCell *equalCell2 = [[SDLChoiceCell alloc] initWithText:@"Text 2" artwork:nil voiceCommands:@[@"Kat"]];
testChoiceSet = [[SDLChoiceSet alloc] initWithTitle:testTitle delegate:testDelegate choices:@[equalCell, equalCell2]];
expect(testChoiceSet).to(beNil());
});
context(@"With bad VR data", ^{
- it(@"should return nil if not all choice set items have voice commands", ^{
- // Cell `voiceCommands` cannot be equal
- SDLChoiceCell *equalCellVR = [[SDLChoiceCell alloc] initWithText:@"Text" artwork:nil voiceCommands:@[@"vr"]];
- SDLChoiceCell *equalCellVR2 = [[SDLChoiceCell alloc] initWithText:@"Text2" artwork:nil voiceCommands:nil];
- testChoiceSet = [[SDLChoiceSet alloc] initWithTitle:testTitle delegate:testDelegate choices:@[equalCellVR, equalCellVR2]];
- expect(testChoiceSet).to(beNil());
- });
-
it(@"should return nil if there are duplicate voice command strings in the choice set", ^{
// Cell `voiceCommands` cannot be equal
SDLChoiceCell *equalCellVR = [[SDLChoiceCell alloc] initWithText:@"Text" artwork:nil voiceCommands:@[@"Dog"]];
@@ -184,6 +188,81 @@ describe(@"an SDLChoiceSet", ^{
});
});
+ describe(@"setting the default timeout", ^{
+ __block SDLChoiceSet *testChoiceSet = nil;
+
+ beforeEach(^{
+ testChoiceSet = [[SDLChoiceSet alloc] init];
+ });
+
+ it(@"should return the default timeout if the timeout value was not set", ^{
+ int testDefaultTimeout = 6.0;
+ SDLChoiceSet.defaultTimeout = testDefaultTimeout;
+
+ expect(SDLChoiceSet.defaultTimeout).to(equal(testDefaultTimeout));
+ expect(testChoiceSet.timeout).to(equal(testDefaultTimeout));
+ });
+
+ it(@"should return the timeout value even if the default timeout was set", ^{
+ int testTimeout = 7.0;
+ int testDefaultTimeout = 9.0;
+ SDLChoiceSet.defaultTimeout = testDefaultTimeout;
+ testChoiceSet.timeout = testTimeout;
+
+ expect(SDLChoiceSet.defaultTimeout).to(equal(testDefaultTimeout));
+ expect(testChoiceSet.timeout).to(equal(testTimeout));
+ });
+
+ it(@"should return 100 if a value greater than 100 has been set", ^{
+ SDLChoiceSet.defaultTimeout = 155.0;
+
+ expect(SDLChoiceSet.defaultTimeout).to(equal(100.0));
+ expect(testChoiceSet.timeout).to(equal(100.0));
+ });
+
+ it(@"should return 5 if a value less than 5 has been set", ^{
+ SDLChoiceSet.defaultTimeout = -3.0;
+
+ expect(SDLChoiceSet.defaultTimeout).to(equal(5.0));
+ expect(testChoiceSet.timeout).to(equal(5.0));
+ });
+ });
+
+ describe(@"setting the timeout", ^{
+ __block SDLChoiceSet *testChoiceSet = nil;
+ __block NSTimeInterval testDefaultTimeout = 7.0;
+
+ beforeEach(^{
+ testChoiceSet = [[SDLChoiceSet alloc] init];
+ SDLChoiceSet.defaultTimeout = testDefaultTimeout;
+ });
+
+ it(@"should return the default timeout if the timeout was not set", ^{
+ expect(testChoiceSet.timeout).to(equal(testDefaultTimeout));
+ });
+
+ it(@"should return the default timeout if the timeout was set to 0", ^{
+ testChoiceSet.timeout = 0.0;
+ expect(testChoiceSet.timeout).to(equal(testDefaultTimeout));
+ });
+
+ it(@"should return the timeout value if it was set", ^{
+ int testTimeout = 9.0;
+ testChoiceSet.timeout = testTimeout;
+ expect(testChoiceSet.timeout).to(equal(testTimeout));
+ });
+
+ it(@"should return 100 if a value greater than 100 has been set", ^{
+ testChoiceSet.timeout = 214.0;
+ expect(testChoiceSet.timeout).to(equal(100.0));
+ });
+
+ it(@"should return 5 if a value less than 5 has been set", ^{
+ testChoiceSet.timeout = 2.25;
+ expect(testChoiceSet.timeout).to(equal(5.0));
+ });
+ });
+
describe(@"canceling the choice set", ^{
__block BOOL canceledHandlerCalled = NO;
diff --git a/SmartDeviceLinkTests/DevAPISpecs/SDLFileManagerSpec.m b/SmartDeviceLinkTests/DevAPISpecs/SDLFileManagerSpec.m
index f7ea93fc8..9c82297e0 100644
--- a/SmartDeviceLinkTests/DevAPISpecs/SDLFileManagerSpec.m
+++ b/SmartDeviceLinkTests/DevAPISpecs/SDLFileManagerSpec.m
@@ -10,6 +10,7 @@
#import "SDLFileManagerConfiguration.h"
#import "SDLFileType.h"
#import "SDLFileWrapper.h"
+#import "SDLGlobals.h"
#import "SDLListFiles.h"
#import "SDLListFilesOperation.h"
#import "SDLListFilesResponse.h"
@@ -47,6 +48,7 @@ SDLFileManagerState *const SDLFileManagerStateStartupError = @"StartupError";
- (BOOL)sdl_canFileBeUploadedAgain:(nullable SDLFile *)file maxUploadCount:(int)maxRetryCount failedFileUploadsCount:(NSMutableDictionary<SDLFileName *, NSNumber<SDLUInt> *> *)failedFileUploadsCount;
+ (NSMutableDictionary<SDLFileName *, NSNumber<SDLUInt> *> *)sdl_incrementFailedUploadCountForFileName:(SDLFileName *)fileName failedFileUploadsCount:(NSMutableDictionary<SDLFileName *, NSNumber<SDLUInt> *> *)failedFileUploadsCount;
+- (BOOL)hasUploadedFile:(SDLFile *)file;
@end
@@ -85,6 +87,7 @@ describe(@"uploading / deleting single files with the file manager", ^{
NSUInteger failureSpaceAvailabe = 2000000000;
NSUInteger newBytesAvailable = 750;
NSArray<NSString *> *testInitialFileNames = @[@"testFile1", @"testFile2", @"testFile3"];
+ NSArray<NSString *> *testInitialFileNames2 = @[@"testFile1", @"testFile2", @"testFile3", @"testFile4"];
beforeEach(^{
testConnectionManager = [[TestConnectionManager alloc] init];
@@ -121,14 +124,12 @@ describe(@"uploading / deleting single files with the file manager", ^{
startupError = error;
completionHandlerCalled = YES;
}];
-
- [NSThread sleepForTimeInterval:0.1];
});
it(@"should have queued a ListFiles request", ^{
- expect(testFileManager.currentState).to(match(SDLFileManagerStateFetchingInitialList));
- expect(testFileManager.pendingTransactions).to(haveCount(@1));
- expect(testFileManager.pendingTransactions.firstObject).to(beAnInstanceOf([SDLListFilesOperation class]));
+ expect(testFileManager.currentState).toEventually(match(SDLFileManagerStateFetchingInitialList));
+ expect(testFileManager.pendingTransactions).toEventually(haveCount(@1));
+ expect(testFileManager.pendingTransactions.firstObject).toEventually(beAnInstanceOf([SDLListFilesOperation class]));
});
describe(@"after going to the shutdown state and receiving a ListFiles response", ^{
@@ -144,33 +145,44 @@ describe(@"uploading / deleting single files with the file manager", ^{
});
});
- describe(@"after receiving a ListFiles error", ^{
+ describe(@"getting an error for a ListFiles request", ^{
+ __block SDLListFilesOperation *operation = nil;
+
beforeEach(^{
- SDLListFilesOperation *operation = testFileManager.pendingTransactions.firstObject;
- operation.completionHandler(NO, initialSpaceAvailable, testInitialFileNames, [NSError sdl_fileManager_unableToStartError]);
+ operation = testFileManager.pendingTransactions.firstObject;
});
- it(@"should handle the error properly", ^{
- expect(testFileManager.currentState).to(match(SDLFileManagerStateStartupError));
+ it(@"should handle a ListFiles error with a resultCode of DISALLOWED and transition to the ready state", ^{
+ operation.completionHandler(NO, initialSpaceAvailable, testInitialFileNames, [NSError errorWithDomain:[NSError sdl_fileManager_unableToStartError].domain code:[NSError sdl_fileManager_unableToStartError].code userInfo:@{@"resultCode" : SDLResultDisallowed}]);
+
+ expect(testFileManager.currentState).to(match(SDLFileManagerStateReady));
expect(testFileManager.remoteFileNames).to(beEmpty());
expect(@(testFileManager.bytesAvailable)).to(equal(initialSpaceAvailable));
});
- });
- describe(@"after receiving a ListFiles error with a resultCode DISALLOWED", ^{
- beforeEach(^{
- SDLListFilesOperation *operation = testFileManager.pendingTransactions.firstObject;
- NSMutableDictionary *userInfo = [[NSError sdl_fileManager_unableToStartError].userInfo mutableCopy];
- userInfo[@"resultCode"] = SDLResultDisallowed;
- NSError *errorWithResultCode = [NSError errorWithDomain:[NSError sdl_fileManager_unableToStartError].domain code:[NSError sdl_fileManager_unableToStartError].code userInfo:userInfo];
- operation.completionHandler(NO, initialSpaceAvailable, testInitialFileNames, errorWithResultCode);
- });
+ it(@"should handle a ListFiles error with a resultCode ENCRYPTION_NEEDED and transition to the ready state", ^{
+ operation.completionHandler(NO, initialSpaceAvailable, testInitialFileNames, [NSError errorWithDomain:[NSError sdl_fileManager_unableToStartError].domain code:[NSError sdl_fileManager_unableToStartError].code userInfo:@{@"resultCode" : SDLResultEncryptionNeeded}]);
- it(@"should handle the error properly", ^{
expect(testFileManager.currentState).to(match(SDLFileManagerStateReady));
expect(testFileManager.remoteFileNames).to(beEmpty());
expect(@(testFileManager.bytesAvailable)).to(equal(initialSpaceAvailable));
});
+
+ it(@"should transition to the error state if it gets a ListFiles error with a resultCode that is not handled by the library", ^{
+ operation.completionHandler(NO, initialSpaceAvailable, testInitialFileNames, [NSError errorWithDomain:[NSError sdl_fileManager_unableToStartError].domain code:[NSError sdl_fileManager_unableToStartError].code userInfo:@{@"resultCode" : SDLResultUnsupportedRequest}]);
+
+ expect(testFileManager.currentState).to(match(SDLFileManagerStateStartupError));
+ expect(testFileManager.remoteFileNames).to(beEmpty());
+ expect(@(testFileManager.bytesAvailable)).to(equal(initialSpaceAvailable));
+ });
+
+ it(@"should transition to the error state if it gets a ListFiles error without a resultCode", ^{
+ operation.completionHandler(NO, initialSpaceAvailable, testInitialFileNames, [NSError sdl_fileManager_unableToStartError]);
+
+ expect(testFileManager.currentState).to(match(SDLFileManagerStateStartupError));
+ expect(testFileManager.remoteFileNames).to(beEmpty());
+ expect(@(testFileManager.bytesAvailable)).to(equal(initialSpaceAvailable));
+ });
});
describe(@"after receiving a ListFiles response", ^{
@@ -246,6 +258,140 @@ describe(@"uploading / deleting single files with the file manager", ^{
});
});
+ describe(@"check hasUploadedFile response", ^{
+ __block SDLFile *testFile = nil;
+
+ beforeEach(^{
+ [testFileManager.stateMachine setToState:SDLFileManagerStateReady fromOldState:SDLFileManagerStateShutdown callEnterTransition:NO];
+ });
+
+ context(@"on RPC version >= 4.4.0", ^{
+ beforeEach(^{
+ [SDLGlobals sharedGlobals].rpcVersion = [SDLVersion versionWithMajor:7 minor:1 patch:0];
+
+ NSData *testFileData = [@"someData" dataUsingEncoding:NSUTF8StringEncoding];
+ testFile = [SDLFile persistentFileWithData:testFileData name:@"testFile4" fileExtension:@"png"];
+ });
+
+ context(@"when the file is in remoteFileNames", ^{
+ beforeEach(^{
+ testFileManager.mutableRemoteFileNames = [NSMutableSet setWithArray:testInitialFileNames2];
+ });
+
+ it(@"should return YES", ^{
+ expect([testFileManager hasUploadedFile:testFile]).to(equal(YES));
+ });
+ });
+
+ context(@"when the file is not in remoteFileNames", ^{
+ beforeEach(^{
+ testFileManager.mutableRemoteFileNames = [NSMutableSet setWithArray:testInitialFileNames];
+ });
+
+ it(@"should return NO", ^{
+ expect([testFileManager hasUploadedFile:testFile]).to(equal(NO));
+ });
+ });
+ });
+
+ context(@"on RPC version < 4.4.0", ^{
+ beforeEach(^{
+ [SDLGlobals sharedGlobals].rpcVersion = [SDLVersion versionWithMajor:4 minor:3 patch:0];
+ });
+
+ context(@"when the file is persistent", ^{
+ beforeEach(^{
+ NSData *testFileData = [@"someData" dataUsingEncoding:NSUTF8StringEncoding];
+ testFile = [SDLFile persistentFileWithData:testFileData name:@"testFile4" fileExtension:@"png"];
+ });
+
+ context(@"when the file is not in remoteFileNames", ^{
+ beforeEach(^{
+ testFileManager.mutableRemoteFileNames = [NSMutableSet setWithArray:testInitialFileNames];
+ });
+
+ it(@"should return NO", ^{
+ expect([testFileManager hasUploadedFile:testFile]).to(equal(NO));
+ });
+ });
+
+ context(@"when the file is in remoteFileNames", ^{
+ beforeEach(^{
+ testFileManager.mutableRemoteFileNames = [NSMutableSet setWithArray:testInitialFileNames2];
+ });
+
+ it(@"should return YES", ^{
+ expect([testFileManager hasUploadedFile:testFile]).to(equal(YES));
+ });
+
+ context(@"when the file is in uploadedEphemeralFiles", ^{
+ beforeEach(^{
+ testFileManager.uploadedEphemeralFileNames = [NSMutableSet setWithArray:testInitialFileNames];
+
+ });
+
+ it(@"should return YES", ^{
+ expect([testFileManager hasUploadedFile:testFile]).to(equal(YES));
+ });
+ });
+ });
+ });
+
+ context(@"when the file is not persistent", ^{
+ beforeEach(^{
+ NSData *testFileData = [@"someData" dataUsingEncoding:NSUTF8StringEncoding];
+ testFile = [SDLFile fileWithData:testFileData name:@"testFile4" fileExtension:@"png"];
+ });
+
+ it(@"should return NO", ^{
+ expect([testFileManager hasUploadedFile:testFile]).to(equal(NO));
+ });
+
+ context(@"when the file is in remoteFileNames", ^{
+ beforeEach(^{
+ testFileManager.mutableRemoteFileNames = [NSMutableSet setWithArray:testInitialFileNames2];
+ });
+
+ it(@"should return NO", ^{
+ expect([testFileManager hasUploadedFile:testFile]).to(equal(NO));
+ });
+
+ context(@"when the file is in uploadedEphemeralFiles", ^{
+ beforeEach(^{
+ testFileManager.uploadedEphemeralFileNames = [NSMutableSet setWithArray:testInitialFileNames2];
+
+ });
+
+ it(@"should return YES", ^{
+ expect([testFileManager hasUploadedFile:testFile]).to(equal(YES));
+ });
+ });
+
+ context(@"when the file is not in uploadedEphemeralFiles", ^{
+ beforeEach(^{
+ testFileManager.uploadedEphemeralFileNames = [NSMutableSet setWithArray:testInitialFileNames];
+
+ });
+
+ it(@"should return NO", ^{
+ expect([testFileManager hasUploadedFile:testFile]).to(equal(NO));
+ });
+ });
+ });
+
+ context(@"when the file is not in remoteFileNames", ^{
+ beforeEach(^{
+ testFileManager.mutableRemoteFileNames = [NSMutableSet setWithArray:testInitialFileNames];
+ });
+
+ it(@"should return NO", ^{
+ expect([testFileManager hasUploadedFile:testFile]).to(equal(NO));
+ });
+ });
+ });
+ });
+ });
+
describe(@"uploading a new file", ^{
__block NSString *testFileName = nil;
__block SDLFile *testUploadFile = nil;
@@ -320,19 +466,20 @@ describe(@"uploading / deleting single files with the file manager", ^{
});
});
- context(@"when allow overwrite is NO", ^{
+ context(@"when allow overwrite is NO and the RPC version is < 4.4.0", ^{
__block NSString *testUploadFileName = nil;
__block Boolean testUploadOverwrite = NO;
beforeEach(^{
testUploadFileName = [testInitialFileNames lastObject];
+ [SDLGlobals sharedGlobals].rpcVersion = [SDLVersion versionWithMajor:4 minor:3 patch:0];
});
- it(@"should not upload the file if persistance is YES", ^{
- SDLFile *persistantFile = [[SDLFile alloc] initWithData:testFileData name:testUploadFileName fileExtension:@"bin" persistent:YES];
- persistantFile.overwrite = testUploadOverwrite;
+ 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;
- [testFileManager uploadFile:persistantFile completionHandler:^(BOOL success, NSUInteger bytesAvailable, NSError * _Nullable error) {
+ [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]));
@@ -341,11 +488,11 @@ describe(@"uploading / deleting single files with the file manager", ^{
expect(testFileManager.pendingTransactions.count).to(equal(0));
});
- it(@"should upload the file if persistance is NO", ^{
- SDLFile *unPersistantFile = [[SDLFile alloc] initWithData:testFileData name:testUploadFileName fileExtension:@"bin" persistent:NO];
- unPersistantFile.overwrite = testUploadOverwrite;
+ it(@"should upload the file if persistence is NO", ^{
+ SDLFile *unPersistentFile = [[SDLFile alloc] initWithData:testFileData name:testUploadFileName fileExtension:@"bin" persistent:NO];
+ unPersistentFile.overwrite = testUploadOverwrite;
- [testFileManager uploadFile:unPersistantFile completionHandler:^(BOOL success, NSUInteger bytesAvailable, NSError * _Nullable error) {
+ [testFileManager uploadFile:unPersistentFile completionHandler:^(BOOL success, NSUInteger bytesAvailable, NSError * _Nullable error) {
expect(success).to(beTrue());
expect(bytesAvailable).to(equal(newBytesAvailable));
expect(error).to(beNil());
@@ -473,6 +620,10 @@ describe(@"uploading / deleting single files with the file manager", ^{
__block SDLArtwork *artwork = nil;
context(@"when artwork is nil", ^{
+ beforeEach(^{
+ artwork = nil;
+ });
+
it(@"should not allow file to be uploaded", ^{
expect(artwork).to(beNil());
BOOL testFileNeedsUpload = [testFileManager fileNeedsUpload:artwork];
diff --git a/SmartDeviceLinkTests/DevAPISpecs/SDLLifecycleManagerSpec.m b/SmartDeviceLinkTests/DevAPISpecs/SDLLifecycleManagerSpec.m
index 8d2cf9dbc..37a5583b8 100644
--- a/SmartDeviceLinkTests/DevAPISpecs/SDLLifecycleManagerSpec.m
+++ b/SmartDeviceLinkTests/DevAPISpecs/SDLLifecycleManagerSpec.m
@@ -4,48 +4,19 @@
#import "SDLLifecycleManager.h"
-#import "SDLAppServiceData.h"
-#import "SDLChangeRegistration.h"
-#import "SDLConfiguration.h"
+#import <SmartDeviceLink/SmartDeviceLink.h>
+
#import "SDLConnectionManagerType.h"
-#import "SDLEncryptionConfiguration.h"
#import "SDLEncryptionLifecycleManager.h"
#import "SDLError.h"
-#import "SDLFileManagerConfiguration.h"
-#import "SDLFileManager.h"
#import "SDLGlobals.h"
-#import "SDLHMILevel.h"
-#import "SDLLifecycleConfiguration.h"
#import "SDLLifecycleProtocolHandler.h"
-#import "SDLLockScreenConfiguration.h"
#import "SDLLockScreenManager.h"
-#import "SDLLogConfiguration.h"
-#import "SDLManagerDelegate.h"
#import "SDLNotificationDispatcher.h"
-#import "SDLOnAppInterfaceUnregistered.h"
-#import "SDLOnAppServiceData.h"
-#import "SDLOnHashChange.h"
-#import "SDLOnHMIStatus.h"
-#import "SDLPerformAppServiceInteractionResponse.h"
-#import "SDLPermissionManager.h"
#import "SDLProtocol.h"
-#import "SDLRegisterAppInterface.h"
-#import "SDLRegisterAppInterfaceResponse.h"
-#import "SDLResult.h"
#import "SDLRPCNotificationNotification.h"
#import "SDLSecondaryTransportManager.h"
-#import "SDLShow.h"
#import "SDLStateMachine.h"
-#import "SDLStreamingMediaConfiguration.h"
-#import "SDLStreamingMediaManager.h"
-#import "SDLSystemCapabilityManager.h"
-#import "SDLTextAlignment.h"
-#import "SDLTTSChunk.h"
-#import "SDLUnregisterAppInterface.h"
-#import "SDLUnregisterAppInterfaceResponse.h"
-#import "SDLVersion.h"
-#import "SDLVideoStreamingState.h"
-
@interface SDLStreamingMediaManager ()
@@ -54,6 +25,12 @@
@end
+@interface SDLSystemCapabilityManager ()
+
+@property (nullable, strong, nonatomic) NSString *lastDisplayLayoutRequestTemplate;
+
+@end
+
@interface SDLLifecycleManager ()
// this private property is used for testing
@property (copy, nonatomic) dispatch_queue_t lifecycleQueue;
@@ -62,6 +39,25 @@
@property (strong, nonatomic, nullable) SDLSecondaryTransportManager *secondaryTransportManager;
@property (strong, nonatomic) SDLEncryptionLifecycleManager *encryptionLifecycleManager;
@property (strong, nonatomic, nullable) SDLLifecycleProtocolHandler *protocolHandler;
+@property (copy, nonatomic, readwrite, nullable) SDLHMILevel hmiLevel;
+@property (copy, nonatomic, readwrite, nullable) SDLAudioStreamingState audioStreamingState;
+@property (copy, nonatomic, readwrite, nullable) SDLVideoStreamingState videoStreamingState;
+@property (copy, nonatomic, readwrite, nullable) SDLSystemContext systemContext;
+@property (strong, nonatomic, readwrite, nullable) SDLRegisterAppInterfaceResponse *registerResponse;
+@property (strong, nonatomic, readwrite, nullable) SDLSystemInfo *systemInfo;
+
+- (void)didEnterStateConnected;
+@end
+
+@interface SDLLifecycleTestManager : SDLLifecycleManager
+- (void)sendConnectionManagerRequest:(__kindof SDLRPCMessage *)request withResponseHandler:(nullable SDLResponseHandler)handler;
+@property (strong, nonatomic, nullable) __kindof SDLRPCMessage *testRequest;
+@end
+
+@implementation SDLLifecycleTestManager
+- (void)sendConnectionManagerRequest:(__kindof SDLRPCMessage *)request withResponseHandler:(nullable SDLResponseHandler)handler {
+ self.testRequest = request;
+}
@end
@interface SDLGlobals ()
@@ -88,10 +84,27 @@ QuickConfigurationEnd
QuickSpecBegin(SDLLifecycleManagerSpec)
+// test lifecycle manager internals
+describe(@"test lifecycle manager internals", ^{
+ context(@"init and assign version", ^{
+ SDLLifecycleTestManager *manager = [[SDLLifecycleTestManager alloc] init];
+ it(@"expect object to be created", ^{
+ expect(manager).notTo(beNil());
+ });
+ context(@"didEnterStateConnected", ^{
+ [manager didEnterStateConnected];
+ it(@"expect request to be of proper kind", ^{
+ expect([manager.testRequest isKindOfClass:SDLRegisterAppInterface.class]).to(equal(YES));
+ });
+ });
+ });
+});
+
+// a lifecycle manager
describe(@"a lifecycle manager", ^{
__block SDLLifecycleManager *testManager = nil;
__block SDLConfiguration *testConfig = nil;
- __block id protocolMock = nil;
+ __block SDLProtocol *protocolMock = nil;
__block id sdlManagerDelegateProtocolMock = nil;
__block id lockScreenManagerMock = nil;
__block id fileManagerMock = nil;
@@ -100,6 +113,9 @@ describe(@"a lifecycle manager", ^{
__block id systemCapabilityMock = nil;
__block id secondaryTransportManagerMock = nil;
__block id encryptionManagerMock = nil;
+ SDLVehicleType *vehicleType = [[SDLVehicleType alloc] initWithMake:@"Make" model:@"Model" modelYear:@"Model Year" trim:@"Trim"];
+ NSString *softwareVersion = @"1.1.1.1";
+ NSString *hardwareVersion = @"2.2.2.2";
void (^transitionToState)(SDLState *) = ^(SDLState *state) {
dispatch_sync(testManager.lifecycleQueue, ^{
@@ -145,7 +161,8 @@ describe(@"a lifecycle manager", ^{
[SDLGlobals sharedGlobals].protocolVersion = [SDLVersion versionWithMajor:3 minor:0 patch:0];
[SDLGlobals sharedGlobals].rpcVersion = [SDLVersion versionWithMajor:3 minor:0 patch:0];
});
-
+
+ // should initialize properties
it(@"should initialize properties", ^{
expect(testManager.configuration).toNot(equal(testConfig)); // This is copied
expect(testManager.delegate).toNot(beNil());
@@ -167,7 +184,8 @@ describe(@"a lifecycle manager", ^{
});
itBehavesLike(@"unable to send an RPC", ^{ return @{ @"manager": testManager }; });
-
+
+ // after receiving an HMI Status
describe(@"after receiving an HMI Status", ^{
__block SDLOnHMIStatus *testHMIStatus = nil;
__block SDLHMILevel testHMILevel = nil;
@@ -175,7 +193,8 @@ describe(@"a lifecycle manager", ^{
beforeEach(^{
testHMIStatus = [[SDLOnHMIStatus alloc] init];
});
-
+
+ // a non-none hmi level
context(@"a non-none hmi level", ^{
beforeEach(^{
testHMILevel = SDLHMILevelNone;
@@ -188,7 +207,8 @@ describe(@"a lifecycle manager", ^{
expect(testManager.hmiLevel).toEventually(equal(testHMILevel));
});
});
-
+
+ // a non-full, non-none hmi level
context(@"a non-full, non-none hmi level", ^{
beforeEach(^{
testHMILevel = SDLHMILevelBackground;
@@ -201,7 +221,8 @@ describe(@"a lifecycle manager", ^{
expect(testManager.hmiLevel).toEventually(equal(testHMILevel));
});
});
-
+
+ // a full hmi level
context(@"a full hmi level", ^{
beforeEach(^{
testHMILevel = SDLHMILevelFull;
@@ -215,7 +236,8 @@ describe(@"a lifecycle manager", ^{
});
});
});
-
+
+ // calling stop
describe(@"calling stop", ^{
beforeEach(^{
[testManager stop];
@@ -226,7 +248,8 @@ describe(@"a lifecycle manager", ^{
expect(testManager.lifecycleState).toEventuallyNot(match(SDLLifecycleStateStarted));
});
});
-
+
+ // when started
describe(@"when started", ^{
__block BOOL readyHandlerSuccess = NO;
__block NSError *readyHandlerError = nil;
@@ -249,34 +272,60 @@ describe(@"a lifecycle manager", ^{
expect(testManager.secondaryTransportManager).toNot(beNil());
});
+ // after receiving a connect notification
describe(@"after receiving a connect notification", ^{
- beforeEach(^{
+ // should send a register app interface request and be in the connected state
+ it(@"should send a register app interface request and be in the connected state", ^{
+ OCMStub([protocolMock sendRPC:[OCMArg any] error:[OCMArg setTo:nil]]).andReturn(YES);
// When we connect, we should be creating an sending an RAI
- OCMExpect([protocolMock sendRPC:[OCMArg isKindOfClass:[SDLRegisterAppInterface class]]]);
-
+
[testManager.notificationDispatcher postNotificationName:SDLRPCServiceDidConnect infoObject:nil];
- });
-
- it(@"should send a register app interface request and be in the connected state", ^{
- OCMVerifyAllWithDelay(protocolMock, 1.0);
expect(testManager.lifecycleState).toEventually(equal(SDLLifecycleStateConnected));
});
-
itBehavesLike(@"unable to send an RPC", ^{ return @{ @"manager": testManager }; });
-
+
+ // when the protocol system info is set
+ context(@"when the protocol system info is set", ^{
+ SDLSystemInfo *testSystemInfo = [[SDLSystemInfo alloc] initWithVehicleType:vehicleType softwareVersion:softwareVersion hardwareVersion:hardwareVersion];
+
+ it(@"should call the delegate handler", ^{
+ OCMStub(protocolMock.systemInfo).andReturn(testSystemInfo);
+ OCMExpect([sdlManagerDelegateProtocolMock didReceiveSystemInfo:[OCMArg isEqual:testSystemInfo]]).andReturn(YES);
+ [testManager.notificationDispatcher postNotificationName:SDLRPCServiceDidConnect infoObject:nil];
+
+ OCMVerifyAllWithDelay(sdlManagerDelegateProtocolMock, 1.0);
+ });
+ });
+
+ // when the protocol system info is not set
+ context(@"when the protocol system info is not set", ^{
+ beforeEach(^{
+ OCMStub(protocolMock.systemInfo).andReturn(nil);
+ [testManager.notificationDispatcher postNotificationName:SDLRPCServiceDidConnect infoObject:nil];
+ });
+
+ it(@"should call the delegate handler", ^{
+ OCMReject([sdlManagerDelegateProtocolMock didReceiveSystemInfo:[OCMArg isNil]]);
+ });
+ });
+
+ // after receiving a disconnect notification"
describe(@"after receiving a disconnect notification", ^{
beforeEach(^{
+ OCMStub([protocolMock sendRPC:[OCMArg any] error:[OCMArg setTo:nil]]).andReturn(YES);
+ [testManager.notificationDispatcher postNotificationName:SDLRPCServiceDidConnect infoObject:nil];
[testManager.notificationDispatcher postNotificationName:SDLTransportDidDisconnect infoObject:nil];
- [NSThread sleepForTimeInterval:0.1];
});
it(@"should be in the started state", ^{
- expect(testManager.lifecycleState).to(equal(SDLLifecycleStateReconnecting));
+ expect(testManager.lifecycleState).toEventually(equal(SDLLifecycleStateReconnecting));
});
});
-
+
+ // stopping the manager
describe(@"stopping the manager", ^{
it(@"should simply stop", ^{
+ [testManager.notificationDispatcher postNotificationName:SDLRPCServiceDidConnect infoObject:nil];
[testManager stop];
expect(testManager.lifecycleState).toEventually(equal(SDLLifecycleStateStopped));
@@ -284,6 +333,7 @@ describe(@"a lifecycle manager", ^{
});
});
+ // in the connected state when the minimum protocol version is in effect
describe(@"in the connected state when the minimum protocol version is in effect", ^{
beforeEach(^{
[SDLGlobals sharedGlobals].protocolVersion = [SDLVersion versionWithMajor:1 minor:0 patch:0];
@@ -296,13 +346,24 @@ describe(@"a lifecycle manager", ^{
OCMVerifyAll(protocolMock);
});
});
-
+
+ // in the connected state
describe(@"in the connected state", ^{
beforeEach(^{
[testManager.lifecycleStateMachine setToState:SDLLifecycleStateConnected fromOldState:nil callEnterTransition:NO];
});
- describe(@"after receiving a register app interface response", ^{
+ // after receiving another connect notification for encryption
+ describe(@"after receiving another connect notification for encryption", ^{
+ it(@"should not crash", ^{
+ expectAction(^{
+ [testManager.notificationDispatcher postNotificationName:SDLRPCServiceDidConnect infoObject:nil];
+ }).toNot(raiseException());
+ });
+ });
+
+ // after receiving a register app interface response
+ describe(@"after receiving a register app interface response", ^{
it(@"should eventually reach the ready state", ^{
NSError *fileManagerStartError = [NSError errorWithDomain:@"testDomain" code:0 userInfo:nil];
NSError *permissionManagerStartError = [NSError errorWithDomain:@"testDomain" code:0 userInfo:nil];
@@ -324,20 +385,156 @@ describe(@"a lifecycle manager", ^{
itBehavesLike(@"unable to send an RPC", ^{ return @{ @"manager": testManager }; });
});
-
+
+ // when the protocol system info is not set
+ context(@"when the protocol system info is not set", ^{
+ beforeEach(^{
+ testManager.systemInfo = nil;
+ OCMExpect([sdlManagerDelegateProtocolMock didReceiveSystemInfo:[OCMArg isNotNil]]).andReturn(YES);
+ });
+
+ it(@"should call the delegate handler", ^{
+ SDLRegisterAppInterfaceResponse *response = [[SDLRegisterAppInterfaceResponse alloc] init];
+ response.resultCode = SDLResultSuccess;
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated"
+ response.vehicleType = vehicleType;
+ response.systemSoftwareVersion = softwareVersion;
+#pragma clang diagnostic pop
+ testManager.registerResponse = response;
+ [testManager.lifecycleStateMachine setToState:SDLLifecycleStateRegistered fromOldState:nil callEnterTransition:YES];
+
+ OCMVerifyAllWithDelay(sdlManagerDelegateProtocolMock, 1.0);
+ });
+ });
+
+ // when the protocol system info is set
+ context(@"when the protocol system info is set", ^{
+ SDLSystemInfo *testSystemInfo = [[SDLSystemInfo alloc] initWithVehicleType:vehicleType softwareVersion:softwareVersion hardwareVersion:hardwareVersion];
+
+ beforeEach(^{
+ testManager.systemInfo = testSystemInfo;
+ });
+
+ it(@"should call not the delegate handler", ^{
+ SDLRegisterAppInterfaceResponse *response = [[SDLRegisterAppInterfaceResponse alloc] init];
+ response.resultCode = SDLResultSuccess;
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated"
+ response.vehicleType = vehicleType;
+ response.systemSoftwareVersion = softwareVersion;
+#pragma clang diagnostic pop
+ [testManager.lifecycleStateMachine setToState:SDLLifecycleStateRegistered fromOldState:nil callEnterTransition:YES];
+
+ OCMReject([sdlManagerDelegateProtocolMock didReceiveSystemInfo:[OCMArg isNotNil]]);
+ });
+ });
+
+ // when the register response returns different language than the one passed with the lifecycle configuration
+ context(@"when the register response returns different language than the one passed with the lifecycle configuration", ^{
+ it(@"should should update the configuration when the app supports the head unit language", ^{
+ SDLRegisterAppInterfaceResponse *registerAppInterfaceResponse = [[SDLRegisterAppInterfaceResponse alloc] init];
+ registerAppInterfaceResponse.success = @YES;
+ registerAppInterfaceResponse.resultCode = SDLResultWrongLanguage;
+ registerAppInterfaceResponse.info = @"Language mismatch";
+ registerAppInterfaceResponse.language = SDLLanguageEnGb;
+ registerAppInterfaceResponse.hmiDisplayLanguage = SDLLanguageEnGb;
+ testManager.registerResponse = registerAppInterfaceResponse;
+
+ SDLLifecycleConfigurationUpdate *update = [[SDLLifecycleConfigurationUpdate alloc] initWithAppName:@"EnGb" shortAppName:@"E" ttsName:[SDLTTSChunk textChunksFromString:@"EnGb ttsName"] voiceRecognitionCommandNames:@[@"EnGb", @"Gb"]];
+ OCMStub([testManager.delegate managerShouldUpdateLifecycleToLanguage:[OCMArg any] hmiLanguage:[OCMArg any]]).andReturn(update);
+
+ OCMExpect([protocolMock sendRPC:[OCMArg checkWithBlock:^BOOL(id value) {
+ SDLChangeRegistration *changeRegistration = (SDLChangeRegistration *)value;
+ expect(changeRegistration.appName).to(equal(update.appName));
+ expect(changeRegistration.ngnMediaScreenAppName).to(equal(update.shortAppName));
+ expect(changeRegistration.ttsName).to(equal(update.ttsName));
+ expect(changeRegistration.vrSynonyms).to(equal(@[@"EnGb", @"Gb"]));
+ return [value isKindOfClass:[SDLChangeRegistration class]];
+ }] error:[OCMArg anyObjectRef]]);
+
+ setToStateWithEnterTransition(SDLLifecycleStateRegistered, SDLLifecycleStateUpdatingConfiguration);
+
+ OCMVerifyAllWithDelay(protocolMock, 0.5);
+
+ expect(testManager.configuration.lifecycleConfig.language).toEventually(equal(SDLLanguageEnGb));
+ expect(testManager.currentVRLanguage).toEventually(equal(SDLLanguageEnGb));
+ expect(testManager.configuration.lifecycleConfig.appName).toEventually(equal(@"EnGb"));
+ expect(testManager.configuration.lifecycleConfig.shortAppName).toEventually(equal(@"E"));
+ expect(testManager.configuration.lifecycleConfig.ttsName).toEventually(equal([SDLTTSChunk textChunksFromString:@"EnGb ttsName"]));
+ });
+
+ it(@"should not update the configuration when the app does not support the head unit language or display language", ^{
+ SDLRegisterAppInterfaceResponse *registerAppInterfaceResponse = [[SDLRegisterAppInterfaceResponse alloc] init];
+ registerAppInterfaceResponse.success = @YES;
+ registerAppInterfaceResponse.resultCode = SDLResultWrongLanguage;
+ registerAppInterfaceResponse.info = @"Language mismatch";
+ registerAppInterfaceResponse.language = SDLLanguageDeDe;
+ registerAppInterfaceResponse.hmiDisplayLanguage = SDLLanguageDeDe;
+ testManager.registerResponse = registerAppInterfaceResponse;
+
+ OCMStub([testManager.delegate managerShouldUpdateLifecycleToLanguage:[OCMArg any] hmiLanguage:[OCMArg any]]).andReturn(nil);
+
+ setToStateWithEnterTransition(SDLLifecycleStateRegistered, SDLLifecycleStateUpdatingConfiguration);
+
+ OCMVerifyAllWithDelay(protocolMock, 0.5);
+
+ expect(testManager.configuration.lifecycleConfig.language).toEventually(equal(SDLLanguageEnUs));
+ expect(testManager.currentVRLanguage).toEventually(equal(SDLLanguageEnUs));
+ expect(testManager.configuration.lifecycleConfig.appName).toEventually(equal(@"Test App"));
+ expect(testManager.configuration.lifecycleConfig.shortAppName).toEventually(equal(@"Short Name"));
+ expect(testManager.configuration.lifecycleConfig.ttsName).toEventually(beNil());
+ });
+
+ it(@"should update when the app supports the head unit display language", ^{
+ SDLRegisterAppInterfaceResponse *registerAppInterfaceResponse = [[SDLRegisterAppInterfaceResponse alloc] init];
+ registerAppInterfaceResponse.success = @YES;
+ registerAppInterfaceResponse.resultCode = SDLResultWrongLanguage;
+ registerAppInterfaceResponse.info = @"Language mismatch";
+ registerAppInterfaceResponse.language = SDLLanguageEnUs;
+ registerAppInterfaceResponse.hmiDisplayLanguage = SDLLanguageEnGb;
+ testManager.registerResponse = registerAppInterfaceResponse;
+
+ SDLLifecycleConfigurationUpdate *update = [[SDLLifecycleConfigurationUpdate alloc] initWithAppName:@"EnGb" shortAppName:@"Gb" ttsName:nil voiceRecognitionCommandNames:nil];
+ OCMStub([testManager.delegate managerShouldUpdateLifecycleToLanguage:registerAppInterfaceResponse.language hmiLanguage:registerAppInterfaceResponse.hmiDisplayLanguage]).andReturn(update);
+
+ setToStateWithEnterTransition(SDLLifecycleStateRegistered, SDLLifecycleStateUpdatingConfiguration);
+
+ OCMExpect([protocolMock sendRPC:[OCMArg checkWithBlock:^BOOL(id value) {
+ SDLChangeRegistration *changeRegistration = (SDLChangeRegistration *)value;
+ expect(changeRegistration.appName).to(equal(update.appName));
+ expect(changeRegistration.ngnMediaScreenAppName).to(equal(update.shortAppName));
+ expect(changeRegistration.ttsName).to(beNil());
+ expect(changeRegistration.vrSynonyms).to(beNil());
+ return [value isKindOfClass:[SDLChangeRegistration class]];
+ }] error:[OCMArg anyObjectRef]]);
+
+ setToStateWithEnterTransition(SDLLifecycleStateRegistered, SDLLifecycleStateUpdatingConfiguration);
+
+ OCMVerifyAllWithDelay(protocolMock, 0.5);
+
+ expect(testManager.configuration.lifecycleConfig.language).toEventually(equal(SDLLanguageEnGb));
+ expect(testManager.currentVRLanguage).toEventually(equal(SDLLanguageEnUs));
+ expect(testManager.configuration.lifecycleConfig.appName).toEventually(equal(@"EnGb"));
+ expect(testManager.configuration.lifecycleConfig.shortAppName).toEventually(equal(@"Gb"));
+ expect(testManager.configuration.lifecycleConfig.ttsName).toEventually(beNil());
+ });
+ });
+
+ // after receiving a disconnect notification
describe(@"after receiving a disconnect notification", ^{
beforeEach(^{
OCMStub([protocolMock stopWithCompletionHandler:[OCMArg invokeBlock]]);
OCMStub([secondaryTransportManagerMock stopWithCompletionHandler:[OCMArg invokeBlock]]);
[testManager.notificationDispatcher postNotificationName:SDLTransportDidDisconnect infoObject:nil];
- [NSThread sleepForTimeInterval:1.0];
});
it(@"should enter the started state", ^{
- expect(testManager.lifecycleState).toEventually(equal(SDLLifecycleStateStarted));
+ expect(testManager.lifecycleState).withTimeout(3.0).toEventually(equal(SDLLifecycleStateStarted));
});
});
-
+
+ // stopping the manager
describe(@"stopping the manager", ^{
beforeEach(^{
[testManager stop];
@@ -349,10 +546,11 @@ describe(@"a lifecycle manager", ^{
});
});
+ // transitioning to the registered state when the minimum RPC version is in effect
describe(@"transitioning to the registered state when the minimum RPC version is in effect", ^{
beforeEach(^{
+ OCMStub([protocolMock sendRPC:[OCMArg any] error:[OCMArg setTo:nil]]).andReturn(YES);
[SDLGlobals sharedGlobals].rpcVersion = [SDLVersion versionWithMajor:1 minor:0 patch:0];
-
[testManager.lifecycleStateMachine setToState:SDLLifecycleStateRegistered fromOldState:nil callEnterTransition:YES];
});
@@ -360,8 +558,9 @@ describe(@"a lifecycle manager", ^{
expect(testManager.lifecycleState).to(equal(SDLLifecycleStateUnregistering));
});
});
-
- describe(@"transitioning to the Setting Up HMI state", ^{
+
+ // transitioning from setting app icon state to the Setting Up HMI state
+ describe(@"transitioning from setting app icon state to the Setting Up HMI state", ^{
context(@"before register response is a success", ^{
it(@"ready handler should not be called yet", ^{
SDLRegisterAppInterfaceResponse *response = [[SDLRegisterAppInterfaceResponse alloc] init];
@@ -399,11 +598,13 @@ describe(@"a lifecycle manager", ^{
});
});
- describe(@"transitioning to the ready state", ^{
+ // transitioning from the registered state to the ready state
+ describe(@"transitioning from the registered state to the ready state", ^{
beforeEach(^{
[testManager.lifecycleStateMachine setToState:SDLLifecycleStateRegistered fromOldState:nil callEnterTransition:NO];
});
+ // when the register response is a success
context(@"when the register response is a success", ^{
it(@"should call the ready handler with success", ^{
SDLRegisterAppInterfaceResponse *response = [[SDLRegisterAppInterfaceResponse alloc] init];
@@ -417,6 +618,7 @@ describe(@"a lifecycle manager", ^{
});
});
+ // when the register response is a warning
context(@"when the register response is a warning", ^{
it(@"should call the ready handler with success but error", ^{
SDLRegisterAppInterfaceResponse *response = [[SDLRegisterAppInterfaceResponse alloc] init];
@@ -432,114 +634,76 @@ describe(@"a lifecycle manager", ^{
expect(readyHandlerError.userInfo[NSLocalizedFailureReasonErrorKey]).toEventually(match(response.info));
});
});
-
- context(@"when the register response returns different language than the one passed with the lifecycle configuration", ^{
- it(@"should should update the configuration when the app supports the head unit language", ^{
- SDLRegisterAppInterfaceResponse *registerAppInterfaceResponse = [[SDLRegisterAppInterfaceResponse alloc] init];
- registerAppInterfaceResponse.success = @YES;
- registerAppInterfaceResponse.resultCode = SDLResultWrongLanguage;
- registerAppInterfaceResponse.info = @"Language mismatch";
- registerAppInterfaceResponse.language = SDLLanguageEnGb;
- registerAppInterfaceResponse.hmiDisplayLanguage = SDLLanguageEnGb;
- testManager.registerResponse = registerAppInterfaceResponse;
-
- SDLLifecycleConfigurationUpdate *update = [[SDLLifecycleConfigurationUpdate alloc] initWithAppName:@"EnGb" shortAppName:@"E" ttsName:[SDLTTSChunk textChunksFromString:@"EnGb ttsName"] voiceRecognitionCommandNames:@[@"EnGb", @"Gb"]];
- OCMStub([testManager.delegate managerShouldUpdateLifecycleToLanguage:[OCMArg any] hmiLanguage:[OCMArg any]]).andReturn(update);
+ });
- OCMExpect([protocolMock sendRPC:[OCMArg checkWithBlock:^BOOL(id value) {
- SDLChangeRegistration *changeRegistration = (SDLChangeRegistration *)value;
- expect(changeRegistration.appName).to(equal(update.appName));
- expect(changeRegistration.ngnMediaScreenAppName).to(equal(update.shortAppName));
- expect(changeRegistration.ttsName).to(equal(update.ttsName));
- expect(changeRegistration.vrSynonyms).to(equal(@[@"EnGb", @"Gb"]));
- return [value isKindOfClass:[SDLChangeRegistration class]];
- }]]);
+ // in the ready state
+ describe(@"in the ready state", ^{
+ beforeEach(^{
+ [testManager.lifecycleStateMachine setToState:SDLLifecycleStateReady fromOldState:nil callEnterTransition:NO];
+ });
- setToStateWithEnterTransition(SDLLifecycleStateRegistered, SDLLifecycleStateUpdatingConfiguration);
+ // after receiving another connect notification for encryption
+ describe(@"after receiving another connect notification for encryption", ^{
+ it(@"should not crash and remain in the same state", ^{
+ expectAction(^{
+ [testManager.notificationDispatcher postNotificationName:SDLRPCServiceDidConnect infoObject:nil];
+ }).toNot(raiseException());
- OCMVerifyAllWithDelay(protocolMock, 0.5);
-
- expect(testManager.configuration.lifecycleConfig.language).toEventually(equal(SDLLanguageEnGb));
- expect(testManager.currentVRLanguage).toEventually(equal(SDLLanguageEnGb));
- expect(testManager.configuration.lifecycleConfig.appName).toEventually(equal(@"EnGb"));
- expect(testManager.configuration.lifecycleConfig.shortAppName).toEventually(equal(@"E"));
- expect(testManager.configuration.lifecycleConfig.ttsName).toEventually(equal([SDLTTSChunk textChunksFromString:@"EnGb ttsName"]));
+ expect(testManager.lifecycleState).to(equal(SDLLifecycleStateReady));
});
+ });
- it(@"should not update the configuration when the app does not support the head unit language or display language", ^{
- SDLRegisterAppInterfaceResponse *registerAppInterfaceResponse = [[SDLRegisterAppInterfaceResponse alloc] init];
- registerAppInterfaceResponse.success = @YES;
- registerAppInterfaceResponse.resultCode = SDLResultWrongLanguage;
- registerAppInterfaceResponse.info = @"Language mismatch";
- registerAppInterfaceResponse.language = SDLLanguageDeDe;
- registerAppInterfaceResponse.hmiDisplayLanguage = SDLLanguageDeDe;
- testManager.registerResponse = registerAppInterfaceResponse;
-
- OCMStub([testManager.delegate managerShouldUpdateLifecycleToLanguage:[OCMArg any] hmiLanguage:[OCMArg any]]).andReturn(nil);
-
- setToStateWithEnterTransition(SDLLifecycleStateRegistered, SDLLifecycleStateUpdatingConfiguration);
-
- OCMVerifyAllWithDelay(protocolMock, 0.5);
+ describe(@"when sending a request RPC", ^{
+ it(@"can send the RPC", ^{
+ SDLShow *testShow = [[SDLShow alloc] initWithMainField1:@"test" mainField2:nil mainField3:nil mainField4:nil alignment:nil statusBar:nil mediaTrack:nil graphic:nil secondaryGraphic:nil softButtons:nil customPresets:nil metadataTags:nil templateTitle:nil windowID:nil templateConfiguration:nil];
+ OCMExpect([protocolMock sendRPC:testShow error:[OCMArg anyObjectRef]]);
+ [testManager sendRPC:testShow];
- expect(testManager.configuration.lifecycleConfig.language).toEventually(equal(SDLLanguageEnUs));
- expect(testManager.currentVRLanguage).toEventually(equal(SDLLanguageEnUs));
- expect(testManager.configuration.lifecycleConfig.appName).toEventually(equal(@"Test App"));
- expect(testManager.configuration.lifecycleConfig.shortAppName).toEventually(equal(@"Short Name"));
- expect(testManager.configuration.lifecycleConfig.ttsName).toEventually(beNil());
+ OCMVerifyAllWithDelay(protocolMock, 0.1);
});
- it(@"should update when the app supports the head unit display language", ^{
- SDLRegisterAppInterfaceResponse *registerAppInterfaceResponse = [[SDLRegisterAppInterfaceResponse alloc] init];
- registerAppInterfaceResponse.success = @YES;
- registerAppInterfaceResponse.resultCode = SDLResultWrongLanguage;
- registerAppInterfaceResponse.info = @"Language mismatch";
- registerAppInterfaceResponse.language = SDLLanguageEnUs;
- registerAppInterfaceResponse.hmiDisplayLanguage = SDLLanguageEnGb;
- testManager.registerResponse = registerAppInterfaceResponse;
-
- SDLLifecycleConfigurationUpdate *update = [[SDLLifecycleConfigurationUpdate alloc] initWithAppName:@"EnGb" shortAppName:@"Gb" ttsName:nil voiceRecognitionCommandNames:nil];
- OCMStub([testManager.delegate managerShouldUpdateLifecycleToLanguage:registerAppInterfaceResponse.language hmiLanguage:registerAppInterfaceResponse.hmiDisplayLanguage]).andReturn(update);
-
- transitionToState(SDLLifecycleStateUpdatingConfiguration);
-
- OCMExpect([protocolMock sendRPC:[OCMArg checkWithBlock:^BOOL(id value) {
- SDLChangeRegistration *changeRegistration = (SDLChangeRegistration *)value;
- expect(changeRegistration.appName).to(equal(update.appName));
- expect(changeRegistration.ngnMediaScreenAppName).to(equal(update.shortAppName));
- expect(changeRegistration.ttsName).to(beNil());
- expect(changeRegistration.vrSynonyms).to(beNil());
- return [value isKindOfClass:[SDLChangeRegistration class]];
- }]]);
-
- setToStateWithEnterTransition(SDLLifecycleStateRegistered, SDLLifecycleStateUpdatingConfiguration);
+ describe(@"when sending a SetDisplayLayout request", ^{
+ beforeEach(^{
+ OCMExpect([systemCapabilityMock setLastDisplayLayoutRequestTemplate:[OCMArg isKindOfClass:NSString.class]]);
- OCMVerifyAllWithDelay(protocolMock, 0.5);
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated"
+ SDLSetDisplayLayout *request = [[SDLSetDisplayLayout alloc] initWithPredefinedLayout:SDLPredefinedLayoutMedia];
+#pragma clang diagnostic pop
+ [testManager sendRequest:request withResponseHandler:nil];
+ });
- expect(testManager.configuration.lifecycleConfig.language).toEventually(equal(SDLLanguageEnGb));
- expect(testManager.currentVRLanguage).toEventually(equal(SDLLanguageEnUs));
- expect(testManager.configuration.lifecycleConfig.appName).toEventually(equal(@"EnGb"));
- expect(testManager.configuration.lifecycleConfig.shortAppName).toEventually(equal(@"Gb"));
- expect(testManager.configuration.lifecycleConfig.ttsName).toEventually(beNil());
+ it(@"should update the System Capability Manager with the next template type", ^{
+ OCMVerifyAllWithDelay(systemCapabilityMock, 0.3);
+ });
});
});
- });
-
- describe(@"in the ready state", ^{
- beforeEach(^{
- [testManager.lifecycleStateMachine setToState:SDLLifecycleStateReady fromOldState:nil callEnterTransition:NO];
- });
- it(@"can send an RPC of type Request", ^{
- SDLShow *testShow = [[SDLShow alloc] initWithMainField1:@"test" mainField2:nil mainField3:nil mainField4:nil alignment:nil statusBar:nil mediaTrack:nil graphic:nil secondaryGraphic:nil softButtons:nil customPresets:nil metadataTags:nil templateTitle:nil windowID:nil templateConfiguration:nil];
- OCMExpect([protocolMock sendRPC:testShow]);
- [testManager sendRPC:testShow];
+ it(@"should call the callback if the protocol fails to send a request", ^{
+ NSError *testError = [NSError sdl_lifecycle_notReadyError];
+ OCMStub([protocolMock sendRPC:[OCMArg any] error:[OCMArg setTo:testError]]).andReturn(NO);
- OCMVerifyAllWithDelay(protocolMock, 0.1);
+ SDLShow *testShow = [[SDLShow alloc] init];
+ testShow.mainField1 = @"Test";
+
+ __block SDLRPCRequest *returnRequest = nil;
+ __block SDLRPCResponse *returnResponse = nil;
+ __block NSError *returnError = nil;
+ [testManager sendRequest:testShow withResponseHandler:^(__kindof SDLRPCRequest * _Nullable request, __kindof SDLRPCResponse * _Nullable response, NSError * _Nullable error) {
+ returnRequest = request;
+ returnResponse = response;
+ returnError = error;
+ }];
+
+ expect(returnRequest).toEventuallyNot(beNil());
+ expect(returnRequest).toEventually(beAnInstanceOf([SDLShow class]));
+ expect(returnResponse).toEventually(beNil());
+ expect(returnError).toEventuallyNot(beNil());
});
it(@"can send an RPC of type Response", ^{
SDLPerformAppServiceInteractionResponse *testResponse = [[SDLPerformAppServiceInteractionResponse alloc] init];
- OCMExpect([protocolMock sendRPC:testResponse]);
+ OCMExpect([protocolMock sendRPC:testResponse error:[OCMArg anyObjectRef]]);
[testManager sendRPC:testResponse];
testResponse.correlationID = @(2);
testResponse.success = @(true);
@@ -551,7 +715,7 @@ describe(@"a lifecycle manager", ^{
it(@"can send an RPC of type Notification", ^{
SDLOnAppServiceData *testNotification = [[SDLOnAppServiceData alloc] initWithServiceData:[[SDLAppServiceData alloc] init]];
- OCMExpect([protocolMock sendRPC:testNotification]);
+ OCMExpect([protocolMock sendRPC:testNotification error:[OCMArg anyObjectRef]]);
[testManager sendRPC:testNotification];
OCMVerifyAllWithDelay(protocolMock, 0.1);
@@ -591,11 +755,12 @@ describe(@"a lifecycle manager", ^{
describe(@"stopping the manager", ^{
beforeEach(^{
+ OCMStub([protocolMock sendRPC:[OCMArg any] error:[OCMArg setTo:nil]]).andReturn(YES);
[testManager stop];
});
it(@"should attempt to unregister", ^{
- OCMVerify([protocolMock sendRPC:[OCMArg isKindOfClass:[SDLUnregisterAppInterface class]]]);
+ OCMVerify([protocolMock sendRPC:[OCMArg isKindOfClass:[SDLUnregisterAppInterface class]] error:[OCMArg anyObjectRef]]);
expect(testManager.lifecycleState).toEventually(match(SDLLifecycleStateUnregistering));
});
diff --git a/SmartDeviceLinkTests/DevAPISpecs/SDLLifecycleProtocolHandlerSpec.m b/SmartDeviceLinkTests/DevAPISpecs/SDLLifecycleProtocolHandlerSpec.m
index 58e2495e3..444cf25bd 100644
--- a/SmartDeviceLinkTests/DevAPISpecs/SDLLifecycleProtocolHandlerSpec.m
+++ b/SmartDeviceLinkTests/DevAPISpecs/SDLLifecycleProtocolHandlerSpec.m
@@ -27,7 +27,6 @@
@interface SDLLifecycleProtocolHandler ()
@property (weak, nonatomic) SDLNotificationDispatcher *notificationDispatcher;
-@property (strong, nonatomic) SDLTimer *rpcStartServiceTimeoutTimer;
@property (copy, nonatomic) NSString *appId;
@end
@@ -49,7 +48,6 @@ describe(@"SDLLifecycleProtocolHandler tests", ^{
SDLConfiguration *testConfig = [[SDLConfiguration alloc] initWithLifecycle:testLifecycleConfig lockScreen:nil logging:nil fileManager:nil encryption:nil];
testHandler = [[SDLLifecycleProtocolHandler alloc] initWithProtocol:mockProtocol notificationDispatcher:mockNotificationDispatcher configuration:testConfig];
- testHandler.rpcStartServiceTimeoutTimer = mockTimer;
});
describe(@"when started", ^{
@@ -79,14 +77,12 @@ describe(@"SDLLifecycleProtocolHandler tests", ^{
beforeEach(^{
OCMExpect([mockNotificationDispatcher postNotificationName:[OCMArg isEqual:SDLTransportDidConnect] infoObject:[OCMArg isNil]]);
OCMExpect([mockProtocol startServiceWithType:0 payload:[OCMArg any]]).ignoringNonObjectArgs();
- OCMExpect([(SDLTimer *)mockTimer start]);
[testHandler protocolDidOpen:mockProtocol];
});
it(@"should set everything up", ^{
OCMVerifyAll(mockNotificationDispatcher);
OCMVerifyAll(mockProtocol);
- OCMVerifyAll(mockTimer);
});
});
@@ -120,14 +116,12 @@ describe(@"SDLLifecycleProtocolHandler tests", ^{
SDLProtocolMessage *message = [SDLProtocolMessage messageWithHeader:header andPayload:nil];
OCMExpect([mockNotificationDispatcher postNotificationName:[OCMArg isEqual:SDLRPCServiceDidConnect] infoObject:[OCMArg isNil]]);
- OCMExpect([(SDLTimer *)mockTimer cancel]);
[testHandler protocol:mockProtocol didReceiveStartServiceACK:message];
});
it(@"should stop the timer and send a notification", ^{
OCMVerifyAll(mockNotificationDispatcher);
- OCMVerifyAll(mockTimer);
});
});
@@ -139,24 +133,18 @@ describe(@"SDLLifecycleProtocolHandler tests", ^{
SDLProtocolMessage *message = [SDLProtocolMessage messageWithHeader:header andPayload:nil];
OCMExpect([mockNotificationDispatcher postNotificationName:[OCMArg isEqual:SDLRPCServiceConnectionDidError] infoObject:[OCMArg isNil]]);
- OCMExpect([(SDLTimer *)mockTimer cancel]);
[testHandler protocol:mockProtocol didReceiveStartServiceNAK:message];
});
it(@"should stop the timer and send a notification", ^{
OCMVerifyAll(mockNotificationDispatcher);
- OCMVerifyAll(mockTimer);
});
});
- context(@"no response from the module to the RPC Start Service", ^{
- beforeEach(^{
- testHandler.rpcStartServiceTimeoutTimer = nil;
- });
-
- it(@"should send a transport disconnected notification when the timer elapses", ^{
- OCMExpect([mockProtocol stopWithCompletionHandler:[OCMArg any]]);
+ context(@"and there is no response from the module to the RPC Start Service", ^{
+ it(@"should not send a transport disconnected notification", ^{
+ OCMReject([mockProtocol stopWithCompletionHandler:[OCMArg any]]);
[testHandler protocolDidOpen:mockProtocol];
@@ -172,14 +160,12 @@ describe(@"SDLLifecycleProtocolHandler tests", ^{
SDLProtocolMessage *message = [SDLProtocolMessage messageWithHeader:header andPayload:nil];
OCMExpect([mockNotificationDispatcher postNotificationName:[OCMArg isEqual:SDLRPCServiceDidDisconnect] infoObject:[OCMArg isNil]]);
- OCMExpect([(SDLTimer *)mockTimer cancel]);
[testHandler protocol:mockProtocol didReceiveEndServiceACK:message];
});
it(@"should stop the timer and send a notification", ^{
OCMVerifyAll(mockNotificationDispatcher);
- OCMVerifyAll(mockTimer);
});
});
diff --git a/SmartDeviceLinkTests/DevAPISpecs/SDLMenuCellSpec.m b/SmartDeviceLinkTests/DevAPISpecs/SDLMenuCellSpec.m
index 485b02b6d..f6d982a57 100644
--- a/SmartDeviceLinkTests/DevAPISpecs/SDLMenuCellSpec.m
+++ b/SmartDeviceLinkTests/DevAPISpecs/SDLMenuCellSpec.m
@@ -10,54 +10,122 @@ describe(@"a menu cell", ^{
__block SDLMenuCell *testCell = nil;
__block SDLMenuCell *testCell2 = nil;
__block SDLMenuLayout testLayout = SDLMenuLayoutList;
+ __block NSString *someTitle = nil;
+ __block NSString *someSecondaryTitle = nil;
+ __block NSString *someTertiaryTitle = nil;
+ __block SDLArtwork *someArtwork = nil;
+ __block SDLArtwork *someSecondaryArtwork = nil;
+
+ beforeEach(^{
+ someTitle = @"Some Title";
+ someSecondaryTitle = @"Some Title 2";
+ someTertiaryTitle = @"Some Title 3";
+ someArtwork = [[SDLArtwork alloc] initWithData:[[NSData alloc] initWithBase64EncodedString:@"data" options:kNilOptions] name:@"Some artwork" fileExtension:@"png" persistent:NO];
+ someSecondaryArtwork = [[SDLArtwork alloc] initWithData:[[NSData alloc] initWithBase64EncodedString:@"data" options:kNilOptions] name:@"Some artwork 2" fileExtension:@"png" persistent:NO];
+ });
describe(@"initializing", ^{
- __block NSString *someTitle = nil;
- __block SDLArtwork *someArtwork = nil;
__block NSArray<NSString *> *someVoiceCommands = nil;
__block NSArray<SDLMenuCell *> *someSubcells = nil;
beforeEach(^{
- someTitle = @"Some Title";
- someArtwork = [[SDLArtwork alloc] initWithData:[[NSData alloc] initWithBase64EncodedString:@"data" options:kNilOptions] name:@"Some artwork" fileExtension:@"png" persistent:NO];
someVoiceCommands = @[@"some command"];
- SDLMenuCell *subcell = [[SDLMenuCell alloc] initWithTitle:@"Hello" icon:nil voiceCommands:nil handler:^(SDLTriggerSource _Nonnull triggerSource) {}];
+ SDLMenuCell *subcell = [[SDLMenuCell alloc] initWithTitle:@"Hello" secondaryText:nil tertiaryText:nil icon:nil secondaryArtwork:nil voiceCommands:nil handler:^(SDLTriggerSource _Nonnull triggerSource) {}];
someSubcells = @[subcell];
});
- it(@"should initialize properly as a menu item", ^{
+ it(@"should set initWithTitle:icon:submenuLayout:subCells: propertly", ^{
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
+ testCell = [[SDLMenuCell alloc] initWithTitle:someTitle icon:someArtwork submenuLayout:testLayout subCells:someSubcells];
+#pragma clang diagnostic pop
+
+ expect(testCell.title).to(equal(someTitle));
+ expect(testCell.icon).to(equal(someArtwork));
+ expect(testCell.voiceCommands).to(beNil());
+ expect(testCell.subCells).to(equal(someSubcells));
+ expect(testCell.secondaryText).to(beNil());
+ expect(testCell.tertiaryText).to(beNil());
+ expect(testCell.secondaryArtwork).to(beNil());
+ });
+
+ it(@"should set initWithTitle:icon:voiceCommands:handler: properly", ^{
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
testCell = [[SDLMenuCell alloc] initWithTitle:someTitle icon:someArtwork voiceCommands:someVoiceCommands handler:^(SDLTriggerSource _Nonnull triggerSource) {}];
+#pragma clang diagnostic pop
expect(testCell.title).to(equal(someTitle));
expect(testCell.icon).to(equal(someArtwork));
expect(testCell.voiceCommands).to(equal(someVoiceCommands));
expect(testCell.subCells).to(beNil());
+ expect(testCell.uniqueTitle).to(equal(someTitle));
+ expect(testCell.secondaryText).to(beNil());
+ expect(testCell.tertiaryText).to(beNil());
+ expect(testCell.secondaryArtwork).to(beNil());
});
- it(@"should initialize properly as a submenu item with icon and layout", ^{
- testCell = [[SDLMenuCell alloc] initWithTitle:someTitle icon:someArtwork submenuLayout:testLayout subCells:someSubcells];
+ it(@"should set initWithTitle:icon:voiceCommands:secondaryText:tertiaryText:secondaryArtwork:handler: properly", ^{
+ testCell = [[SDLMenuCell alloc] initWithTitle:someTitle secondaryText:someSecondaryTitle tertiaryText:someTertiaryTitle icon:someArtwork secondaryArtwork:someSecondaryArtwork voiceCommands:someVoiceCommands handler:^(SDLTriggerSource _Nonnull triggerSource) {}];
+
+ expect(testCell.title).to(equal(someTitle));
+ expect(testCell.icon).to(equal(someArtwork));
+ expect(testCell.voiceCommands).to(equal(someVoiceCommands));
+ expect(testCell.subCells).to(beNil());
+ expect(testCell.secondaryText).to(equal(someSecondaryTitle));
+ expect(testCell.tertiaryText).to(equal(someTertiaryTitle));
+ expect(testCell.secondaryArtwork).to(equal(someSecondaryArtwork));
+ });
+
+ it(@"should initWithTitle:icon:submenuLayout:subCells:secondaryText:tertiaryText:secondaryArtwork: initialize", ^{
+ testCell = [[SDLMenuCell alloc] initWithTitle:someTitle secondaryText:someSecondaryTitle tertiaryText:someTertiaryTitle icon:someArtwork secondaryArtwork:someSecondaryArtwork submenuLayout:testLayout subCells:someSubcells];
expect(testCell.title).to(equal(someTitle));
expect(testCell.icon).to(equal(someArtwork));
expect(testCell.voiceCommands).to(beNil());
expect(testCell.subCells).to(equal(someSubcells));
expect(testCell.submenuLayout).to(equal(testLayout));
+ expect(testCell.uniqueTitle).to(equal(someTitle));
+ expect(testCell.secondaryText).to(equal(someSecondaryTitle));
+ expect(testCell.tertiaryText).to(equal(someTertiaryTitle));
+ expect(testCell.secondaryArtwork).to(equal(someSecondaryArtwork));
});
});
- describe(@"check cell eqality", ^{
+
+ describe(@"check cell equality", ^{
+ it(@"should compare cells and return true if cells equal", ^{
+ testCell = [[SDLMenuCell alloc] initWithTitle:someTitle secondaryText:someSecondaryTitle tertiaryText:someTertiaryTitle icon:nil secondaryArtwork:someSecondaryArtwork submenuLayout:testLayout subCells:@[]];
+ testCell2 = [[SDLMenuCell alloc] initWithTitle:someTitle secondaryText:someSecondaryTitle tertiaryText:someTertiaryTitle icon:nil secondaryArtwork:someSecondaryArtwork submenuLayout:testLayout subCells:@[]];
+
+ expect([testCell isEqual:testCell2]).to(beTrue());
+ });
+
+ it(@"should compare cells and return false if not equal ", ^{
+ testCell = [[SDLMenuCell alloc] initWithTitle:@"True" secondaryText:someSecondaryTitle tertiaryText:someTertiaryTitle icon:nil secondaryArtwork:someSecondaryArtwork submenuLayout:testLayout subCells:@[]];
+ testCell2 = [[SDLMenuCell alloc] initWithTitle:@"False" secondaryText:nil tertiaryText:nil icon:nil secondaryArtwork:nil submenuLayout:testLayout subCells:@[]];
+
+ expect([testCell isEqual:testCell2]).to(beFalse());
+ });
+
it(@"should compare cells and return true if cells equal", ^{
- testCell = [[SDLMenuCell alloc] initWithTitle:@"Title" icon:nil submenuLayout:testLayout subCells:@[]];
- testCell2 = [[SDLMenuCell alloc] initWithTitle:@"Title" icon:nil submenuLayout:testLayout subCells:@[]];
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
+ testCell = [[SDLMenuCell alloc] initWithTitle:someTitle icon:nil submenuLayout:testLayout subCells:@[]];
+ testCell2 = [[SDLMenuCell alloc] initWithTitle:someTitle icon:nil submenuLayout:testLayout subCells:@[]];
+#pragma clang diagnostic pop
- expect([testCell isEqual:testCell2]).to(equal(true));
+ expect([testCell isEqual:testCell2]).to(beTrue());
});
it(@"should compare cells and return false if not equal ", ^{
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
testCell = [[SDLMenuCell alloc] initWithTitle:@"True" icon:nil submenuLayout:testLayout subCells:@[]];
testCell2 = [[SDLMenuCell alloc] initWithTitle:@"False" icon:nil submenuLayout:testLayout subCells:@[]];
+#pragma clang diagnostic pop
- expect([testCell isEqual:testCell2]).to(equal(false));
+ expect([testCell isEqual:testCell2]).to(beFalse());
});
});
});
diff --git a/SmartDeviceLinkTests/DevAPISpecs/SDLPreloadChoicesOperationSpec.m b/SmartDeviceLinkTests/DevAPISpecs/SDLPreloadChoicesOperationSpec.m
index 144f54e3d..fc6fdfe00 100644
--- a/SmartDeviceLinkTests/DevAPISpecs/SDLPreloadChoicesOperationSpec.m
+++ b/SmartDeviceLinkTests/DevAPISpecs/SDLPreloadChoicesOperationSpec.m
@@ -16,6 +16,18 @@
#import "SDLWindowCapability.h"
#import "TestConnectionManager.h"
+@interface SDLPreloadChoicesOperation()
+
+@property (strong, nonatomic, nullable) NSMutableArray<NSNumber *> *failedChoiceUploadIDs;
+
+@end
+
+@interface SDLChoiceCell()
+
+@property (assign, nonatomic) UInt16 choiceId;
+
+@end
+
QuickSpecBegin(SDLPreloadChoicesOperationSpec)
describe(@"a preload choices operation", ^{
@@ -58,8 +70,8 @@ describe(@"a preload choices operation", ^{
});
context(@"with artworks", ^{
- __block NSSet<SDLChoiceCell *> *cellsWithArtwork = nil;
- __block NSSet<SDLChoiceCell *> *cellsWithStaticIcon = nil;
+ __block NSOrderedSet<SDLChoiceCell *> *cellsWithArtwork = nil;
+ __block NSOrderedSet<SDLChoiceCell *> *cellsWithStaticIcon = nil;
__block NSString *art1Name = @"Art1Name";
__block NSString *art2Name = @"Art2Name";
__block SDLArtwork *cell1Art2 = [[SDLArtwork alloc] initWithData:cellArtData2 name:art1Name fileExtension:@"png" persistent:NO];
@@ -74,8 +86,8 @@ describe(@"a preload choices operation", ^{
SDLArtwork *staticIconArt = [SDLArtwork artworkWithStaticIcon:SDLStaticIconNameDate];
SDLChoiceCell *cellWithStaticIcon = [[SDLChoiceCell alloc] initWithText:@"Static Icon" secondaryText:nil tertiaryText:nil voiceCommands:nil artwork:staticIconArt secondaryArtwork:nil];
- cellsWithArtwork = [NSSet setWithArray:@[cell1WithArt, cell2WithArtAndSecondary]];
- cellsWithStaticIcon = [NSSet setWithArray:@[cellWithStaticIcon]];
+ cellsWithArtwork = [[NSOrderedSet alloc] initWithArray:@[cell1WithArt, cell2WithArtAndSecondary]];
+ cellsWithStaticIcon = [[NSOrderedSet alloc] initWithArray:@[cellWithStaticIcon]];
});
context(@"if the menuName is not set", ^{
@@ -84,7 +96,7 @@ describe(@"a preload choices operation", ^{
primaryTextField.name = SDLTextFieldNameMenuName;
windowCapability.textFields = @[];
- testOp = [[SDLPreloadChoicesOperation alloc] initWithConnectionManager:testConnectionManager fileManager:testFileManager displayName:testDisplayName windowCapability:windowCapability isVROptional:NO cellsToPreload:cellsWithArtwork];
+ testOp = [[SDLPreloadChoicesOperation alloc] initWithConnectionManager:testConnectionManager fileManager:testFileManager displayName:testDisplayName windowCapability:windowCapability isVROptional:NO cellsToPreload:cellsWithArtwork updateCompletionHandler:^(NSArray<NSNumber *> * _Nullable failedChoiceUploadIDs) {}];
[testOp start];
NSArray<SDLCreateInteractionChoiceSet *> *receivedRequests = (NSArray<SDLCreateInteractionChoiceSet *> *)testConnectionManager.receivedRequests;
@@ -95,7 +107,7 @@ describe(@"a preload choices operation", ^{
context(@"only main text capabilities", ^{
it(@"should skip to preloading cells", ^{
- testOp = [[SDLPreloadChoicesOperation alloc] initWithConnectionManager:testConnectionManager fileManager:testFileManager displayName:testDisplayName windowCapability:windowCapability isVROptional:NO cellsToPreload:cellsWithArtwork];
+ testOp = [[SDLPreloadChoicesOperation alloc] initWithConnectionManager:testConnectionManager fileManager:testFileManager displayName:testDisplayName windowCapability:windowCapability isVROptional:NO cellsToPreload:cellsWithArtwork updateCompletionHandler:^(NSArray<NSNumber *> * _Nullable failedChoiceUploadIDs) {}];
[testOp start];
expect(@(testOp.currentState)).to(equal(SDLPreloadChoicesOperationStatePreloadingChoices));
@@ -110,7 +122,7 @@ describe(@"a preload choices operation", ^{
OCMStub([testFileManager hasUploadedFile:[OCMArg isNotNil]]).andReturn(NO);
- testOp = [[SDLPreloadChoicesOperation alloc] initWithConnectionManager:testConnectionManager fileManager:testFileManager displayName:testDisplayName windowCapability:windowCapability isVROptional:NO cellsToPreload:cellsWithArtwork];
+ testOp = [[SDLPreloadChoicesOperation alloc] initWithConnectionManager:testConnectionManager fileManager:testFileManager displayName:testDisplayName windowCapability:windowCapability isVROptional:NO cellsToPreload:cellsWithArtwork updateCompletionHandler:^(NSArray<NSNumber *> * _Nullable failedChoiceUploadIDs) {}];
[testOp start];
});
@@ -137,7 +149,7 @@ describe(@"a preload choices operation", ^{
beforeEach(^{
OCMStub([testFileManager hasUploadedFile:[OCMArg isNotNil]]).andReturn(YES);
- testOp = [[SDLPreloadChoicesOperation alloc] initWithConnectionManager:testConnectionManager fileManager:testFileManager displayName:testDisplayName windowCapability:windowCapability isVROptional:NO cellsToPreload:cellsWithArtwork];
+ testOp = [[SDLPreloadChoicesOperation alloc] initWithConnectionManager:testConnectionManager fileManager:testFileManager displayName:testDisplayName windowCapability:windowCapability isVROptional:NO cellsToPreload:cellsWithArtwork updateCompletionHandler:^(NSArray<NSNumber *> * _Nullable failedChoiceUploadIDs) {}];
[testOp start];
});
@@ -159,9 +171,9 @@ describe(@"a preload choices operation", ^{
SDLArtwork *staticIconArt = [SDLArtwork artworkWithStaticIcon:SDLStaticIconNameDate];
SDLChoiceCell *cellWithStaticIcon = [[SDLChoiceCell alloc] initWithText:@"Static Icon" secondaryText:nil tertiaryText:nil voiceCommands:nil artwork:staticIconArt secondaryArtwork:nil];
- cellsWithArtwork = [NSSet setWithArray:@[cell1WithArt, cell2WithArtAndSecondary]];
- cellsWithStaticIcon = [NSSet setWithArray:@[cellWithStaticIcon]];
- testOp = [[SDLPreloadChoicesOperation alloc] initWithConnectionManager:testConnectionManager fileManager:testFileManager displayName:testDisplayName windowCapability:windowCapability isVROptional:NO cellsToPreload:cellsWithArtwork];
+ cellsWithArtwork = [[NSOrderedSet alloc] initWithArray:@[cell1WithArt, cell2WithArtAndSecondary]];
+ cellsWithStaticIcon = [[NSOrderedSet alloc] initWithArray:@[cellWithStaticIcon]];
+ testOp = [[SDLPreloadChoicesOperation alloc] initWithConnectionManager:testConnectionManager fileManager:testFileManager displayName:testDisplayName windowCapability:windowCapability isVROptional:NO cellsToPreload:cellsWithArtwork updateCompletionHandler:^(NSArray<NSNumber *> * _Nullable failedChoiceUploadIDs) {}];
[testOp start];
OCMExpect([testFileManager uploadArtworks:[OCMArg any] completionHandler:[OCMArg any]]);
@@ -171,7 +183,7 @@ describe(@"a preload choices operation", ^{
context(@"when artworks are static icons", ^{
beforeEach(^{
- testOp = [[SDLPreloadChoicesOperation alloc] initWithConnectionManager:testConnectionManager fileManager:testFileManager displayName:testDisplayName windowCapability:windowCapability isVROptional:NO cellsToPreload:cellsWithStaticIcon];
+ testOp = [[SDLPreloadChoicesOperation alloc] initWithConnectionManager:testConnectionManager fileManager:testFileManager displayName:testDisplayName windowCapability:windowCapability isVROptional:NO cellsToPreload:cellsWithStaticIcon updateCompletionHandler:^(NSArray<NSNumber *> * _Nullable failedChoiceUploadIDs) {}];
[testOp start];
});
@@ -184,7 +196,7 @@ describe(@"a preload choices operation", ^{
beforeEach(^{
OCMStub([testFileManager hasUploadedFile:[OCMArg isNotNil]]).andReturn(NO);
- testOp = [[SDLPreloadChoicesOperation alloc] initWithConnectionManager:testConnectionManager fileManager:testFileManager displayName:testDisplayName windowCapability:windowCapability isVROptional:NO cellsToPreload:cellsWithArtwork];
+ testOp = [[SDLPreloadChoicesOperation alloc] initWithConnectionManager:testConnectionManager fileManager:testFileManager displayName:testDisplayName windowCapability:windowCapability isVROptional:NO cellsToPreload:cellsWithArtwork updateCompletionHandler:^(NSArray<NSNumber *> * _Nullable failedChoiceUploadIDs) {}];
[testOp start];
});
@@ -200,12 +212,12 @@ describe(@"a preload choices operation", ^{
});
context(@"without artworks", ^{
- __block NSSet<SDLChoiceCell *> *cellsWithoutArtwork = nil;
+ __block NSOrderedSet<SDLChoiceCell *> *cellsWithoutArtwork = nil;
beforeEach(^{
SDLChoiceCell *cellBasic = [[SDLChoiceCell alloc] initWithText:@"Cell1" artwork:nil voiceCommands:nil];
SDLChoiceCell *cellWithVR = [[SDLChoiceCell alloc] initWithText:@"Cell2" secondaryText:nil tertiaryText:nil voiceCommands:@[@"Cell2"] artwork:nil secondaryArtwork:nil];
SDLChoiceCell *cellWithAllText = [[SDLChoiceCell alloc] initWithText:@"Cell2" secondaryText:@"Cell2" tertiaryText:@"Cell2" voiceCommands:nil artwork:nil secondaryArtwork:nil];
- cellsWithoutArtwork = [NSSet setWithArray:@[cellBasic, cellWithVR, cellWithAllText]];
+ cellsWithoutArtwork = [[NSOrderedSet alloc] initWithArray:@[cellBasic, cellWithVR, cellWithAllText]];
});
it(@"should skip to preloading cells", ^{
@@ -214,7 +226,7 @@ describe(@"a preload choices operation", ^{
describe(@"assembling choices", ^{
it(@"should be correct with no text and VR required", ^{
- testOp = [[SDLPreloadChoicesOperation alloc] initWithConnectionManager:testConnectionManager fileManager:testFileManager displayName:testDisplayName windowCapability:windowCapability isVROptional:NO cellsToPreload:cellsWithoutArtwork];
+ testOp = [[SDLPreloadChoicesOperation alloc] initWithConnectionManager:testConnectionManager fileManager:testFileManager displayName:testDisplayName windowCapability:windowCapability isVROptional:NO cellsToPreload:cellsWithoutArtwork updateCompletionHandler:^(NSArray<NSNumber *> * _Nullable failedChoiceUploadIDs) {}];
[testOp start];
NSArray<SDLCreateInteractionChoiceSet *> *receivedRequests = (NSArray<SDLCreateInteractionChoiceSet *> *)testConnectionManager.receivedRequests;
@@ -230,7 +242,7 @@ describe(@"a preload choices operation", ^{
primaryTextField.name = SDLTextFieldNameMenuName;
windowCapability.textFields = @[primaryTextField];
- testOp = [[SDLPreloadChoicesOperation alloc] initWithConnectionManager:testConnectionManager fileManager:testFileManager displayName:testDisplayName windowCapability:windowCapability isVROptional:NO cellsToPreload:cellsWithoutArtwork];
+ testOp = [[SDLPreloadChoicesOperation alloc] initWithConnectionManager:testConnectionManager fileManager:testFileManager displayName:testDisplayName windowCapability:windowCapability isVROptional:NO cellsToPreload:cellsWithoutArtwork updateCompletionHandler:^(NSArray<NSNumber *> * _Nullable failedChoiceUploadIDs) {}];
[testOp start];
NSArray<SDLCreateInteractionChoiceSet *> *receivedRequests = (NSArray<SDLCreateInteractionChoiceSet *> *)testConnectionManager.receivedRequests;
@@ -249,7 +261,7 @@ describe(@"a preload choices operation", ^{
secondaryTextField.name = SDLTextFieldNameSecondaryText;
windowCapability.textFields = @[primaryTextField, secondaryTextField];
- testOp = [[SDLPreloadChoicesOperation alloc] initWithConnectionManager:testConnectionManager fileManager:testFileManager displayName:testDisplayName windowCapability:windowCapability isVROptional:NO cellsToPreload:cellsWithoutArtwork];
+ testOp = [[SDLPreloadChoicesOperation alloc] initWithConnectionManager:testConnectionManager fileManager:testFileManager displayName:testDisplayName windowCapability:windowCapability isVROptional:NO cellsToPreload:cellsWithoutArtwork updateCompletionHandler:^(NSArray<NSNumber *> * _Nullable failedChoiceUploadIDs) {}];
[testOp start];
NSArray<SDLCreateInteractionChoiceSet *> *receivedRequests = (NSArray<SDLCreateInteractionChoiceSet *> *)testConnectionManager.receivedRequests;
@@ -270,7 +282,7 @@ describe(@"a preload choices operation", ^{
tertiaryTextField.name = SDLTextFieldNameTertiaryText;
windowCapability.textFields = @[primaryTextField, secondaryTextField, tertiaryTextField];
- testOp = [[SDLPreloadChoicesOperation alloc] initWithConnectionManager:testConnectionManager fileManager:testFileManager displayName:testDisplayName windowCapability:windowCapability isVROptional:NO cellsToPreload:cellsWithoutArtwork];
+ testOp = [[SDLPreloadChoicesOperation alloc] initWithConnectionManager:testConnectionManager fileManager:testFileManager displayName:testDisplayName windowCapability:windowCapability isVROptional:NO cellsToPreload:cellsWithoutArtwork updateCompletionHandler:^(NSArray<NSNumber *> * _Nullable failedChoiceUploadIDs) {}];
[testOp start];
NSArray<SDLCreateInteractionChoiceSet *> *receivedRequests = (NSArray<SDLCreateInteractionChoiceSet *> *)testConnectionManager.receivedRequests;
@@ -283,8 +295,7 @@ describe(@"a preload choices operation", ^{
});
it(@"should be correct with VR optional", ^{
-
- testOp = [[SDLPreloadChoicesOperation alloc] initWithConnectionManager:testConnectionManager fileManager:testFileManager displayName:testDisplayName windowCapability:windowCapability isVROptional:YES cellsToPreload:cellsWithoutArtwork];
+ testOp = [[SDLPreloadChoicesOperation alloc] initWithConnectionManager:testConnectionManager fileManager:testFileManager displayName:testDisplayName windowCapability:windowCapability isVROptional:YES cellsToPreload:cellsWithoutArtwork updateCompletionHandler:^(NSArray<NSNumber *> * _Nullable failedChoiceUploadIDs) {}];
[testOp start];
NSArray<SDLCreateInteractionChoiceSet *> *receivedRequests = (NSArray<SDLCreateInteractionChoiceSet *> *)testConnectionManager.receivedRequests;
@@ -297,6 +308,109 @@ describe(@"a preload choices operation", ^{
});
});
});
+
+ context(@"updating choices", ^{
+ __block SDLChoiceCell *testCell1 = nil;
+ __block SDLChoiceCell *testCell2 = nil;
+ __block NSOrderedSet<SDLChoiceCell *> *testCells = nil;
+
+ beforeEach(^{
+ testCell1 = [[SDLChoiceCell alloc] initWithText:@"Cell1" artwork:nil voiceCommands:nil];
+ testCell2 = [[SDLChoiceCell alloc] initWithText:@"Cell2" secondaryText:nil tertiaryText:nil voiceCommands:nil artwork:nil secondaryArtwork:[SDLArtwork artworkWithStaticIcon:SDLStaticIconNameClock]];
+ testCells = [[NSOrderedSet alloc] initWithArray:@[testCell1, testCell2]];
+ });
+
+ describe(@"if a choice item is removed", ^{
+ it(@"should be removed if the removal is attempted while the operation is pending", ^{
+ testOp = [[SDLPreloadChoicesOperation alloc] initWithConnectionManager:testConnectionManager fileManager:testFileManager displayName:testDisplayName windowCapability:windowCapability isVROptional:NO cellsToPreload:testCells updateCompletionHandler:^(NSArray<NSNumber *> * _Nullable failedChoiceUploadIDs) {}];
+ [testOp removeChoicesFromUpload:[NSSet setWithArray:@[testCell1]]];
+ [testOp start];
+
+ NSArray<SDLCreateInteractionChoiceSet *> *receivedRequests = (NSArray<SDLCreateInteractionChoiceSet *> *)testConnectionManager.receivedRequests;
+
+ expect(receivedRequests).to(haveCount(1));
+ expect(receivedRequests[0].choiceSet[0].menuName).to(equal(testCell2.text));
+ });
+
+ it(@"should not be removed if the removal is attempted while operation is executing", ^{
+ SDLTextField *primaryTextField = [[SDLTextField alloc] init];
+ primaryTextField.name = SDLTextFieldNameMenuName;
+ windowCapability.textFields = @[primaryTextField];
+
+ testOp = [[SDLPreloadChoicesOperation alloc] initWithConnectionManager:testConnectionManager fileManager:testFileManager displayName:testDisplayName windowCapability:windowCapability isVROptional:NO cellsToPreload:testCells updateCompletionHandler:^(NSArray<NSNumber *> * _Nullable failedChoiceUploadIDs) {}];
+ [testOp start];
+ [testOp removeChoicesFromUpload:[NSSet setWithArray:@[testCell1]]];
+
+ NSArray<SDLCreateInteractionChoiceSet *> *receivedRequests = (NSArray<SDLCreateInteractionChoiceSet *> *)testConnectionManager.receivedRequests;
+
+ expect(receivedRequests).to(haveCount(2));
+ expect(receivedRequests[0].choiceSet[0].menuName).to(equal(testCell1.text));
+ expect(receivedRequests[1].choiceSet[0].menuName).to(equal(testCell2.text));
+ });
+ });
+ });
+
+ describe(@"the module's response to choice uploads", ^{
+ __block SDLChoiceCell *testCell1 = nil;
+ __block SDLChoiceCell *testCell2 = nil;
+ __block NSOrderedSet<SDLChoiceCell *> *testCells = nil;
+ __block SDLCreateInteractionChoiceSetResponse *testBadResponse = nil;
+ __block SDLCreateInteractionChoiceSetResponse *testGoodResponse = nil;
+
+ beforeEach(^{
+ testCell1 = [[SDLChoiceCell alloc] initWithText:@"Cell1" artwork:nil voiceCommands:nil];
+ testCell1.choiceId = 55;
+ testCell2 = [[SDLChoiceCell alloc] initWithText:@"Cell2" secondaryText:nil tertiaryText:nil voiceCommands:nil artwork:nil secondaryArtwork:[SDLArtwork artworkWithStaticIcon:SDLStaticIconNameClock]];
+ testCell2.choiceId = 66;
+ testCells = [[NSOrderedSet alloc] initWithArray:@[testCell1, testCell2]];
+
+ testBadResponse = [[SDLCreateInteractionChoiceSetResponse alloc] init];
+ testBadResponse.success = @NO;
+ testBadResponse.resultCode = SDLResultRejected;
+
+ testGoodResponse = [[SDLCreateInteractionChoiceSetResponse alloc] init];
+ testGoodResponse.success = @YES;
+ testGoodResponse.resultCode = SDLResultSuccess;
+ });
+
+ context(@"when a bad response comes back", ^{
+ it(@"should add the choiceID of the failed choice item to the failedChoiceUploadIDs array", ^{
+ testOp = [[SDLPreloadChoicesOperation alloc] initWithConnectionManager:testConnectionManager fileManager:testFileManager displayName:testDisplayName windowCapability:windowCapability isVROptional:NO cellsToPreload:testCells updateCompletionHandler:^(NSArray<NSNumber *> * _Nullable failedChoiceUploadIDs) {}];
+ [testOp start];
+
+ NSArray<SDLCreateInteractionChoiceSet *> *receivedRequests = (NSArray<SDLCreateInteractionChoiceSet *> *)testConnectionManager.receivedRequests;
+
+ expect(receivedRequests).to(haveCount(2));
+ expect(receivedRequests[0].choiceSet[0].menuName).to(equal(testCell1.text));
+ expect(receivedRequests[1].choiceSet[0].menuName).to(equal(testCell2.text));
+
+ [testConnectionManager respondToRequestWithResponse:testGoodResponse requestNumber:0 error:nil];
+ [testConnectionManager respondToRequestWithResponse:testBadResponse requestNumber:1 error:[NSError errorWithDomain:SDLErrorDomainChoiceSetManager code:SDLChoiceSetManagerErrorUploadFailed userInfo:nil]];
+
+ expect(testOp.failedChoiceUploadIDs.count).to(equal(1));
+ expect(testOp.failedChoiceUploadIDs).to(contain(@(testCell2.choiceId)));
+ expect(testOp.failedChoiceUploadIDs).toNot(contain(@(testCell1.choiceId)));
+ });
+ });
+
+ context(@"when only good responses comes back", ^{
+ it(@"should leave the failedChoiceUploadIDs array empty", ^{
+ testOp = [[SDLPreloadChoicesOperation alloc] initWithConnectionManager:testConnectionManager fileManager:testFileManager displayName:testDisplayName windowCapability:windowCapability isVROptional:NO cellsToPreload:testCells updateCompletionHandler:^(NSArray<NSNumber *> * _Nullable failedChoiceUploadIDs) {}];
+ [testOp start];
+
+ NSArray<SDLCreateInteractionChoiceSet *> *receivedRequests = (NSArray<SDLCreateInteractionChoiceSet *> *)testConnectionManager.receivedRequests;
+
+ expect(receivedRequests).to(haveCount(2));
+ expect(receivedRequests[0].choiceSet[0].menuName).to(equal(testCell1.text));
+ expect(receivedRequests[1].choiceSet[0].menuName).to(equal(testCell2.text));
+
+ [testConnectionManager respondToRequestWithResponse:testGoodResponse requestNumber:0 error:nil];
+ [testConnectionManager respondToRequestWithResponse:testGoodResponse requestNumber:1 error:nil];
+
+ expect(testOp.failedChoiceUploadIDs).to(beEmpty());
+ });
+ });
+ });
});
});
diff --git a/SmartDeviceLinkTests/DevAPISpecs/SDLPresentChoiceSetOperationSpec.m b/SmartDeviceLinkTests/DevAPISpecs/SDLPresentChoiceSetOperationSpec.m
index 883720b82..f918c6909 100644
--- a/SmartDeviceLinkTests/DevAPISpecs/SDLPresentChoiceSetOperationSpec.m
+++ b/SmartDeviceLinkTests/DevAPISpecs/SDLPresentChoiceSetOperationSpec.m
@@ -46,6 +46,7 @@ describe(@"present choice operation", ^{
__block BOOL hasCalledOperationCompletionHandler = NO;
__block NSError *resultError = nil;
+ __block SDLWindowCapability *windowCapability = nil;
beforeEach(^{
resultError = nil;
@@ -58,9 +59,10 @@ describe(@"present choice operation", ^{
testChoices = @[cell1];
testChoiceSet = [[SDLChoiceSet alloc] initWithTitle:@"Test Title" delegate:testChoiceDelegate layout:SDLChoiceSetLayoutTiles timeout:13 initialPromptString:@"Test initial prompt" timeoutPromptString:@"Test timeout prompt" helpPromptString:@"Test help prompt" vrHelpList:nil choices:testChoices];
+ windowCapability = [[SDLWindowCapability alloc] init];
testKeyboardDelegate = OCMProtocolMock(@protocol(SDLKeyboardDelegate));
OCMStub([testKeyboardDelegate customKeyboardConfiguration]).andReturn(nil);
- testKeyboardProperties = [[SDLKeyboardProperties alloc] initWithLanguage:SDLLanguageArSa keyboardLayout:SDLKeyboardLayoutAZERTY keypressMode:SDLKeypressModeResendCurrentEntry limitedCharacterList:nil autoCompleteList:nil];
+ testKeyboardProperties = [[SDLKeyboardProperties alloc] initWithLanguage:SDLLanguageArSa keyboardLayout:SDLKeyboardLayoutAZERTY keypressMode:SDLKeypressModeResendCurrentEntry limitedCharacterList:nil autoCompleteList:nil maskInputCharacters:nil customKeys:nil];
});
it(@"should have a priority of 'normal'", ^{
@@ -71,7 +73,7 @@ describe(@"present choice operation", ^{
describe(@"running a non-searchable choice set operation", ^{
beforeEach(^{
- testOp = [[SDLPresentChoiceSetOperation alloc] initWithConnectionManager:testConnectionManager choiceSet:testChoiceSet mode:testInteractionMode keyboardProperties:nil keyboardDelegate:nil cancelID:testCancelID];
+ testOp = [[SDLPresentChoiceSetOperation alloc] initWithConnectionManager:testConnectionManager choiceSet:testChoiceSet mode:testInteractionMode keyboardProperties:nil keyboardDelegate:nil cancelID:testCancelID windowCapability:windowCapability];
testOp.completionBlock = ^{
hasCalledOperationCompletionHandler = YES;
};
@@ -125,7 +127,7 @@ describe(@"present choice operation", ^{
__block SDLPresentChoiceSetOperation *testCancelOp = nil;
beforeEach(^{
- testCancelOp = [[SDLPresentChoiceSetOperation alloc] initWithConnectionManager:testConnectionManager choiceSet:testChoiceSet mode:testInteractionMode keyboardProperties:nil keyboardDelegate:nil cancelID:testCancelID];
+ testCancelOp = [[SDLPresentChoiceSetOperation alloc] initWithConnectionManager:testConnectionManager choiceSet:testChoiceSet mode:testInteractionMode keyboardProperties:nil keyboardDelegate:nil cancelID:testCancelID windowCapability:windowCapability];
testCancelOp.completionBlock = ^{
hasCalledOperationCompletionHandler = YES;
};
@@ -313,7 +315,7 @@ describe(@"present choice operation", ^{
describe(@"running a searchable choice set operation", ^{
beforeEach(^{
- testOp = [[SDLPresentChoiceSetOperation alloc] initWithConnectionManager:testConnectionManager choiceSet:testChoiceSet mode:testInteractionMode keyboardProperties:testKeyboardProperties keyboardDelegate:testKeyboardDelegate cancelID:testCancelID];
+ testOp = [[SDLPresentChoiceSetOperation alloc] initWithConnectionManager:testConnectionManager choiceSet:testChoiceSet mode:testInteractionMode keyboardProperties:testKeyboardProperties keyboardDelegate:testKeyboardDelegate cancelID:testCancelID windowCapability:windowCapability];
testOp.completionBlock = ^{
hasCalledOperationCompletionHandler = YES;
@@ -414,6 +416,25 @@ describe(@"present choice operation", ^{
}]]);
});
+ it(@"should respond to enabled keyboard event", ^{
+ SDLRPCNotificationNotification *notification = nil;
+
+ // Submit notification
+ SDLOnKeyboardInput *input = [[SDLOnKeyboardInput alloc] init];
+ input.event = SDLKeyboardEventInputKeyMaskEnabled;
+ notification = [[SDLRPCNotificationNotification alloc] initWithName:SDLDidReceiveKeyboardInputNotification object:nil rpcNotification:input];
+
+ [[NSNotificationCenter defaultCenter] postNotification:notification];
+
+ OCMVerify([testKeyboardDelegate keyboardDidSendEvent:[OCMArg checkWithBlock:^BOOL(id obj) {
+ return [(SDLKeyboardEvent)obj isEqualToEnum:SDLKeyboardEventInputKeyMaskEnabled];
+ }] text:[OCMArg isNil]]);
+
+ OCMVerify([testKeyboardDelegate keyboardDidUpdateInputMask:[OCMArg checkWithBlock:^BOOL(id obj) {
+ return [(SDLKeyboardEvent)obj isEqualToEnum:SDLKeyboardEventInputKeyMaskEnabled];
+ }]]);
+ });
+
it(@"should respond to cancellation notifications", ^{
SDLRPCNotificationNotification *notification = nil;
diff --git a/SmartDeviceLinkTests/DevAPISpecs/SDLPresentKeyboardOperationSpec.m b/SmartDeviceLinkTests/DevAPISpecs/SDLPresentKeyboardOperationSpec.m
index 29ed6d0b0..389e0367d 100644
--- a/SmartDeviceLinkTests/DevAPISpecs/SDLPresentKeyboardOperationSpec.m
+++ b/SmartDeviceLinkTests/DevAPISpecs/SDLPresentKeyboardOperationSpec.m
@@ -33,6 +33,7 @@ describe(@"present keyboard operation", ^{
__block BOOL hasCalledOperationCompletionHandler = NO;
__block NSError *resultError = nil;
+ __block SDLWindowCapability *windowCapability = nil;
beforeEach(^{
testOp = nil;
@@ -43,7 +44,17 @@ describe(@"present keyboard operation", ^{
testDelegate = OCMProtocolMock(@protocol(SDLKeyboardDelegate));
OCMStub([testDelegate customKeyboardConfiguration]).andReturn(nil);
- testInitialProperties = [[SDLKeyboardProperties alloc] initWithLanguage:SDLLanguageArSa keyboardLayout:SDLKeyboardLayoutAZERTY keypressMode:SDLKeypressModeResendCurrentEntry limitedCharacterList:nil autoCompleteList:nil];
+ windowCapability = [[SDLWindowCapability alloc] init];
+ testInitialProperties = [[SDLKeyboardProperties alloc] initWithLanguage:SDLLanguageArSa keyboardLayout:SDLKeyboardLayoutAZERTY keypressMode:SDLKeypressModeResendCurrentEntry limitedCharacterList:nil autoCompleteList:nil maskInputCharacters:nil customKeys:nil];
+ });
+
+ afterEach(^{
+ if (testOp) {
+ // rationale: every test run creates a new operation to test, the old operation from a previous test
+ // stays 'undead' undefined time and can receive notifications causing a test fail at random
+ [[NSNotificationCenter defaultCenter] removeObserver:testOp];
+ testOp = nil;
+ }
});
it(@"should have a priority of 'normal'", ^{
@@ -54,7 +65,7 @@ describe(@"present keyboard operation", ^{
describe(@"running the operation", ^{
beforeEach(^{
- testOp = [[SDLPresentKeyboardOperation alloc] initWithConnectionManager:testConnectionManager keyboardProperties:testInitialProperties initialText:testInitialText keyboardDelegate:testDelegate cancelID:testCancelID];
+ testOp = [[SDLPresentKeyboardOperation alloc] initWithConnectionManager:testConnectionManager keyboardProperties:testInitialProperties initialText:testInitialText keyboardDelegate:testDelegate cancelID:testCancelID windowCapability:windowCapability];
testOp.completionBlock = ^{
hasCalledOperationCompletionHandler = YES;
};
@@ -148,6 +159,25 @@ describe(@"present keyboard operation", ^{
}]]);
});
+ it(@"should respond to enabled keyboard event", ^{
+ SDLRPCNotificationNotification *notification = nil;
+
+ // Submit notification
+ SDLOnKeyboardInput *input = [[SDLOnKeyboardInput alloc] init];
+ input.event = SDLKeyboardEventInputKeyMaskEnabled;
+ notification = [[SDLRPCNotificationNotification alloc] initWithName:SDLDidReceiveKeyboardInputNotification object:nil rpcNotification:input];
+
+ [[NSNotificationCenter defaultCenter] postNotification:notification];
+
+ OCMVerify([testDelegate keyboardDidSendEvent:[OCMArg checkWithBlock:^BOOL(id obj) {
+ return [(SDLKeyboardEvent)obj isEqualToEnum:SDLKeyboardEventInputKeyMaskEnabled];
+ }] text:[OCMArg isNil]]);
+
+ OCMVerify([testDelegate keyboardDidUpdateInputMask:[OCMArg checkWithBlock:^BOOL(id obj) {
+ return [(SDLKeyboardEvent)obj isEqualToEnum:SDLKeyboardEventInputKeyMaskEnabled];
+ }]]);
+ });
+
it(@"should respond to cancellation notifications", ^{
SDLRPCNotificationNotification *notification = nil;
@@ -260,7 +290,7 @@ describe(@"present keyboard operation", ^{
describe(@"Canceling the keyboard", ^{
beforeEach(^{
- testOp = [[SDLPresentKeyboardOperation alloc] initWithConnectionManager:testConnectionManager keyboardProperties:testInitialProperties initialText:testInitialText keyboardDelegate:testDelegate cancelID:testCancelID];
+ testOp = [[SDLPresentKeyboardOperation alloc] initWithConnectionManager:testConnectionManager keyboardProperties:testInitialProperties initialText:testInitialText keyboardDelegate:testDelegate cancelID:testCancelID windowCapability:windowCapability];
testOp.completionBlock = ^{
hasCalledOperationCompletionHandler = YES;
};
diff --git a/SmartDeviceLinkTests/DevAPISpecs/SDLSequentialRPCRequestOperationSpec.m b/SmartDeviceLinkTests/DevAPISpecs/SDLSequentialRPCRequestOperationSpec.m
index 9f3f5cc93..ab7001ae0 100644
--- a/SmartDeviceLinkTests/DevAPISpecs/SDLSequentialRPCRequestOperationSpec.m
+++ b/SmartDeviceLinkTests/DevAPISpecs/SDLSequentialRPCRequestOperationSpec.m
@@ -51,20 +51,19 @@ describe(@"Sending sequential requests", ^{
testOperation = [[SDLSequentialRPCRequestOperation alloc] initWithConnectionManager:testConnectionManager requests:sendRequests.copy progressHandler:^BOOL(__kindof SDLRPCRequest * _Nonnull request, __kindof SDLRPCResponse * _Nullable response, NSError * _Nullable error, float percentComplete) {
TestRequestProgressResponse *progressResponse = testProgressResponses[request.correlationID];
- expect(progressResponse.percentComplete).toEventually(beCloseTo(percentComplete));
- expect(response).toEventuallyNot(beNil());
- expect(error).toEventually(beNil());
+ expect(progressResponse.percentComplete).to(beCloseTo(percentComplete));
+ expect(response).toNot(beNil());
+ expect(error).to(beNil());
[resultResponses addObject:response];
return YES;
} completionHandler:^(BOOL success) {
expect(resultResponses).to(haveCount(3));
- expect(success).to(beTruthy());
+ expect(success).to(beTrue());
}];
[testOperationQueue addOperation:testOperation];
- [NSThread sleepForTimeInterval:0.5];
});
});
@@ -82,11 +81,10 @@ describe(@"Sending sequential requests", ^{
return NO;
} completionHandler:^(BOOL success) {
expect(resultResponses).to(haveCount(1));
- expect(success).to(beFalsy());
+ expect(success).to(beFalse());
}];
[testOperationQueue addOperation:testOperation];
- [NSThread sleepForTimeInterval:0.5];
});
});
});
@@ -124,11 +122,10 @@ describe(@"Sending sequential requests", ^{
return YES;
} completionHandler:^(BOOL success) {
expect(resultResponses).to(haveCount(3));
- expect(success).to(beFalsy());
+ expect(success).to(beFalse());
}];
[testOperationQueue addOperation:testOperation];
- [NSThread sleepForTimeInterval:0.5];
});
});
});
diff --git a/SmartDeviceLinkTests/DevAPISpecs/SDLSoftButtonManagerSpec.m b/SmartDeviceLinkTests/DevAPISpecs/SDLSoftButtonManagerSpec.m
index a24c432a7..437789ee0 100644
--- a/SmartDeviceLinkTests/DevAPISpecs/SDLSoftButtonManagerSpec.m
+++ b/SmartDeviceLinkTests/DevAPISpecs/SDLSoftButtonManagerSpec.m
@@ -46,7 +46,7 @@
@property (strong, nonatomic) NSMutableArray<SDLAsynchronousOperation *> *batchQueue;
- (void)sdl_hmiStatusNotification:(SDLRPCNotificationNotification *)notification;
-- (void)sdl_displayCapabilityDidUpdate:(SDLSystemCapability *)systemCapability;
+- (void)sdl_displayCapabilityDidUpdate;
@end
@@ -76,6 +76,8 @@ describe(@"a soft button manager", ^{
__block SDLArtwork *object2State1Art = [[SDLArtwork alloc] initWithData:[@"TestData" dataUsingEncoding:NSUTF8StringEncoding] name:object2State1ArtworkName fileExtension:@"png" persistent:YES];
__block SDLSoftButtonState *object2State1 = [[SDLSoftButtonState alloc] initWithStateName:object2State1Name text:object2State1Text artwork:object2State1Art];
+ __block SDLWindowCapability *testWindowCapability = nil;
+
beforeEach(^{
testFileManager = OCMClassMock([SDLFileManager class]);
testSystemCapabilityManager = OCMClassMock([SDLSystemCapabilityManager class]);
@@ -84,71 +86,102 @@ describe(@"a soft button manager", ^{
testManager = [[SDLSoftButtonManager alloc] initWithConnectionManager:testConnectionManager fileManager:testFileManager systemCapabilityManager:testSystemCapabilityManager];
[testManager start];
- SDLOnHMIStatus *status = [[SDLOnHMIStatus alloc] init];
- status.hmiLevel = SDLHMILevelFull;
- [testManager sdl_hmiStatusNotification:[[SDLRPCNotificationNotification alloc] initWithName:SDLDidChangeHMIStatusNotification object:nil rpcNotification:status]];
-
SDLSoftButtonCapabilities *softButtonCapabilities = [[SDLSoftButtonCapabilities alloc] init];
softButtonCapabilities.imageSupported = @YES;
softButtonCapabilities.textSupported = @YES;
softButtonCapabilities.longPressAvailable = @YES;
softButtonCapabilities.shortPressAvailable = @YES;
- SDLWindowCapability *windowCapability = [[SDLWindowCapability alloc] init];
- windowCapability.softButtonCapabilities = @[softButtonCapabilities];
- SDLDisplayCapability *displayCapability = [[SDLDisplayCapability alloc] initWithDisplayName:@"TEST" windowCapabilities:@[windowCapability] windowTypeSupported:nil];
- [testManager sdl_displayCapabilityDidUpdate:[[SDLSystemCapability alloc] initWithDisplayCapabilities:@[displayCapability]]];
+ testWindowCapability = [[SDLWindowCapability alloc] init];
+ testWindowCapability.softButtonCapabilities = @[softButtonCapabilities];
});
it(@"should instantiate correctly", ^{
expect(testManager.connectionManager).to(equal(testConnectionManager));
expect(testManager.fileManager).to(equal(testFileManager));
-
expect(testManager.softButtonObjects).to(beEmpty());
expect(testManager.currentMainField1).to(beNil());
expect(testManager.transactionQueue).toNot(beNil());
- expect(testManager.transactionQueue.isSuspended).to(beFalse());
- expect(testManager.softButtonCapabilities).toNot(beNil());
- expect(testManager.currentLevel).to(equal(SDLHMILevelFull));
-
- // These are set up earlier for future tests and therefore won't be nil
-// expect(testManager.windowCapability).to(beNil());
-// expect(testManager.currentLevel).to(beNil());
+ expect(testManager.transactionQueue.isSuspended).to(beTrue());
+ expect(testManager.softButtonCapabilities).to(beNil());
+ expect(testManager.currentLevel).to(beNil());
});
- context(@"when in HMI NONE", ^{
+ describe(@"the SDL app has not been opened", ^{
beforeEach(^{
- SDLOnHMIStatus *status = [[SDLOnHMIStatus alloc] init];
- status.hmiLevel = SDLHMILevelNone;
- [testManager sdl_hmiStatusNotification:[[SDLRPCNotificationNotification alloc] initWithName:SDLDidChangeHMIStatusNotification object:nil rpcNotification:status]];
-
testObject1 = [[SDLSoftButtonObject alloc] initWithName:@"name1" states:@[object1State1, object1State2] initialStateName:object1State1Name handler:nil];
testObject2 = [[SDLSoftButtonObject alloc] initWithName:@"name2" state:object2State1 handler:nil];
-
testManager.softButtonObjects = @[testObject1, testObject2];
});
- it(@"should set the soft buttons, but not update", ^{
- expect(testManager.softButtonObjects).toNot(beEmpty());
- expect(testManager.transactionQueue.suspended).to(beTrue());
+ context(@"when the HMI level notification has not been received", ^{
+ it(@"should set the soft buttons, but not update", ^{
+ expect(testManager.currentLevel).to(beNil());
+ expect(testManager.softButtonObjects).toNot(beEmpty());
+ expect(testManager.transactionQueue.suspended).to(beTrue());
+ });
+ });
+
+ context(@"when the HMI level is NONE", ^{
+ beforeEach(^{
+ SDLOnHMIStatus *status = [[SDLOnHMIStatus alloc] init];
+ status.hmiLevel = SDLHMILevelNone;
+ [testManager sdl_hmiStatusNotification:[[SDLRPCNotificationNotification alloc] initWithName:SDLDidChangeHMIStatusNotification object:nil rpcNotification:status]];
+ });
+
+ it(@"should set the soft buttons, but not update", ^{
+ expect(testManager.currentLevel).to(equal(SDLHMILevelNone));
+ expect(testManager.softButtonObjects).toNot(beEmpty());
+ expect(testManager.transactionQueue.suspended).to(beTrue());
+ });
});
});
- context(@"when there are no soft button capabilities", ^{
+ describe(@"the SDL app has been opened", ^{
beforeEach(^{
- SDLWindowCapability *windowCapability = [[SDLWindowCapability alloc] init];
- SDLDisplayCapability *displayCapability = [[SDLDisplayCapability alloc] initWithDisplayName:@"TEST" windowCapabilities:@[windowCapability] windowTypeSupported:nil];
- [testManager sdl_displayCapabilityDidUpdate:[[SDLSystemCapability alloc] initWithDisplayCapabilities:@[displayCapability]]];
+ testObject1 = [[SDLSoftButtonObject alloc] initWithName:@"name1" states:@[object1State1, object1State2] initialStateName:object1State1Name handler:nil];
+ testObject2 = [[SDLSoftButtonObject alloc] initWithName:@"name2" state:object2State1 handler:nil];
+ testManager.softButtonObjects = @[testObject1, testObject2];
+
+ SDLOnHMIStatus *status = [[SDLOnHMIStatus alloc] init];
+ status.hmiLevel = SDLHMILevelFull;
+ [testManager sdl_hmiStatusNotification:[[SDLRPCNotificationNotification alloc] initWithName:SDLDidChangeHMIStatusNotification object:nil rpcNotification:status]];
});
- it(@"should set the buttons but have the queue suspended", ^{
- expect(testManager.softButtonObjects).toNot(beNil());
- expect(testManager.transactionQueue.isSuspended).to(beTrue());
+ context(@"when the soft button capabilities notification has not been received", ^{
+ beforeEach(^{
+ OCMStub([testSystemCapabilityManager defaultMainWindowCapability]).andReturn(nil);
+ [testManager sdl_displayCapabilityDidUpdate];
+ });
+
+ it(@"should set the buttons but have the queue suspended", ^{
+ expect(testManager.softButtonObjects).toNot(beNil());
+ expect(testManager.transactionQueue.isSuspended).to(beTrue());
+ });
+ });
+
+ context(@"when the soft button capabilities notification has been received", ^{
+ beforeEach(^{
+ OCMStub([testSystemCapabilityManager defaultMainWindowCapability]).andReturn(testWindowCapability);
+ [testManager sdl_displayCapabilityDidUpdate];
+ });
+
+ it(@"should set the buttons and unsuspend the queue", ^{
+ expect(testManager.softButtonObjects).toNot(beNil());
+ expect(testManager.transactionQueue.isSuspended).to(beFalse());
+ });
});
});
- context(@"when button objects have the same name", ^{
+ describe(@"invalid button objects (button objects have same names)", ^{
beforeEach(^{
+ SDLOnHMIStatus *status = [[SDLOnHMIStatus alloc] init];
+ status.hmiLevel = SDLHMILevelFull;
+ [testManager sdl_hmiStatusNotification:[[SDLRPCNotificationNotification alloc] initWithName:SDLDidChangeHMIStatusNotification object:nil rpcNotification:status]];
+
+ OCMStub([testSystemCapabilityManager defaultMainWindowCapability]).andReturn(testWindowCapability);
+ [testManager sdl_displayCapabilityDidUpdate];
+
NSString *sameName = @"Same name";
testObject1 = [[SDLSoftButtonObject alloc] initWithName:sameName states:@[object1State1, object1State2] initialStateName:object1State1Name handler:nil];
testObject2 = [[SDLSoftButtonObject alloc] initWithName:sameName state:object2State1 handler:nil];
@@ -162,33 +195,44 @@ describe(@"a soft button manager", ^{
});
});
- context(@"when button objects have different names", ^{
+ // valid button objects (button objects have different names)
+ describe(@"valid button objects (button objects have different names)", ^{
beforeEach(^{
+ SDLOnHMIStatus *status = [[SDLOnHMIStatus alloc] init];
+ status.hmiLevel = SDLHMILevelFull;
+ [testManager sdl_hmiStatusNotification:[[SDLRPCNotificationNotification alloc] initWithName:SDLDidChangeHMIStatusNotification object:nil rpcNotification:status]];
+
+ OCMStub([testSystemCapabilityManager defaultMainWindowCapability]).andReturn(testWindowCapability);
+ [testManager sdl_displayCapabilityDidUpdate];
+
testObject1 = [[SDLSoftButtonObject alloc] initWithName:object1Name states:@[object1State1, object1State2] initialStateName:object1State1Name handler:nil];
testObject2 = [[SDLSoftButtonObject alloc] initWithName:object2Name state:object2State1 handler:nil];
testManager.softButtonObjects = @[testObject1, testObject2];
- [NSThread sleepForTimeInterval:0.1];
});
+ // should set soft buttons correctly
it(@"should set soft buttons correctly", ^{
- expect(testManager.softButtonObjects).toNot(beNil());
- expect(testObject1.buttonId).to(equal(1));
- expect(testObject2.buttonId).to(equal(2));
- expect(testObject1.manager).to(equal(testManager));
- expect(testObject2.manager).to(equal(testManager));
+ expect(testManager.softButtonObjects).toEventuallyNot(beNil());
+ expect(testObject1.buttonId).toEventually(equal(1));
+ expect(testObject2.buttonId).toEventually(equal(2));
+ expect(testObject1.manager).toEventually(equal(testManager));
+ expect(testObject2.manager).toEventually(equal(testManager));
// One replace operation
- expect(testManager.transactionQueue.operationCount).to(equal(1));
+ expect(testManager.transactionQueue.operationCount).toEventually(equal(1));
});
+ // should replace earlier operations when a replace operation is entered
it(@"should replace earlier operations when a replace operation is entered", ^{
[testObject1 transitionToNextState];
testManager.softButtonObjects = @[testObject1];
- expect(testManager.transactionQueue.operationCount).to(equal(3));
- expect(testManager.transactionQueue.operations[0].isCancelled).to(beTrue());
- expect(testManager.transactionQueue.operations[1].isCancelled).to(beTrue());
- expect(testManager.transactionQueue.operations[2].isCancelled).to(beFalse());
+ [NSThread sleepForTimeInterval:0.5]; // Necessary to not get range exceptions with toEventually?
+
+ expect(testManager.transactionQueue.operationCount).withTimeout(3.0).toEventually(equal(3));
+ expect(testManager.transactionQueue.operations[0].isCancelled).withTimeout(3.0).toEventually(beTrue());
+ expect(testManager.transactionQueue.operations[1].isCancelled).withTimeout(3.0).toEventually(beTrue());
+ expect(testManager.transactionQueue.operations[2].isCancelled).withTimeout(3.0).toEventually(beFalse());
});
it(@"should retrieve soft buttons correctly", ^{
@@ -228,6 +272,13 @@ describe(@"a soft button manager", ^{
describe(@"transitioning soft button states", ^{
beforeEach(^{
+ SDLOnHMIStatus *status = [[SDLOnHMIStatus alloc] init];
+ status.hmiLevel = SDLHMILevelFull;
+ [testManager sdl_hmiStatusNotification:[[SDLRPCNotificationNotification alloc] initWithName:SDLDidChangeHMIStatusNotification object:nil rpcNotification:status]];
+
+ OCMStub([testSystemCapabilityManager defaultMainWindowCapability]).andReturn(testWindowCapability);
+ [testManager sdl_displayCapabilityDidUpdate];
+
testObject1 = [[SDLSoftButtonObject alloc] initWithName:object1Name states:@[object1State1, object1State2] initialStateName:object1State1Name handler:nil];
testObject2 = [[SDLSoftButtonObject alloc] initWithName:object2Name state:object2State1 handler:nil];
@@ -266,6 +317,17 @@ describe(@"a soft button manager", ^{
context(@"On disconnects", ^{
beforeEach(^{
+ SDLOnHMIStatus *status = [[SDLOnHMIStatus alloc] init];
+ status.hmiLevel = SDLHMILevelFull;
+ [testManager sdl_hmiStatusNotification:[[SDLRPCNotificationNotification alloc] initWithName:SDLDidChangeHMIStatusNotification object:nil rpcNotification:status]];
+
+ OCMStub([testSystemCapabilityManager defaultMainWindowCapability]).andReturn(testWindowCapability);
+ [testManager sdl_displayCapabilityDidUpdate];
+
+ testObject1 = [[SDLSoftButtonObject alloc] initWithName:@"name1" states:@[object1State1, object1State2] initialStateName:object1State1Name handler:nil];
+ testObject2 = [[SDLSoftButtonObject alloc] initWithName:@"name2" state:object2State1 handler:nil];
+ testManager.softButtonObjects = @[testObject1, testObject2];
+
[testManager stop];
});
diff --git a/SmartDeviceLinkTests/DevAPISpecs/SDLStreamingAudioLifecycleManagerSpec.m b/SmartDeviceLinkTests/DevAPISpecs/SDLStreamingAudioLifecycleManagerSpec.m
index 4a39a0676..cca65997b 100644
--- a/SmartDeviceLinkTests/DevAPISpecs/SDLStreamingAudioLifecycleManagerSpec.m
+++ b/SmartDeviceLinkTests/DevAPISpecs/SDLStreamingAudioLifecycleManagerSpec.m
@@ -27,7 +27,6 @@
@interface SDLStreamingAudioLifecycleManager()
@property (weak, nonatomic) SDLProtocol *protocol;
-@property (copy, nonatomic, nullable) NSString *connectedVehicleMake;
@property (nonatomic, strong, readwrite) SDLAudioStreamManager *audioTranscodingManager;
@end
@@ -98,32 +97,11 @@ describe(@"the streaming audio manager", ^{
expect(streamingLifecycleManager.currentAudioStreamState).to(match(SDLAudioStreamManagerStateStopped));
});
- describe(@"after receiving a register app interface response", ^{
- __block SDLRegisterAppInterfaceResponse *someRegisterAppInterfaceResponse = nil;
- __block SDLVehicleType *testVehicleType = nil;
-
- beforeEach(^{
- someRegisterAppInterfaceResponse = [[SDLRegisterAppInterfaceResponse alloc] init];
- testVehicleType = [[SDLVehicleType alloc] init];
- testVehicleType.make = @"TestVehicleType";
- someRegisterAppInterfaceResponse.vehicleType = testVehicleType;
-
- SDLRPCResponseNotification *notification = [[SDLRPCResponseNotification alloc] initWithName:SDLDidReceiveRegisterAppInterfaceResponse object:self rpcResponse:someRegisterAppInterfaceResponse];
-
- [[NSNotificationCenter defaultCenter] postNotification:notification];
- });
-
- it(@"should should save the connected vehicle make", ^{
- expect(streamingLifecycleManager.connectedVehicleMake).toEventually(equal(testVehicleType.make));
- });
- });
-
describe(@"if the app state is active", ^{
__block id streamStub = nil;
beforeEach(^{
streamStub = OCMPartialMock(streamingLifecycleManager);
-
OCMStub([streamStub isStreamingSupported]).andReturn(YES);
[streamingLifecycleManager.appStateMachine setToState:SDLAppStateActive fromOldState:nil callEnterTransition:NO];
@@ -395,7 +373,6 @@ describe(@"the streaming audio manager", ^{
[streamingLifecycleManager endAudioServiceWithCompletionHandler:^ {
handlerCalled = YES;
}];
- streamingLifecycleManager.connectedVehicleMake = @"OEM_make_2";
});
context(@"when the manager is READY", ^{
@@ -409,7 +386,6 @@ describe(@"the streaming audio manager", ^{
expect(streamingLifecycleManager.protocol).to(beNil());
expect(streamingLifecycleManager.hmiLevel).to(equal(SDLHMILevelNone));
- expect(streamingLifecycleManager.connectedVehicleMake).to(beNil());
OCMVerify([mockAudioStreamManager stop]);
expect(handlerCalled).to(beTrue());
});
@@ -426,7 +402,6 @@ describe(@"the streaming audio manager", ^{
expect(streamingLifecycleManager.protocol).to(beNil());
expect(streamingLifecycleManager.hmiLevel).to(equal(SDLHMILevelNone));
- expect(streamingLifecycleManager.connectedVehicleMake).to(beNil());
OCMReject([mockAudioStreamManager stop]);
expect(handlerCalled).to(beFalse());
});
diff --git a/SmartDeviceLinkTests/DevAPISpecs/SDLStreamingMediaConfigurationSpec.m b/SmartDeviceLinkTests/DevAPISpecs/SDLStreamingMediaConfigurationSpec.m
index 5132e0551..c1dd911b4 100644
--- a/SmartDeviceLinkTests/DevAPISpecs/SDLStreamingMediaConfigurationSpec.m
+++ b/SmartDeviceLinkTests/DevAPISpecs/SDLStreamingMediaConfigurationSpec.m
@@ -1,11 +1,14 @@
#import <Quick/Quick.h>
#import <Nimble/Nimble.h>
+#import <OCMock/OCMock.h>
#import <VideoToolbox/VideoToolbox.h>
#import "SDLStreamingMediaConfiguration.h"
#import "SDLFakeSecurityManager.h"
#import "SDLFakeStreamingManagerDataSource.h"
+#import "SDLImageResolution.h"
+#import "SDLVideoStreamingRange.h"
QuickSpecBegin(SDLStreamingMediaConfigurationSpec)
@@ -15,21 +18,45 @@ describe(@"a streaming media configuration", ^{
__block UIViewController *testViewController = nil;
__block SDLStreamingEncryptionFlag testEncryptionFlag = SDLStreamingEncryptionFlagNone;
__block SDLFakeStreamingManagerDataSource *testDataSource = nil;
+ __block id<SDLStreamingVideoDelegate> testDelegate = nil;
__block NSDictionary<NSString *, id> *testVideoEncoderSettings = nil;
+ __block SDLVideoStreamingRange *testLandscapeRange = nil;
+ __block SDLVideoStreamingRange *testPortraitRange = nil;
beforeEach(^{
testFakeSecurityManager = [[SDLFakeSecurityManager alloc] init];
testDataSource = [[SDLFakeStreamingManagerDataSource alloc] init];
+ testDelegate = OCMProtocolMock(@protocol(SDLStreamingVideoDelegate));
testVideoEncoderSettings = @{
(__bridge NSString *)kVTCompressionPropertyKey_ExpectedFrameRate : @1
};
testViewController = [[UIViewController alloc] init];
testEncryptionFlag = SDLStreamingEncryptionFlagAuthenticateAndEncrypt;
+ testLandscapeRange = [[SDLVideoStreamingRange alloc] initWithMinimumResolution:[[SDLImageResolution alloc] initWithWidth:100 height:100] maximumResolution:[[SDLImageResolution alloc] initWithWidth:200 height:200]];
+ testPortraitRange = [[SDLVideoStreamingRange alloc] initWithMinimumResolution:[[SDLImageResolution alloc] initWithWidth:50 height:50] maximumResolution:[[SDLImageResolution alloc] initWithWidth:150 height:150]];
});
context(@"That is created without the default secure/insecure settings", ^{
+ it(@"should properly set all properties with initWithEncryptionFlag:videoSettings:supportedLandscapeRange:supportedPortraitRange:dataSource:delegate:rootViewController:", ^{
+ testConfig = [[SDLStreamingMediaConfiguration alloc] initWithEncryptionFlag:testEncryptionFlag videoSettings:testVideoEncoderSettings supportedLandscapeRange:testLandscapeRange supportedPortraitRange:testPortraitRange dataSource:testDataSource delegate:testDelegate rootViewController:testViewController];
+
+ expect(@(testConfig.maximumDesiredEncryption)).to(equal(@(testEncryptionFlag)));
+ expect(testConfig.customVideoEncoderSettings).to(equal(testVideoEncoderSettings));
+ expect(testConfig.allowMultipleViewControllerOrientations).to(equal(NO));
+ expect(testConfig.dataSource).to(equal(testDataSource));
+ expect(testConfig.rootViewController).to(equal(testViewController));
+ expect(@(testConfig.carWindowRenderingType)).to(equal(@(SDLCarWindowRenderingTypeLayer)));
+ expect(testConfig.enableForcedFramerateSync).to(beTrue());
+ expect(testConfig.delegate).to(equal(testDelegate));
+ expect(testConfig.supportedPortraitStreamingRange).to(equal(testPortraitRange));
+ expect(testConfig.supportedLandscapeStreamingRange).to(equal(testLandscapeRange));
+ });
+
it(@"should have properly set all properties with initWithEncryptionFlag:videoSettings:dataSource:rootViewController:", ^{
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated"
testConfig = [[SDLStreamingMediaConfiguration alloc] initWithEncryptionFlag:testEncryptionFlag videoSettings:testVideoEncoderSettings dataSource:testDataSource rootViewController:testViewController];
+#pragma clang diagnostic pop
expect(@(testConfig.maximumDesiredEncryption)).to(equal(@(testEncryptionFlag)));
expect(testConfig.customVideoEncoderSettings).to(equal(testVideoEncoderSettings));
@@ -38,6 +65,9 @@ describe(@"a streaming media configuration", ^{
expect(testConfig.rootViewController).to(equal(testViewController));
expect(@(testConfig.carWindowRenderingType)).to(equal(@(SDLCarWindowRenderingTypeLayer)));
expect(testConfig.enableForcedFramerateSync).to(beTrue());
+ expect(testConfig.delegate).to(beNil());
+ expect(testConfig.supportedPortraitStreamingRange).to(beNil());
+ expect(testConfig.supportedLandscapeStreamingRange).to(beNil());
});
it(@"should have properly set and insecure configuration with init", ^{
@@ -50,6 +80,9 @@ describe(@"a streaming media configuration", ^{
expect(testConfig.rootViewController).to(beNil());
expect(@(testConfig.carWindowRenderingType)).to(equal(@(SDLCarWindowRenderingTypeLayer)));
expect(testConfig.enableForcedFramerateSync).to(beTrue());
+ expect(testConfig.delegate).to(beNil());
+ expect(testConfig.supportedPortraitStreamingRange).to(beNil());
+ expect(testConfig.supportedLandscapeStreamingRange).to(beNil());
});
});
@@ -64,6 +97,9 @@ describe(@"a streaming media configuration", ^{
expect(testConfig.rootViewController).to(beNil());
expect(@(testConfig.carWindowRenderingType)).to(equal(@(SDLCarWindowRenderingTypeLayer)));
expect(testConfig.enableForcedFramerateSync).to(beTrue());
+ expect(testConfig.delegate).to(beNil());
+ expect(testConfig.supportedPortraitStreamingRange).to(beNil());
+ expect(testConfig.supportedLandscapeStreamingRange).to(beNil());
});
it(@"should have properly set properties with autostreamingInsecureConfigurationWithInitialViewController", ^{
@@ -76,6 +112,9 @@ describe(@"a streaming media configuration", ^{
expect(testConfig.rootViewController).to(equal(testViewController));
expect(@(testConfig.carWindowRenderingType)).to(equal(@(SDLCarWindowRenderingTypeLayer)));
expect(testConfig.enableForcedFramerateSync).to(beTrue());
+ expect(testConfig.delegate).to(beNil());
+ expect(testConfig.supportedPortraitStreamingRange).to(beNil());
+ expect(testConfig.supportedLandscapeStreamingRange).to(beNil());
});
});
@@ -90,6 +129,9 @@ describe(@"a streaming media configuration", ^{
expect(testConfig.rootViewController).to(beNil());
expect(@(testConfig.carWindowRenderingType)).to(equal(@(SDLCarWindowRenderingTypeLayer)));
expect(testConfig.enableForcedFramerateSync).to(beTrue());
+ expect(testConfig.delegate).to(beNil());
+ expect(testConfig.supportedPortraitStreamingRange).to(beNil());
+ expect(testConfig.supportedLandscapeStreamingRange).to(beNil());
});
it(@"should have properly set properties with autostreamingSecureConfigurationWithInitialViewController:", ^{
@@ -102,15 +144,18 @@ describe(@"a streaming media configuration", ^{
expect(testConfig.rootViewController).to(equal(testViewController));
expect(@(testConfig.carWindowRenderingType)).to(equal(@(SDLCarWindowRenderingTypeLayer)));
expect(testConfig.enableForcedFramerateSync).to(beTrue());
+ expect(testConfig.delegate).to(beNil());
+ expect(testConfig.supportedPortraitStreamingRange).to(beNil());
+ expect(testConfig.supportedLandscapeStreamingRange).to(beNil());
});
});
- context(@"copying a filter", ^{
+ context(@"copying a configuration", ^{
__block SDLStreamingMediaConfiguration *testStreamingMediaConfiguration = nil;
__block SDLStreamingMediaConfiguration *testCopiedStreamingMediaConfiguration = nil;
beforeEach(^{
- testStreamingMediaConfiguration = [[SDLStreamingMediaConfiguration alloc] initWithEncryptionFlag:testEncryptionFlag videoSettings:testVideoEncoderSettings dataSource:testDataSource rootViewController:testViewController];
+ testStreamingMediaConfiguration = [[SDLStreamingMediaConfiguration alloc] initWithEncryptionFlag:testEncryptionFlag videoSettings:testVideoEncoderSettings supportedLandscapeRange:testLandscapeRange supportedPortraitRange:testPortraitRange dataSource:testDataSource delegate:testDelegate rootViewController:testViewController];
testCopiedStreamingMediaConfiguration = [testStreamingMediaConfiguration copy];
});
@@ -123,6 +168,9 @@ describe(@"a streaming media configuration", ^{
expect(@(testCopiedStreamingMediaConfiguration.carWindowRenderingType)).to(equal(testStreamingMediaConfiguration.carWindowRenderingType));
expect(testCopiedStreamingMediaConfiguration.enableForcedFramerateSync).to(equal(testStreamingMediaConfiguration.enableForcedFramerateSync));
expect(testCopiedStreamingMediaConfiguration.allowMultipleViewControllerOrientations).to(equal(testStreamingMediaConfiguration.allowMultipleViewControllerOrientations));
+ expect(testCopiedStreamingMediaConfiguration.delegate).to(equal(testStreamingMediaConfiguration.delegate));
+ expect(testCopiedStreamingMediaConfiguration.supportedPortraitStreamingRange).to(equal(testStreamingMediaConfiguration.supportedPortraitStreamingRange));
+ expect(testCopiedStreamingMediaConfiguration.supportedLandscapeStreamingRange).to(equal(testStreamingMediaConfiguration.supportedLandscapeStreamingRange));
});
});
});
diff --git a/SmartDeviceLinkTests/DevAPISpecs/SDLStreamingVideoLifecycleManagerSpec.m b/SmartDeviceLinkTests/DevAPISpecs/SDLStreamingVideoLifecycleManagerSpec.m
index 8543f4db0..36330c74d 100644
--- a/SmartDeviceLinkTests/DevAPISpecs/SDLStreamingVideoLifecycleManagerSpec.m
+++ b/SmartDeviceLinkTests/DevAPISpecs/SDLStreamingVideoLifecycleManagerSpec.m
@@ -17,10 +17,12 @@
#import "SDLGetSystemCapabilityResponse.h"
#import "SDLGenericResponse.h"
#import "SDLGlobals.h"
+#import "SDLH264VideoEncoder.h"
#import "SDLHMICapabilities.h"
#import "SDLHMILevel.h"
#import "SDLImageResolution.h"
#import "SDLLifecycleConfiguration.h"
+#import "SDLLogMacros.h"
#import "SDLLockScreenConfiguration.h"
#import "SDLLogConfiguration.h"
#import "SDLOnHMIStatus.h"
@@ -33,103 +35,413 @@
#import "SDLStreamingMediaConfiguration.h"
#import "SDLStreamingVideoLifecycleManager.h"
#import "SDLStreamingVideoScaleManager.h"
+#import "SDLVideoStreamingRange.h"
#import "SDLSystemCapability.h"
#import "SDLSystemCapabilityManager.h"
#import "SDLV2ProtocolHeader.h"
#import "SDLV2ProtocolMessage.h"
#import "SDLVehicleType.h"
+#import "SDLVersion.h"
#import "SDLVideoStreamingCapability.h"
#import "SDLVideoStreamingState.h"
#import "SDLVehicleType.h"
#import "TestConnectionManager.h"
+#import "TestSmartConnectionManager.h"
+#import "TestStreamingMediaDelegate.h"
-
-@interface SDLStreamingVideoLifecycleManager ()
+// expose private methods to the test suite
+@interface SDLStreamingVideoLifecycleManager (test)
@property (weak, nonatomic) SDLProtocol *protocol;
@property (copy, nonatomic, readonly) NSString *appName;
@property (copy, nonatomic, readonly) NSString *videoStreamBackgroundString;
-@property (copy, nonatomic, nullable) NSString *connectedVehicleMake;
+@property (strong, nonatomic, nullable) SDLVideoStreamingRange *supportedLandscapeStreamingRange;
+@property (strong, nonatomic, nullable) SDLVideoStreamingRange *supportedPortraitStreamingRange;
+@property (weak, nonatomic, nullable) id<SDLStreamingVideoDelegate> delegate;
+@property (assign, nonatomic) BOOL shouldAutoResume;
+@property (strong, nonatomic, nullable) SDLVideoStreamingCapability *videoStreamingCapability;
+@property (strong, nonatomic, nullable) SDLVideoStreamingCapability *videoStreamingCapabilityUpdated;
+@property (strong, nonatomic, nullable) CADisplayLink *displayLink;
+@property (strong, nonatomic) NSMutableDictionary *videoEncoderSettings;
+@property (copy, nonatomic) NSDictionary<NSString *, id> *customEncoderSettings;
+
+- (void)sdl_shutDown;
+- (NSArray<SDLVideoStreamingCapability *>* __nullable)matchVideoCapability:(SDLVideoStreamingCapability *)videoStreamingCapability;
+- (void)sdl_suspendVideo;
+- (void)didEnterStateVideoStreamStopped;
+- (void)didEnterStateVideoStreamStarting;
+- (void)didEnterStateVideoStreamReady;
+- (void)didEnterStateVideoStreamSuspended;
+- (void)sdl_videoStreamingCapabilityDidUpdate:(SDLSystemCapability *)systemCapability;
+- (void)sdl_applyVideoCapability:(SDLVideoStreamingCapability *)capability;
+
+@end
+@interface SDLStreamingVideoLifecycleTestManager : SDLStreamingVideoLifecycleManager
+@property (assign) BOOL didStopVideoSession;
+@property (strong, nullable) id testVideoCapabilityUpdatedWhileStarting;
+@property (strong, nullable) id testVideoCapabilityUpdatedWhenStreaming;
@end
-QuickSpecBegin(SDLStreamingVideoLifecycleManagerSpec)
+@implementation SDLStreamingVideoLifecycleTestManager
+
+- (BOOL)isVideoConnected {
+ return NO;
+}
+
+- (void)sdl_stopVideoSession {
+ self.didStopVideoSession = YES;
+}
+
+- (void)sdl_applyVideoCapabilityWhileStarting:(SDLVideoStreamingCapability *)videoCapabilityUpdated {
+ self.testVideoCapabilityUpdatedWhileStarting = videoCapabilityUpdated;
+}
+
+- (void)sdl_applyVideoCapabilityWhenStreaming:(nullable SDLVideoStreamingCapability *)videoCapability {
+ self.testVideoCapabilityUpdatedWhenStreaming = videoCapability;
+}
+
+- (BOOL)isAppStateVideoStreamCapable {
+ return YES;
+}
+
+- (SDLVideoStreamingFormat *)videoFormat {
+ return [[SDLVideoStreamingFormat alloc] initWithCodec:SDLVideoStreamingCodecH264 protocol:SDLVideoStreamingProtocolRAW];
+}
+
+- (SDLH264VideoEncoder *)videoEncoder {
+ return OCMClassMock([SDLH264VideoEncoder class]);
+}
+
+- (BOOL)useDisplayLink {
+ return YES;
+}
+
+@end
+
+
+// expose private methods to the test suite
+@interface SDLVideoStreamingCapability (test)
+- (NSArray <SDLVideoStreamingCapability *> *)allVideoStreamingCapabilities;
+- (instancetype)shortCopy;
+@end
+
+// video streaming capabilities values for testing, used in SDLGetSystemCapabilityResponse
+static const float testVSCScale = 1.25;
+static const int32_t testVSCMaxBitrate = 12345;
+static const uint16_t testVSCResolutionWidth = 42;
+static const uint16_t testVSCResolutionHeight = 69;
+NSString *const testAppName = @"Test App";
+
+static void postRAINotification(void);
+static void sendNotificationForHMILevel(SDLHMILevel hmiLevel, SDLVideoStreamingState streamState);
+static SDLGetSystemCapabilityResponse *createSystemCapabilityResponse(void);
+
+#pragma mark - test Init
-describe(@"the streaming video manager", ^{
+QuickSpecBegin(SDLStreamingVideoLifecycleManagerSpec_Init)
+
+describe(@"init tests", ^{
__block SDLStreamingVideoLifecycleManager *streamingLifecycleManager = nil;
- __block SDLStreamingMediaConfiguration *testConfiguration = [SDLStreamingMediaConfiguration insecureConfiguration];
- __block SDLCarWindowViewController *testViewController = [[SDLCarWindowViewController alloc] init];
- __block SDLFakeStreamingManagerDataSource *testDataSource = [[SDLFakeStreamingManagerDataSource alloc] init];
- __block TestConnectionManager *testConnectionManager = nil;
- __block NSString *testAppName = @"Test App";
- __block SDLLifecycleConfiguration *testLifecycleConfiguration = [SDLLifecycleConfiguration defaultConfigurationWithAppName:testAppName fullAppId:@""];
__block SDLSystemCapabilityManager *testSystemCapabilityManager = nil;
+ SDLStreamingMediaConfiguration *testConfiguration = [SDLStreamingMediaConfiguration insecureConfiguration];
+ SDLCarWindowViewController *testViewController = [[SDLCarWindowViewController alloc] init];
+ SDLFakeStreamingManagerDataSource *testDataSource = [[SDLFakeStreamingManagerDataSource alloc] init];
+ SDLLifecycleConfiguration *testLifecycleConfiguration = [SDLLifecycleConfiguration defaultConfigurationWithAppName:testAppName fullAppId:@""];
__block SDLConfiguration *testConfig = nil;
-
- __block void (^sendNotificationForHMILevel)(SDLHMILevel hmiLevel, SDLVideoStreamingState streamState) = ^(SDLHMILevel hmiLevel, SDLVideoStreamingState streamState) {
- SDLOnHMIStatus *hmiStatus = [[SDLOnHMIStatus alloc] init];
- hmiStatus.hmiLevel = hmiLevel;
- hmiStatus.videoStreamingState = streamState;
- SDLRPCNotificationNotification *notification = [[SDLRPCNotificationNotification alloc] initWithName:SDLDidChangeHMIStatusNotification object:self rpcNotification:hmiStatus];
- [[NSNotificationCenter defaultCenter] postNotification:notification];
- };
+ SDLSystemInfo *testSystemInfo = [[SDLSystemInfo alloc] initWithMake:@"Livio" model:@"Model" trim:@"Trim" modelYear:@"2021" softwareVersion:@"1.1.1.1" hardwareVersion:@"2.2.2.2"];
+ __block TestSmartConnectionManager *testConnectionManager = nil;
+ SDLVersion *version600 = [SDLVersion versionWithMajor:6 minor:0 patch:0];
beforeEach(^{
- testConfiguration.customVideoEncoderSettings = @{
- (__bridge NSString *)kVTCompressionPropertyKey_ExpectedFrameRate : @1
- };
+ // set up proper version
+ [SDLGlobals sharedGlobals].rpcVersion = version600;
+ [SDLGlobals sharedGlobals].maxHeadUnitProtocolVersion = version600;
+
+ testConfiguration.customVideoEncoderSettings = @{(id)kVTCompressionPropertyKey_ExpectedFrameRate : @1};
testConfiguration.dataSource = testDataSource;
testConfiguration.rootViewController = testViewController;
- testConnectionManager = [[TestConnectionManager alloc] init];
+ testConnectionManager = [[TestSmartConnectionManager alloc] init];
+ testConnectionManager.systemInfo = testSystemInfo;
+
+ // load connection manager with fake data
+ TestSmartConnection *connectionModel = [[TestSmartConnection alloc] init];
+ SDLGetSystemCapability *getRequest = [[SDLGetSystemCapability alloc] initWithType:SDLSystemCapabilityTypeVideoStreaming];
+ connectionModel.request = getRequest;
+ connectionModel.response = createSystemCapabilityResponse();
+ [testConnectionManager addConnectionModel:connectionModel];
testLifecycleConfiguration.appType = SDLAppHMITypeNavigation;
testConfig = [[SDLConfiguration alloc] initWithLifecycle:testLifecycleConfiguration lockScreen:[SDLLockScreenConfiguration enabledConfiguration] logging:[SDLLogConfiguration debugConfiguration] streamingMedia:testConfiguration fileManager:[SDLFileManagerConfiguration defaultConfiguration] encryption:nil];
+ });
- testSystemCapabilityManager = OCMClassMock([SDLSystemCapabilityManager class]);
- streamingLifecycleManager = [[SDLStreamingVideoLifecycleManager alloc] initWithConnectionManager:testConnectionManager configuration:testConfig systemCapabilityManager:testSystemCapabilityManager];
+ it(@"should return true by default if the system capability manager is nil", ^{
+ SDLStreamingVideoLifecycleManager *streamingLifecycleManager = [[SDLStreamingVideoLifecycleManager alloc] initWithConnectionManager:testConnectionManager configuration:testConfig systemCapabilityManager:nil];
+ expect(streamingLifecycleManager.isStreamingSupported).to(beTrue());
+ });
+
+ context(@"having inited", ^{
+ beforeEach(^{
+ testSystemCapabilityManager = [[SDLSystemCapabilityManager alloc] initWithConnectionManager:testConnectionManager];
+ streamingLifecycleManager = [[SDLStreamingVideoLifecycleManager alloc] initWithConnectionManager:testConnectionManager configuration:testConfig systemCapabilityManager:testSystemCapabilityManager];
+ testConnectionManager.lastRequestBlock = ^(__kindof SDLRPCRequest * _Nullable request, __kindof SDLRPCResponse * _Nullable response, NSError * _Nullable error) {
+ SDLLogD(@"testConnectionManager.lastRequestBlock:\n\trequest:{%@};\n\tresponse:{%@}\n\terror:{%@};", request, response, error);
+ };
+ });
+
+ afterEach(^{
+ if (streamingLifecycleManager) {
+ // sdl_shutDown: unsubscribe from notifications, otherwise the zombie managers will still receive all notifications
+ [streamingLifecycleManager sdl_shutDown];
+ streamingLifecycleManager = nil;
+ }
+ });
+
+ it(@"expect post RAI change streaming inner state", ^{
+ expect(@(streamingLifecycleManager.isStreamingSupported)).to(equal(@NO));
+ postRAINotification();
+ expect(@(streamingLifecycleManager.isStreamingSupported)).to(equal(@YES));
+ });
+
+ it(@"should get the value from the system capability manager", ^{
+ expect([testSystemCapabilityManager isCapabilitySupported:SDLSystemCapabilityTypeVideoStreaming]).to(equal(NO));
+ postRAINotification();
+ expect([testSystemCapabilityManager isCapabilitySupported:SDLSystemCapabilityTypeVideoStreaming]).to(equal(YES));
+ });
+
+ it(@"expect all properties to be inited properly", ^{
+ postRAINotification();
+ expect(streamingLifecycleManager.videoScaleManager.scale).to(equal([[SDLStreamingVideoScaleManager alloc] init].scale));
+ expect(streamingLifecycleManager.touchManager).toNot(beNil());
+ expect(streamingLifecycleManager.focusableItemManager).toNot(beNil());
+ expect(streamingLifecycleManager.isStreamingSupported).to(equal(YES));
+ expect(@(streamingLifecycleManager.isVideoConnected)).to(equal(@NO));
+ expect(@(streamingLifecycleManager.isVideoEncrypted)).to(equal(@NO));
+ expect(@(streamingLifecycleManager.isVideoStreamingPaused)).to(equal(@YES));
+ expect(@(CGSizeEqualToSize(streamingLifecycleManager.videoScaleManager.displayViewportResolution, CGSizeZero))).to(equal(@YES));
+ expect(@(streamingLifecycleManager.pixelBufferPool == NULL)).to(equal(@YES));
+ expect(@(streamingLifecycleManager.requestedEncryptionType)).to(equal(@(SDLStreamingEncryptionFlagNone)));
+ expect(@(streamingLifecycleManager.showVideoBackgroundDisplay)).to(equal(@YES));
+ expect(streamingLifecycleManager.currentAppState).to(equal(SDLAppStateActive));
+ expect(streamingLifecycleManager.currentVideoStreamState).to(equal(SDLVideoStreamManagerStateStopped));
+ expect(streamingLifecycleManager.videoFormat).to(beNil());
+ expect(streamingLifecycleManager.dataSource).to(equal(testDataSource));
+ expect(streamingLifecycleManager.supportedFormats).to(haveCount(2));
+ expect(streamingLifecycleManager.preferredFormats).to(beNil());
+ expect(streamingLifecycleManager.preferredResolutions).to(beNil());
+ expect(streamingLifecycleManager.preferredFormatIndex).to(equal(0));
+ expect(streamingLifecycleManager.preferredResolutionIndex).to(equal(0));
+ });
});
+});
- it(@"should initialize properties", ^{
- expect(streamingLifecycleManager.videoScaleManager.scale).to(equal([[SDLStreamingVideoScaleManager alloc] init].scale));
- expect(streamingLifecycleManager.touchManager).toNot(beNil());
- expect(streamingLifecycleManager.focusableItemManager).toNot(beNil());
- expect(@(streamingLifecycleManager.isStreamingSupported)).to(equal(@NO));
- expect(@(streamingLifecycleManager.isVideoConnected)).to(equal(@NO));
- expect(@(streamingLifecycleManager.isVideoEncrypted)).to(equal(@NO));
- expect(@(streamingLifecycleManager.isVideoStreamingPaused)).to(equal(@YES));
- expect(@(CGSizeEqualToSize(streamingLifecycleManager.videoScaleManager.displayViewportResolution, CGSizeZero))).to(equal(@YES));
- expect(@(streamingLifecycleManager.pixelBufferPool == NULL)).to(equal(@YES));
- expect(@(streamingLifecycleManager.requestedEncryptionType)).to(equal(@(SDLStreamingEncryptionFlagNone)));
- expect(@(streamingLifecycleManager.showVideoBackgroundDisplay)).to(equal(@YES));
- expect(streamingLifecycleManager.currentAppState).to(equal(SDLAppStateActive));
- expect(streamingLifecycleManager.currentVideoStreamState).to(equal(SDLVideoStreamManagerStateStopped));
- expect(streamingLifecycleManager.videoFormat).to(beNil());
- expect(streamingLifecycleManager.dataSource).to(equal(testDataSource));
- expect(streamingLifecycleManager.supportedFormats).to(haveCount(2));
- expect(streamingLifecycleManager.preferredFormats).to(beNil());
- expect(streamingLifecycleManager.preferredResolutions).to(beNil());
- expect(streamingLifecycleManager.preferredFormatIndex).to(equal(0));
- expect(streamingLifecycleManager.preferredResolutionIndex).to(equal(0));
+QuickSpecEnd
+
+#pragma mark - test Runtime
+
+QuickSpecBegin(SDLStreamingVideoLifecycleManagerSpec_Runtime)
+
+describe(@"test internals", ^{
+ context(@"init extended manager", ^{
+ id<SDLConnectionManagerType> mockConnectionManager = OCMProtocolMock(@protocol(SDLConnectionManagerType));
+ SDLConfiguration *configuration = [[SDLConfiguration alloc] init];
+ SDLStreamingVideoLifecycleTestManager *streamingLifecycleManager = [[SDLStreamingVideoLifecycleTestManager alloc] initWithConnectionManager:mockConnectionManager configuration:configuration systemCapabilityManager:nil];
+ SDLProtocol *protocolMock = OCMClassMock([SDLProtocol class]);
+ it(@"suspendVideo with and without a protocol", ^{
+ expect(streamingLifecycleManager.didStopVideoSession).to(equal(NO));
+ [streamingLifecycleManager sdl_suspendVideo];
+ expect(streamingLifecycleManager.didStopVideoSession).to(equal(NO));
+ streamingLifecycleManager.protocol = protocolMock;
+ [streamingLifecycleManager sdl_suspendVideo];
+ expect(streamingLifecycleManager.didStopVideoSession).to(equal(YES));
+ });
});
- describe(@"Getting isStreamingSupported", ^{
- it(@"should get the value from the system capability manager", ^{
- [streamingLifecycleManager isStreamingSupported];
- OCMVerify([testSystemCapabilityManager isCapabilitySupported:SDLSystemCapabilityTypeVideoStreaming]);
+ context(@"init extended manager", ^{
+ id<SDLConnectionManagerType> mockConnectionManager = OCMProtocolMock(@protocol(SDLConnectionManagerType));
+ SDLConfiguration *configuration = [[SDLConfiguration alloc] init];
+ SDLStreamingVideoLifecycleTestManager *streamingLifecycleManager = [[SDLStreamingVideoLifecycleTestManager alloc] initWithConnectionManager:mockConnectionManager configuration:configuration systemCapabilityManager:nil];
+
+ context(@"test didEnterStateVideoStreamStopped", ^{
+ it(@"state before and after", ^{
+ streamingLifecycleManager.shouldAutoResume = YES;
+ SDLState *stateBefore = streamingLifecycleManager.videoStreamStateMachine.currentState;
+ expect([stateBefore isEqualToString:SDLVideoStreamManagerStateStopped]).to(equal(YES));
+
+ [streamingLifecycleManager didEnterStateVideoStreamStopped];
+
+ SDLState *stateAfter = streamingLifecycleManager.videoStreamStateMachine.currentState;
+ expect([stateAfter isEqualToString:SDLVideoStreamManagerStateStarting]).to(equal(YES));
+ });
});
+ });
+
+ context(@"init extended manager", ^{
+ id<SDLConnectionManagerType> mockConnectionManager = OCMProtocolMock(@protocol(SDLConnectionManagerType));
+ SDLConfiguration *configuration = [[SDLConfiguration alloc] init];
+ SDLStreamingVideoLifecycleTestManager *streamingLifecycleManager = [[SDLStreamingVideoLifecycleTestManager alloc] initWithConnectionManager:mockConnectionManager configuration:configuration systemCapabilityManager:nil];
+
+ context(@"test videoStreamingCapabilityUpdated", ^{
+ SDLVideoStreamingCapability *videoStreamingCapabilityUpdated = OCMClassMock([SDLVideoStreamingCapability class]);
+ streamingLifecycleManager.videoStreamingCapabilityUpdated = videoStreamingCapabilityUpdated;
+ it(@"expect correct state", ^{
+ streamingLifecycleManager.shouldAutoResume = YES;
+ expect(streamingLifecycleManager.videoStreamingCapabilityUpdated).notTo(beNil());
+ expect(streamingLifecycleManager.videoStreamingCapabilityUpdated).to(equal(videoStreamingCapabilityUpdated));
+
+ [streamingLifecycleManager didEnterStateVideoStreamStarting];
- it(@"should return true by default if the system capability manager is nil", ^{
- streamingLifecycleManager = [[SDLStreamingVideoLifecycleManager alloc] initWithConnectionManager:testConnectionManager configuration:testConfig systemCapabilityManager:nil];
- expect(streamingLifecycleManager.isStreamingSupported).to(beTrue());
+ expect(streamingLifecycleManager.videoStreamingCapabilityUpdated).to(beNil());
+ expect(streamingLifecycleManager.testVideoCapabilityUpdatedWhileStarting).to(equal(videoStreamingCapabilityUpdated));
+ });
+ });
+ });
+
+ context(@"init extended manager", ^{
+ id<SDLConnectionManagerType> mockConnectionManager = OCMProtocolMock(@protocol(SDLConnectionManagerType));
+ SDLConfiguration *configuration = [[SDLConfiguration alloc] init];
+ SDLStreamingVideoLifecycleTestManager *streamingLifecycleManager = [[SDLStreamingVideoLifecycleTestManager alloc] initWithConnectionManager:mockConnectionManager configuration:configuration systemCapabilityManager:nil];
+
+ context(@"test didEnterStateVideoStreamReady", ^{
+ it(@"expect displayLink update properly", ^{
+ expect(streamingLifecycleManager.displayLink).to(beNil());
+ [streamingLifecycleManager didEnterStateVideoStreamReady];
+ expect([streamingLifecycleManager.displayLink isKindOfClass:[CADisplayLink class]]).toEventually(beTrue());
+ });
+ });
+
+ context(@"test didEnterStateVideoStreamSuspended", ^{
+ SDLVideoStreamingCapability *videoStreamingCapabilityUpdated = OCMClassMock([SDLVideoStreamingCapability class]);
+ streamingLifecycleManager.videoStreamingCapabilityUpdated = videoStreamingCapabilityUpdated;
+ it(@"expect properties to update properly", ^{
+ streamingLifecycleManager.shouldAutoResume = YES;
+
+ expect(streamingLifecycleManager.shouldAutoResume).to(equal(YES));
+ expect(streamingLifecycleManager.videoStreamingCapabilityUpdated).notTo(beNil());
+ expect(streamingLifecycleManager.videoStreamingCapabilityUpdated).to(equal(videoStreamingCapabilityUpdated));
+
+ [streamingLifecycleManager didEnterStateVideoStreamSuspended];
+
+ expect(streamingLifecycleManager.shouldAutoResume).to(equal(NO));
+ expect(streamingLifecycleManager.videoStreamingCapability).toEventually(equal(videoStreamingCapabilityUpdated));
+ expect(streamingLifecycleManager.shouldAutoResume).toEventually(equal(NO));
+
+ [streamingLifecycleManager.videoStreamStateMachine transitionToState:SDLVideoStreamManagerStateStarting];
+ SDLProtocol *protocolMock = OCMClassMock([SDLProtocol class]);
+ SDLProtocolMessage *startServiceNAK = OCMClassMock([SDLProtocolMessage class]);
+ SDLState *stateBefore = streamingLifecycleManager.videoStreamStateMachine.currentState;
+ [streamingLifecycleManager protocol:protocolMock didReceiveStartServiceNAK:startServiceNAK];
+ SDLState *stateAfter = streamingLifecycleManager.videoStreamStateMachine.currentState;
+
+ expect(stateBefore).to(equal(SDLVideoStreamManagerStateStarting));
+ expect(stateAfter).to(equal(SDLVideoStreamManagerStateStarting));
+ });
+ });
+ });
+
+ context(@"init extended manager", ^{
+ id<SDLConnectionManagerType> mockConnectionManager = OCMProtocolMock(@protocol(SDLConnectionManagerType));
+ SDLConfiguration *configuration = [[SDLConfiguration alloc] init];
+ SDLStreamingVideoLifecycleTestManager *streamingLifecycleManager = [[SDLStreamingVideoLifecycleTestManager alloc] initWithConnectionManager:mockConnectionManager configuration:configuration systemCapabilityManager:nil];
+
+ context(@"test sdl_videoStreamingCapabilityDidUpdate", ^{
+ streamingLifecycleManager.shouldAutoResume = YES;
+ SDLVideoStreamingCapability *videoStreamingCapabilityUpdated = OCMClassMock([SDLVideoStreamingCapability class]);
+ streamingLifecycleManager.videoStreamingCapabilityUpdated = videoStreamingCapabilityUpdated;
+ it(@"expect correct state", ^{
+ SDLSystemCapability *systemCapability = nil;
+ [streamingLifecycleManager.videoStreamStateMachine transitionToState:SDLVideoStreamManagerStateStarting];
+ SDLState *stateBefore = streamingLifecycleManager.videoStreamStateMachine.currentState;
+ [streamingLifecycleManager sdl_videoStreamingCapabilityDidUpdate:systemCapability];
+ expect(streamingLifecycleManager.testVideoCapabilityUpdatedWhileStarting).to(beNil());
+ expect(streamingLifecycleManager.testVideoCapabilityUpdatedWhenStreaming).to(beNil());
+ SDLState *stateAfter = streamingLifecycleManager.videoStreamStateMachine.currentState;
+
+ expect(stateBefore).to(equal(SDLVideoStreamManagerStateStarting));
+ expect(stateAfter).to(equal(SDLVideoStreamManagerStateStarting));
+
+ systemCapability = [[SDLSystemCapability alloc] init];
+ systemCapability.videoStreamingCapability = OCMClassMock([SDLVideoStreamingCapability class]);
+
+ stateBefore = streamingLifecycleManager.videoStreamStateMachine.currentState;
+ [streamingLifecycleManager sdl_videoStreamingCapabilityDidUpdate:systemCapability];
+ expect(streamingLifecycleManager.testVideoCapabilityUpdatedWhileStarting).to(equal(systemCapability.videoStreamingCapability));
+ expect(streamingLifecycleManager.testVideoCapabilityUpdatedWhenStreaming).to(beNil());
+ stateAfter = streamingLifecycleManager.videoStreamStateMachine.currentState;
+
+ expect(stateBefore).to(equal(SDLVideoStreamManagerStateStarting));
+ expect(stateAfter).to(equal(SDLVideoStreamManagerStateStarting));
+
+ // state ready
+ streamingLifecycleManager.testVideoCapabilityUpdatedWhileStarting = nil;
+ streamingLifecycleManager.testVideoCapabilityUpdatedWhenStreaming = nil;
+
+ [streamingLifecycleManager.videoStreamStateMachine transitionToState:SDLVideoStreamManagerStateReady];
+ stateBefore = streamingLifecycleManager.videoStreamStateMachine.currentState;
+ [streamingLifecycleManager sdl_videoStreamingCapabilityDidUpdate:systemCapability];
+ expect(streamingLifecycleManager.testVideoCapabilityUpdatedWhileStarting).to(beNil());
+ expect(streamingLifecycleManager.testVideoCapabilityUpdatedWhenStreaming).to(equal(systemCapability.videoStreamingCapability));
+ stateAfter = streamingLifecycleManager.videoStreamStateMachine.currentState;
+
+ expect(stateBefore).to(equal(SDLVideoStreamManagerStateReady));
+ expect(stateAfter).to(equal(SDLVideoStreamManagerStateReady));
+ });
});
});
+});
+
+describe(@"runtime tests", ^{
+ __block SDLStreamingVideoLifecycleManager *streamingLifecycleManager = nil;
+ SDLStreamingMediaConfiguration *testConfiguration = [SDLStreamingMediaConfiguration insecureConfiguration];
+ SDLCarWindowViewController *testViewController = [[SDLCarWindowViewController alloc] init];
+ SDLFakeStreamingManagerDataSource *testDataSource = [[SDLFakeStreamingManagerDataSource alloc] init];
+ NSString *testAppName = @"Test App";
+ SDLLifecycleConfiguration *testLifecycleConfiguration = [SDLLifecycleConfiguration defaultConfigurationWithAppName:testAppName fullAppId:@""];
+ SDLVersion *version600 = [SDLVersion versionWithMajor:6 minor:0 patch:0];
+
+ // set proper version up
+ [SDLGlobals sharedGlobals].rpcVersion = version600;
+ [SDLGlobals sharedGlobals].maxHeadUnitProtocolVersion = version600;
+
+ testConfiguration.customVideoEncoderSettings = @{(id)kVTCompressionPropertyKey_ExpectedFrameRate : @1};
+ testConfiguration.dataSource = testDataSource;
+ testConfiguration.rootViewController = testViewController;
+
+ // load connection manager with fake data
+ TestSmartConnectionManager *testConnectionManager = [[TestSmartConnectionManager alloc] init];
+ TestSmartConnection *connectionModel = [[TestSmartConnection alloc] init];
+ SDLGetSystemCapability *getRequest = [[SDLGetSystemCapability alloc] initWithType:SDLSystemCapabilityTypeVideoStreaming];
+ connectionModel.request = getRequest;
+ connectionModel.response = createSystemCapabilityResponse();
+ [testConnectionManager addConnectionModel:connectionModel];
+
+ testLifecycleConfiguration.appType = SDLAppHMITypeNavigation;
+
+ SDLConfiguration *testConfig = [[SDLConfiguration alloc] initWithLifecycle:testLifecycleConfiguration lockScreen:[SDLLockScreenConfiguration enabledConfiguration] logging:[SDLLogConfiguration debugConfiguration] streamingMedia:testConfiguration fileManager:[SDLFileManagerConfiguration defaultConfiguration] encryption:nil];
+
+ SDLSystemCapabilityManager *testSystemCapabilityManager = [[SDLSystemCapabilityManager alloc] initWithConnectionManager:testConnectionManager];
+
+ beforeEach(^{
+ streamingLifecycleManager = [[SDLStreamingVideoLifecycleManager alloc] initWithConnectionManager:testConnectionManager configuration:testConfig systemCapabilityManager:testSystemCapabilityManager];
+ testConnectionManager.lastRequestBlock = ^(__kindof SDLRPCRequest * _Nullable request, __kindof SDLRPCResponse * _Nullable response, NSError * _Nullable error) {
+ SDLLogD(@"testConnectionManager.lastRequestBlock:\n\trequest:{%@};\n\tresponse:{%@}\n\terror:{%@};", request, response, error);
+ };
+ });
+
+ afterEach(^{
+ if (streamingLifecycleManager) {
+ // sdl_shutDown: unsubscribe from notifications, otherwise the zombie managers will still receive all notifications
+ [streamingLifecycleManager sdl_shutDown];
+ streamingLifecycleManager = nil;
+ }
+ });
+
describe(@"when started", ^{
__block BOOL readyHandlerSuccess = NO;
__block NSError *readyHandlerError = nil;
-
- __block SDLProtocol *protocolMock = OCMClassMock([SDLProtocol class]);
+ SDLProtocol *protocolMock = OCMClassMock([SDLProtocol class]);
beforeEach(^{
readyHandlerSuccess = NO;
@@ -139,7 +451,7 @@ describe(@"the streaming video manager", ^{
});
it(@"should be ready to stream", ^{
- expect(@(streamingLifecycleManager.isStreamingSupported)).to(equal(@NO));
+ expect(@(streamingLifecycleManager.isStreamingSupported)).to(equal(@YES));
expect(@(streamingLifecycleManager.isVideoConnected)).to(equal(@NO));
expect(@(streamingLifecycleManager.isVideoEncrypted)).to(equal(@NO));
expect(@(streamingLifecycleManager.isVideoStreamingPaused)).to(equal(@YES));
@@ -179,7 +491,11 @@ describe(@"the streaming video manager", ^{
someRegisterAppInterfaceResponse = [[SDLRegisterAppInterfaceResponse alloc] init];
someRegisterAppInterfaceResponse.hmiCapabilities = someHMICapabilities;
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
someRegisterAppInterfaceResponse.vehicleType = testVehicleType;
+#pragma clang diagnostic pop
+ someRegisterAppInterfaceResponse.success = @YES;
SDLRPCResponseNotification *notification = [[SDLRPCResponseNotification alloc] initWithName:SDLDidReceiveRegisterAppInterfaceResponse object:self rpcResponse:someRegisterAppInterfaceResponse];
@@ -188,7 +504,6 @@ describe(@"the streaming video manager", ^{
it(@"should save the connected vehicle make but not the screen size", ^{
expect(@(CGSizeEqualToSize(streamingLifecycleManager.videoScaleManager.displayViewportResolution, CGSizeZero))).toEventually(equal(@YES));
- expect(streamingLifecycleManager.connectedVehicleMake).toEventually(equal(testVehicleType.make));
});
});
@@ -209,9 +524,8 @@ describe(@"the streaming video manager", ^{
#pragma clang diagnostic ignored "-Wdeprecated"
someRegisterAppInterfaceResponse.displayCapabilities = someDisplayCapabilities;
#pragma clang diagnostic pop
- someRegisterAppInterfaceResponse.vehicleType = testVehicleType;
- someRegisterAppInterfaceResponse.vehicleType = testVehicleType;
+ someRegisterAppInterfaceResponse.success = @YES;
SDLRPCResponseNotification *notification = [[SDLRPCResponseNotification alloc] initWithName:SDLDidReceiveRegisterAppInterfaceResponse object:self rpcResponse:someRegisterAppInterfaceResponse];
@@ -220,7 +534,6 @@ describe(@"the streaming video manager", ^{
it(@"should save the connected vehicle make and the screen size", ^{
expect(@(CGSizeEqualToSize(streamingLifecycleManager.videoScaleManager.displayViewportResolution, CGSizeMake(600, 100)))).toEventually(equal(@YES));
- expect(streamingLifecycleManager.connectedVehicleMake).toEventually(equal(testVehicleType.make));
});
});
});
@@ -392,6 +705,8 @@ describe(@"the streaming video manager", ^{
context(@"and both streams are closed", ^{
beforeEach(^{
[streamingLifecycleManager.videoStreamStateMachine setToState:SDLVideoStreamManagerStateStopped fromOldState:nil callEnterTransition:NO];
+
+ postRAINotification();
});
context(@"and the hmi state is none", ^{
@@ -453,96 +768,19 @@ describe(@"the streaming video manager", ^{
});
describe(@"sending a video capabilities request", ^{
- __block SDLImageResolution *resolution = [[SDLImageResolution alloc] initWithWidth:42 height:69];
- __block int32_t maxBitrate = 12345;
- __block NSArray<SDLVideoStreamingFormat *> *testFormats = @[[[SDLVideoStreamingFormat alloc] initWithCodec:SDLVideoStreamingCodecH265 protocol:SDLVideoStreamingProtocolRTMP], [[SDLVideoStreamingFormat alloc] initWithCodec:SDLVideoStreamingCodecH264 protocol:SDLVideoStreamingProtocolRTP]];
- __block BOOL testHapticsSupported = YES;
-
beforeEach(^{
[streamingLifecycleManager.videoStreamStateMachine setToState:SDLVideoStreamManagerStateStarting fromOldState:nil callEnterTransition:YES];
});
- it(@"should send out a video capabilities request", ^{
- expect(testConnectionManager.receivedRequests.lastObject).to(beAnInstanceOf([SDLGetSystemCapability class]));
-
- SDLGetSystemCapability *getCapability = (SDLGetSystemCapability *)testConnectionManager.receivedRequests.lastObject;
- expect(getCapability.systemCapabilityType).to(equal(SDLSystemCapabilityTypeVideoStreaming));
- });
-
- describe(@"after sending GetSystemCapabilities", ^{
- context(@"and receiving an error response", ^{
- // This happens if the HU doesn't understand GetSystemCapabilities
- beforeEach(^{
- SDLGenericResponse *genericResponse = [[SDLGenericResponse alloc] init];
- genericResponse.resultCode = SDLResultInvalidData;
-
- [testConnectionManager respondToLastRequestWithResponse:genericResponse];
- });
-
- it(@"should have correct format and resolution", ^{
- expect(streamingLifecycleManager.preferredFormats).to(haveCount(1));
- expect(streamingLifecycleManager.preferredFormats.firstObject.codec).to(equal(SDLVideoStreamingCodecH264));
- expect(streamingLifecycleManager.preferredFormats.firstObject.protocol).to(equal(SDLVideoStreamingProtocolRAW));
-
- expect(streamingLifecycleManager.preferredResolutions).to(haveCount(1));
- expect(streamingLifecycleManager.preferredResolutions.firstObject.resolutionWidth).to(equal(0));
- expect(streamingLifecycleManager.preferredResolutions.firstObject.resolutionHeight).to(equal(0));
- });
-
- context(@"and receiving a response", ^{
- __block SDLVideoStreamingCapability *testVideoStreamingCapability = nil;
-
- beforeEach(^{
- SDLGetSystemCapabilityResponse *response = [[SDLGetSystemCapabilityResponse alloc] init];
- response.success = @YES;
- response.systemCapability = [[SDLSystemCapability alloc] init];
- response.systemCapability.systemCapabilityType = SDLSystemCapabilityTypeVideoStreaming;
-
- testVideoStreamingCapability = [[SDLVideoStreamingCapability alloc] initWithPreferredResolution:resolution maxBitrate:@(maxBitrate) supportedFormats:testFormats hapticSpatialDataSupported:@(testHapticsSupported) diagonalScreenSize:@(8.5) pixelPerInch:@(117) scale:@(1.25) preferredFPS:@(15)];
- response.systemCapability.videoStreamingCapability = testVideoStreamingCapability;
- [testConnectionManager respondToLastRequestWithResponse:response];
- });
-
- it(@"should have correct data from the data source", ^{
- // Correct formats should be retrieved from the data source
- expect(streamingLifecycleManager.preferredResolutions).to(haveCount(1));
- expect(streamingLifecycleManager.preferredResolutions.firstObject.resolutionWidth).to(equal(resolution.resolutionWidth));
- expect(streamingLifecycleManager.preferredResolutions.firstObject.resolutionHeight).to(equal(resolution.resolutionHeight));
-
- expect(streamingLifecycleManager.preferredFormats).to(haveCount(streamingLifecycleManager.supportedFormats.count + 1));
- expect(streamingLifecycleManager.preferredFormats.firstObject.codec).to(equal(testDataSource.extraFormat.codec));
- expect(streamingLifecycleManager.preferredFormats.firstObject.protocol).to(equal(testDataSource.extraFormat.protocol));
-
- // The haptic manager should be enabled
- expect(streamingLifecycleManager.focusableItemManager.enableHapticDataRequests).to(equal(YES));
- });
-
- it(@"should have decided upon the correct preferred format and resolution", ^{
- SDLVideoStreamingFormat *preferredFormat = streamingLifecycleManager.preferredFormats[streamingLifecycleManager.preferredFormatIndex];
- expect(preferredFormat.codec).to(equal(SDLVideoStreamingCodecH264));
- expect(preferredFormat.protocol).to(equal(SDLVideoStreamingProtocolRTP));
-
- SDLImageResolution *preferredResolution = streamingLifecycleManager.preferredResolutions[streamingLifecycleManager.preferredResolutionIndex];
- expect(preferredResolution.resolutionHeight).to(equal(@69));
- expect(preferredResolution.resolutionWidth).to(equal(@42));
- });
-
- it(@"should set the correct scale value", ^{
- expect(streamingLifecycleManager.videoScaleManager.scale).to(equal(testVideoStreamingCapability.scale));
- });
- });
- });
- });
-
describe(@"after receiving a Video Start ACK", ^{
__block SDLProtocolHeader *testVideoHeader = nil;
__block SDLProtocolMessage *testVideoMessage = nil;
__block SDLControlFramePayloadVideoStartServiceAck *testVideoStartServicePayload = nil;
- __block int64_t testMTU = 789456;
- __block int32_t testVideoHeight = 42;
- __block int32_t testVideoWidth = 32;
- __block SDLVideoStreamingCodec testVideoCodec = SDLVideoStreamingCodecH264;
- __block SDLVideoStreamingProtocol testVideoProtocol = SDLVideoStreamingProtocolRTP;
+ const int64_t testMTU = 789456;
+ const int32_t testVideoHeight = 42;
+ const int32_t testVideoWidth = 32;
+ SDLVideoStreamingCodec testVideoCodec = SDLVideoStreamingCodecH264;
+ SDLVideoStreamingProtocol testVideoProtocol = SDLVideoStreamingProtocolRTP;
beforeEach(^{
[streamingLifecycleManager.videoStreamStateMachine setToState:SDLVideoStreamManagerStateStarting fromOldState:nil callEnterTransition:NO];
@@ -556,6 +794,7 @@ describe(@"the streaming video manager", ^{
context(@"with data", ^{
beforeEach(^{
+ streamingLifecycleManager.videoScaleManager.scale = 1.0f;
testVideoStartServicePayload = [[SDLControlFramePayloadVideoStartServiceAck alloc] initWithMTU:testMTU height:testVideoHeight width:testVideoWidth protocol:testVideoProtocol codec:testVideoCodec];
testVideoMessage = [[SDLV2ProtocolMessage alloc] initWithHeader:testVideoHeader andPayload:testVideoStartServicePayload.data];
[streamingLifecycleManager protocol:protocolMock didReceiveStartServiceACK:testVideoMessage];
@@ -566,11 +805,18 @@ describe(@"the streaming video manager", ^{
expect(streamingLifecycleManager.videoEncrypted).to(equal(YES));
expect(streamingLifecycleManager.videoFormat).to(equal([[SDLVideoStreamingFormat alloc] initWithCodec:testVideoCodec protocol:testVideoProtocol]));
expect(streamingLifecycleManager.currentVideoStreamState).to(equal(SDLVideoStreamManagerStateReady));
+
+ const CGSize displayViewportResolution = streamingLifecycleManager.videoScaleManager.displayViewportResolution;
+ const CGSize testSize = CGSizeMake(testVideoWidth, testVideoHeight);
+ if (!CGSizeEqualToSize(displayViewportResolution, testSize)) {
+ failWithMessage(([NSString stringWithFormat:@"wrong displayViewportResolution: %@, expected: %@", NSStringFromCGSize(displayViewportResolution), NSStringFromCGSize(testSize)]));
+ }
});
});
context(@"with missing data", ^{
beforeEach(^{
+ streamingLifecycleManager.videoScaleManager.scale = 1.0f;
testVideoStartServicePayload = [[SDLControlFramePayloadVideoStartServiceAck alloc] initWithMTU:testMTU height:testVideoHeight width:testVideoWidth protocol:nil codec:nil];
testVideoMessage = [[SDLV2ProtocolMessage alloc] initWithHeader:testVideoHeader andPayload:testVideoStartServicePayload.data];
[streamingLifecycleManager protocol:protocolMock didReceiveStartServiceACK:testVideoMessage];
@@ -585,6 +831,7 @@ describe(@"the streaming video manager", ^{
context(@"with missing screen height and screen width values", ^{
beforeEach(^{
+ postRAINotification();
streamingLifecycleManager.preferredResolutions = @[];
testVideoStartServicePayload = [[SDLControlFramePayloadVideoStartServiceAck alloc] initWithMTU:testMTU height:SDLControlFrameInt32NotFound width:SDLControlFrameInt32NotFound protocol:nil codec:nil];
@@ -645,11 +892,13 @@ describe(@"the streaming video manager", ^{
SDLVideoStreamingFormat *testVideoFormat2 = [[SDLVideoStreamingFormat alloc] initWithCodec:SDLVideoStreamingCodecH264 protocol:SDLVideoStreamingProtocolRTP];
testPreferredFormats = @[testVideoFormat, testVideoFormat2];
streamingLifecycleManager.preferredFormats = testPreferredFormats;
+ streamingLifecycleManager.preferredFormatIndex = 0;
SDLImageResolution *testImageResolution = [[SDLImageResolution alloc] initWithWidth:400 height:200];
SDLImageResolution *testImageResolution2 = [[SDLImageResolution alloc] initWithWidth:500 height:800];
testPreferredResolutions = @[testImageResolution, testImageResolution2];
streamingLifecycleManager.preferredResolutions = testPreferredResolutions;
+ streamingLifecycleManager.preferredResolutionIndex = 0;
testVideoStartNakPayload = [[SDLControlFramePayloadNak alloc] initWithRejectedParams:@[[NSString stringWithUTF8String:SDLControlFrameHeightKey], [NSString stringWithUTF8String:SDLControlFrameVideoCodecKey]] reason:@"failed"];
testVideoMessage = [[SDLV2ProtocolMessage alloc] initWithHeader:testVideoHeader andPayload:testVideoStartNakPayload.data];
@@ -668,10 +917,12 @@ describe(@"the streaming video manager", ^{
SDLVideoStreamingFormat *testVideoFormat2 = [[SDLVideoStreamingFormat alloc] initWithCodec:SDLVideoStreamingCodecH264 protocol:SDLVideoStreamingProtocolRTP];
testPreferredFormats = @[testVideoFormat, testVideoFormat2];
streamingLifecycleManager.preferredFormats = testPreferredFormats;
+ streamingLifecycleManager.preferredFormatIndex = 0;
SDLImageResolution *testImageResolution = [[SDLImageResolution alloc] initWithWidth:400 height:200];
testPreferredResolutions = @[testImageResolution];
streamingLifecycleManager.preferredResolutions = testPreferredResolutions;
+ streamingLifecycleManager.preferredResolutionIndex = 0;
testVideoStartNakPayload = [[SDLControlFramePayloadNak alloc] initWithRejectedParams:@[[NSString stringWithUTF8String:SDLControlFrameVideoCodecKey]] reason:@"failed"];
testVideoMessage = [[SDLV2ProtocolMessage alloc] initWithHeader:testVideoHeader andPayload:testVideoStartNakPayload.data];
@@ -721,19 +972,16 @@ describe(@"the streaming video manager", ^{
});
describe(@"after receiving a video end ACK", ^{
- __block SDLProtocolHeader *testVideoHeader = nil;
- __block SDLProtocolMessage *testVideoMessage = nil;
-
beforeEach(^{
[streamingLifecycleManager.videoStreamStateMachine setToState:SDLVideoStreamManagerStateStarting fromOldState:nil callEnterTransition:NO];
- testVideoHeader = [[SDLV2ProtocolHeader alloc] initWithVersion:5];
+ SDLProtocolHeader *testVideoHeader = [[SDLV2ProtocolHeader alloc] initWithVersion:5];
testVideoHeader.frameType = SDLFrameTypeSingle;
testVideoHeader.frameData = SDLFrameInfoEndServiceACK;
testVideoHeader.encrypted = NO;
testVideoHeader.serviceType = SDLServiceTypeVideo;
- testVideoMessage = [[SDLV2ProtocolMessage alloc] initWithHeader:testVideoHeader andPayload:nil];
+ SDLProtocolMessage *testVideoMessage = [[SDLV2ProtocolMessage alloc] initWithHeader:testVideoHeader andPayload:nil];
[streamingLifecycleManager protocol:protocolMock didReceiveEndServiceACK:testVideoMessage];
});
@@ -743,23 +991,20 @@ describe(@"the streaming video manager", ^{
});
describe(@"after receiving a video end NAK", ^{
- __block SDLProtocolHeader *testVideoHeader = nil;
- __block SDLProtocolMessage *testVideoMessage = nil;
-
beforeEach(^{
[streamingLifecycleManager.videoStreamStateMachine setToState:SDLVideoStreamManagerStateStarting fromOldState:nil callEnterTransition:NO];
- testVideoHeader = [[SDLV2ProtocolHeader alloc] initWithVersion:5];
+ SDLProtocolHeader *testVideoHeader = [[SDLV2ProtocolHeader alloc] initWithVersion:5];
testVideoHeader.frameType = SDLFrameTypeSingle;
testVideoHeader.frameData = SDLFrameInfoEndServiceNACK;
testVideoHeader.encrypted = NO;
testVideoHeader.serviceType = SDLServiceTypeVideo;
- testVideoMessage = [[SDLV2ProtocolMessage alloc] initWithHeader:testVideoHeader andPayload:nil];
+ SDLProtocolMessage *testVideoMessage = [[SDLV2ProtocolMessage alloc] initWithHeader:testVideoHeader andPayload:nil];
[streamingLifecycleManager protocol:protocolMock didReceiveEndServiceNAK:testVideoMessage];
});
- it(@"should have set all the right properties", ^{
+ it(@"expect video stream is stopped", ^{
expect(streamingLifecycleManager.currentVideoStreamState).to(equal(SDLVideoStreamManagerStateStopped));
});
});
@@ -774,7 +1019,6 @@ describe(@"the streaming video manager", ^{
[streamingLifecycleManager endVideoServiceWithCompletionHandler:^ {
handlerCalled = YES;
}];
- streamingLifecycleManager.connectedVehicleMake = @"OEM_make_2";
});
context(@"when the manager is not stopped", ^{
@@ -786,7 +1030,6 @@ describe(@"the streaming video manager", ^{
it(@"should transition to the stopped state", ^{
expect(streamingLifecycleManager.currentVideoStreamState).to(equal(SDLVideoStreamManagerStateStopped));
expect(streamingLifecycleManager.protocol).to(beNil());
- expect(streamingLifecycleManager.connectedVehicleMake).to(beNil());
expect(streamingLifecycleManager.hmiLevel).to(equal(SDLHMILevelNone));
expect(streamingLifecycleManager.videoStreamingState).to(equal(SDLVideoStreamingStateNotStreamable));
expect(streamingLifecycleManager.preferredFormatIndex).to(equal(0));
@@ -804,7 +1047,6 @@ describe(@"the streaming video manager", ^{
it(@"should stay in the stopped state", ^{
expect(streamingLifecycleManager.currentVideoStreamState).to(equal(SDLVideoStreamManagerStateStopped));
expect(streamingLifecycleManager.protocol).to(beNil());
- expect(streamingLifecycleManager.connectedVehicleMake).to(beNil());
expect(streamingLifecycleManager.hmiLevel).to(equal(SDLHMILevelNone));
expect(streamingLifecycleManager.videoStreamingState).to(equal(SDLVideoStreamingStateNotStreamable));
expect(streamingLifecycleManager.preferredFormatIndex).to(equal(0));
@@ -815,7 +1057,7 @@ describe(@"the streaming video manager", ^{
});
describe(@"starting the manager", ^{
- __block SDLProtocol *protocolMock = OCMClassMock([SDLProtocol class]);
+ SDLProtocol *protocolMock = OCMClassMock([SDLProtocol class]);
beforeEach(^{
[streamingLifecycleManager startWithProtocol:protocolMock];
@@ -831,16 +1073,13 @@ describe(@"the streaming video manager", ^{
});
context(@"when the end video service ACKs", ^{
- __block SDLProtocolHeader *testVideoHeader = nil;
- __block SDLProtocolMessage *testVideoMessage = nil;
-
beforeEach(^{
- testVideoHeader = [[SDLV2ProtocolHeader alloc] initWithVersion:5];
+ SDLProtocolHeader *testVideoHeader = [[SDLV2ProtocolHeader alloc] initWithVersion:5];
testVideoHeader.frameType = SDLFrameTypeSingle;
testVideoHeader.frameData = SDLFrameInfoEndServiceACK;
testVideoHeader.encrypted = NO;
testVideoHeader.serviceType = SDLServiceTypeVideo;
- testVideoMessage = [[SDLV2ProtocolMessage alloc] initWithHeader:testVideoHeader andPayload:nil];
+ SDLProtocolMessage *testVideoMessage = [[SDLV2ProtocolMessage alloc] initWithHeader:testVideoHeader andPayload:nil];
[streamingLifecycleManager protocol:protocolMock didReceiveEndServiceACK:testVideoMessage];
});
@@ -851,16 +1090,13 @@ describe(@"the streaming video manager", ^{
});
context(@"when the end audio service NAKs", ^{
- __block SDLProtocolHeader *testVideoHeader = nil;
- __block SDLProtocolMessage *testVideoMessage = nil;
-
beforeEach(^{
- testVideoHeader = [[SDLV2ProtocolHeader alloc] initWithVersion:5];
+ SDLProtocolHeader *testVideoHeader = [[SDLV2ProtocolHeader alloc] initWithVersion:5];
testVideoHeader.frameType = SDLFrameTypeSingle;
testVideoHeader.frameData = SDLFrameInfoEndServiceNACK;
testVideoHeader.encrypted = NO;
testVideoHeader.serviceType = SDLServiceTypeVideo;
- testVideoMessage = [[SDLV2ProtocolMessage alloc] initWithHeader:testVideoHeader andPayload:nil];
+ SDLProtocolMessage *testVideoMessage = [[SDLV2ProtocolMessage alloc] initWithHeader:testVideoHeader andPayload:nil];
[streamingLifecycleManager protocol:protocolMock didReceiveEndServiceNAK:testVideoMessage];
});
@@ -873,12 +1109,537 @@ describe(@"the streaming video manager", ^{
});
describe(@"Creating a background video stream string", ^{
- __block NSString *expectedVideoStreamBackgroundString = [NSString stringWithFormat:@"When it is safe to do so, open %@ on your phone", testAppName];
+ NSString *expectedVideoStreamBackgroundString = [NSString stringWithFormat:@"When it is safe to do so, open %@ on your phone", testAppName];
it(@"Should return the correct video stream background string for the screen size", ^{
expect(streamingLifecycleManager.videoStreamBackgroundString).to(match(expectedVideoStreamBackgroundString));
});
});
+
+ describe(@"Getting notifications of VideoStreamingCapability updates", ^{
+ beforeEach(^{
+ streamingLifecycleManager.delegate = nil;
+ streamingLifecycleManager.dataSource = nil;
+ streamingLifecycleManager.customEncoderSettings = nil;
+ [streamingLifecycleManager.videoStreamStateMachine setToState:SDLVideoStreamManagerStateStarting fromOldState:nil callEnterTransition:NO];
+ });
+
+ context(@"the module does not support the GetSystemCapabilities request", ^{
+ __block SDLSystemCapability *testNilVideoStreamingCapability = nil;
+
+ beforeEach(^{
+ testNilVideoStreamingCapability = [[SDLSystemCapability alloc] init];
+ testNilVideoStreamingCapability.videoStreamingCapability = nil;
+
+ [streamingLifecycleManager sdl_videoStreamingCapabilityDidUpdate:testNilVideoStreamingCapability];
+ });
+
+ it(@"should use the library's default values", ^{
+ expect(streamingLifecycleManager.videoStreamingCapability.maxBitrate).to(beNil());
+ expect(streamingLifecycleManager.videoStreamingCapability.preferredFPS).to(beNil());
+
+ expect(streamingLifecycleManager.preferredFormats).to(haveCount(1));
+ expect(streamingLifecycleManager.preferredFormats[0].codec).to(equal(SDLVideoStreamingCodecH264));
+ expect(streamingLifecycleManager.preferredFormats[0].protocol).to(equal(SDLVideoStreamingProtocolRAW));
+
+ expect(streamingLifecycleManager.preferredResolutions).to(haveCount(1));
+ expect(streamingLifecycleManager.preferredResolutions[0].resolutionWidth).to(equal(streamingLifecycleManager.videoScaleManager.displayViewportResolution.width));
+ expect(streamingLifecycleManager.preferredResolutions[0].resolutionHeight).to(equal(streamingLifecycleManager.videoScaleManager.displayViewportResolution.height));
+
+
+ expect(streamingLifecycleManager.focusableItemManager.enableHapticDataRequests).to(beFalse());
+ expect(streamingLifecycleManager.videoScaleManager.scale).to(equal(streamingLifecycleManager.videoScaleManager.scale));
+ });
+ });
+
+ context(@"the module supports the GetSystemCapabilities request", ^{
+ __block SDLSystemCapability *testSystemCapability = nil;
+ __block SDLVideoStreamingCapability *testVideoStreamingCapability = nil;
+
+ context(@"the module does not support VideoStreamingCapability.additionalVideoStreamingCapabilities", ^{
+ beforeEach(^{
+ SDLImageResolution *resolution = [[SDLImageResolution alloc] initWithWidth:44 height:99];
+ SDLVideoStreamingFormat *format1 = [[SDLVideoStreamingFormat alloc] initWithCodec:SDLVideoStreamingCodecH265 protocol:SDLVideoStreamingProtocolRTMP];
+ SDLVideoStreamingFormat *format2 = [[SDLVideoStreamingFormat alloc] initWithCodec:SDLVideoStreamingCodecH264 protocol:SDLVideoStreamingProtocolRTP];
+ NSArray<SDLVideoStreamingFormat *> *testFormats = @[format1, format2];
+
+ testVideoStreamingCapability = [[SDLVideoStreamingCapability alloc] initWithPreferredResolution:resolution maxBitrate:@(333) supportedFormats:testFormats hapticSpatialDataSupported:@YES diagonalScreenSize:@(8.5) pixelPerInch:@(117) scale:@(1) preferredFPS:@(222)];
+
+ testSystemCapability = [[SDLSystemCapability alloc] init];
+ testSystemCapability.videoStreamingCapability = testVideoStreamingCapability;
+
+ [streamingLifecycleManager sdl_videoStreamingCapabilityDidUpdate:testSystemCapability];
+ });
+
+ it(@"should use the data from the VideoStreamingCapability", ^{
+ expect(streamingLifecycleManager.videoStreamingCapability.maxBitrate).to(equal(testVideoStreamingCapability.maxBitrate));
+ expect(streamingLifecycleManager.videoStreamingCapability.preferredFPS).to(equal(testVideoStreamingCapability.preferredFPS));
+
+ expect(streamingLifecycleManager.preferredResolutions).to(haveCount(1));
+ expect(streamingLifecycleManager.preferredResolutions[0].resolutionWidth).to(equal(testVideoStreamingCapability.preferredResolution.resolutionWidth));
+ expect(streamingLifecycleManager.preferredResolutions[0].resolutionHeight).to(equal(testVideoStreamingCapability.preferredResolution.resolutionHeight));
+
+ expect(streamingLifecycleManager.preferredFormats).to(haveCount(2));
+ expect(streamingLifecycleManager.preferredFormats[0].codec).to(equal(testVideoStreamingCapability.supportedFormats[0].codec));
+ expect(streamingLifecycleManager.preferredFormats[0].protocol).to(equal(testVideoStreamingCapability.supportedFormats[0].protocol));
+ expect(streamingLifecycleManager.preferredFormats[1].codec).to(equal(testVideoStreamingCapability.supportedFormats[1].codec));
+ expect(streamingLifecycleManager.preferredFormats[1].protocol).to(equal(testVideoStreamingCapability.supportedFormats[1].protocol));
+
+ expect(streamingLifecycleManager.focusableItemManager.enableHapticDataRequests).to(equal(YES));
+ expect(streamingLifecycleManager.videoScaleManager.scale).to(equal(testVideoStreamingCapability.scale));
+ });
+ });
+
+ context(@"the module supports VideoStreamingCapability.additionalVideoStreamingCapabilities", ^{
+ __block SDLVideoStreamingCapability *testAdditionalVideoStreamingCapability = nil;
+
+ beforeEach(^{
+ SDLImageResolution *resolution = [[SDLImageResolution alloc] initWithWidth:44 height:99];
+ SDLVideoStreamingFormat *format1 = [[SDLVideoStreamingFormat alloc] initWithCodec:SDLVideoStreamingCodecH265 protocol:SDLVideoStreamingProtocolRTMP];
+ SDLVideoStreamingFormat *format2 = [[SDLVideoStreamingFormat alloc] initWithCodec:SDLVideoStreamingCodecH264 protocol:SDLVideoStreamingProtocolRTP];
+ NSArray<SDLVideoStreamingFormat *> *testFormats = @[format1, format2];
+
+ testAdditionalVideoStreamingCapability = [[SDLVideoStreamingCapability alloc] init];
+ testAdditionalVideoStreamingCapability.preferredResolution = [[SDLImageResolution alloc] initWithWidth:500 height:100];
+ testAdditionalVideoStreamingCapability.hapticSpatialDataSupported = @YES;
+ testAdditionalVideoStreamingCapability.diagonalScreenSize = @8;
+ testAdditionalVideoStreamingCapability.scale = @1;
+
+ testVideoStreamingCapability = [[SDLVideoStreamingCapability alloc] initWithPreferredResolution:resolution maxBitrate:@(333) supportedFormats:testFormats hapticSpatialDataSupported:@YES diagonalScreenSize:@(8.5) pixelPerInch:@(117) scale:@(1) preferredFPS:@(222)];
+ testVideoStreamingCapability.additionalVideoStreamingCapabilities = @[testAdditionalVideoStreamingCapability];
+
+ testSystemCapability = [[SDLSystemCapability alloc] init];
+ testSystemCapability.videoStreamingCapability = testVideoStreamingCapability;
+
+ [streamingLifecycleManager sdl_videoStreamingCapabilityDidUpdate:testSystemCapability];
+ });
+
+ it(@"should use the data from the VideoStreamingCapability and additionalVideoStreamingCapabilities", ^{
+ expect(streamingLifecycleManager.videoStreamingCapability.maxBitrate).to(equal(testVideoStreamingCapability.maxBitrate));
+ expect(streamingLifecycleManager.videoStreamingCapability.preferredFPS).to(equal(testVideoStreamingCapability.preferredFPS));
+
+ expect(streamingLifecycleManager.preferredResolutions).to(haveCount(2));
+ expect(streamingLifecycleManager.preferredResolutions[0].resolutionWidth).to(equal(testVideoStreamingCapability.preferredResolution.resolutionWidth));
+ expect(streamingLifecycleManager.preferredResolutions[0].resolutionHeight).to(equal(testVideoStreamingCapability.preferredResolution.resolutionHeight));
+ expect(streamingLifecycleManager.preferredResolutions[1].resolutionWidth).to(equal(testVideoStreamingCapability.additionalVideoStreamingCapabilities[0].preferredResolution.resolutionWidth));
+ expect(streamingLifecycleManager.preferredResolutions[1].resolutionHeight).to(equal(testVideoStreamingCapability.additionalVideoStreamingCapabilities[0].preferredResolution.resolutionHeight));
+
+ expect(streamingLifecycleManager.preferredFormats).to(haveCount(2));
+ expect(streamingLifecycleManager.preferredFormats[0].codec).to(equal(testVideoStreamingCapability.supportedFormats[0].codec));
+ expect(streamingLifecycleManager.preferredFormats[0].protocol).to(equal(testVideoStreamingCapability.supportedFormats[0].protocol));
+ expect(streamingLifecycleManager.preferredFormats[1].codec).to(equal(testVideoStreamingCapability.supportedFormats[1].codec));
+ expect(streamingLifecycleManager.preferredFormats[1].protocol).to(equal(testVideoStreamingCapability.supportedFormats[1].protocol));
+
+ expect(streamingLifecycleManager.focusableItemManager.enableHapticDataRequests).to(equal(YES));
+ expect(streamingLifecycleManager.videoScaleManager.scale).to(equal(testVideoStreamingCapability.scale));
+ });
+ });
+ });
+ });
+
+ describe(@"setting the video encoder properties", ^{
+ __block SDLVideoStreamingCapability *testVideoStreamingCapability = nil;
+
+ beforeEach(^{
+ testVideoStreamingCapability = nil;
+ });
+
+ describe(@"setting the bitrate", ^{
+ context(@"the VideoStreamingCapability returns a maxBitrate", ^{
+ it(@"should use the custom averageBitRate set by the developer when it is less than the VideoStreamingCapability's maxBitrate", ^{
+ int testVideoStreamingCapabilityMaxBitrate = 99999;
+ float testCustomBitRate = 88;
+
+ testVideoStreamingCapability = [[SDLVideoStreamingCapability alloc] init];
+ testVideoStreamingCapability.maxBitrate = @(testVideoStreamingCapabilityMaxBitrate);
+
+ streamingLifecycleManager.customEncoderSettings = @{(__bridge NSString *)kVTCompressionPropertyKey_ExpectedFrameRate:@(111), (__bridge NSString *)kVTCompressionPropertyKey_AverageBitRate:@(testCustomBitRate)};
+
+ [streamingLifecycleManager sdl_applyVideoCapability:testVideoStreamingCapability];
+
+ expect(streamingLifecycleManager.videoEncoderSettings[(__bridge NSString *)kVTCompressionPropertyKey_AverageBitRate]).to(equal(@(testCustomBitRate)));
+ });
+
+ it(@"should use the the module's VideoStreamingCapability's maxBitrate if it is less than the averageBitRate set by the developer ", ^{
+ int testVideoStreamingCapabilityMaxBitrate = 88;
+ int testCustomBitRate = 99999;
+
+ testVideoStreamingCapability = [[SDLVideoStreamingCapability alloc] init];
+ testVideoStreamingCapability.maxBitrate = @(testVideoStreamingCapabilityMaxBitrate);
+
+ streamingLifecycleManager.customEncoderSettings = @{(__bridge NSString *)kVTCompressionPropertyKey_ExpectedFrameRate:@(111), (__bridge NSString *)kVTCompressionPropertyKey_AverageBitRate:@(testCustomBitRate)};
+
+ [streamingLifecycleManager sdl_applyVideoCapability:testVideoStreamingCapability];
+
+ int expectedCustomBitRate = testVideoStreamingCapabilityMaxBitrate * 1000; //convert from video streaming capability bitrate unit of kbps to video encoder units of bps
+ expect(streamingLifecycleManager.videoEncoderSettings[(__bridge NSString *)kVTCompressionPropertyKey_AverageBitRate]).to(equal(@(expectedCustomBitRate)));
+ });
+
+ it(@"should use the the module's VideoStreamingCapability's maxBitrate if no averageBitRate was set by the developer ", ^{
+ int testVideoStreamingCapabilityMaxBitrate = 7889;
+
+ testVideoStreamingCapability = [[SDLVideoStreamingCapability alloc] init];
+ testVideoStreamingCapability.maxBitrate = @(testVideoStreamingCapabilityMaxBitrate);
+
+ streamingLifecycleManager.customEncoderSettings = nil;
+
+ [streamingLifecycleManager sdl_applyVideoCapability:testVideoStreamingCapability];
+
+ int expectedCustomBitRate = testVideoStreamingCapabilityMaxBitrate * 1000; //convert from video streaming capability bitrate unit of kbps to video encoder units of bps
+ expect(streamingLifecycleManager.videoEncoderSettings[(__bridge NSString *)kVTCompressionPropertyKey_AverageBitRate]).to(equal(@(expectedCustomBitRate)));
+ });
+ });
+
+ context(@"the VideoStreamingCapability returns a nil maxBitrate", ^{
+ it(@"should use the custom averageBitRate set by the developer even if it is larger than the default averageBitRate", ^{
+ int testCustomBitRate = 9900000; // larger than the default of @600000
+
+ testVideoStreamingCapability = [[SDLVideoStreamingCapability alloc] init];
+ testVideoStreamingCapability.maxBitrate = nil;
+
+ streamingLifecycleManager.customEncoderSettings = @{(__bridge NSString *)kVTCompressionPropertyKey_ExpectedFrameRate:@(111), (__bridge NSString *)kVTCompressionPropertyKey_AverageBitRate:@(testCustomBitRate)};
+
+ [streamingLifecycleManager sdl_applyVideoCapability:testVideoStreamingCapability];
+
+ expect(streamingLifecycleManager.videoEncoderSettings[(__bridge NSString *)kVTCompressionPropertyKey_AverageBitRate]).to(equal(@(testCustomBitRate)));
+ });
+
+ it(@"should use the custom averageBitRate set by the developer even if it is smaller than the default averageBitRate", ^{
+ int testCustomBitRate = 2; // less than the default of @600000
+
+ testVideoStreamingCapability = [[SDLVideoStreamingCapability alloc] init];
+ testVideoStreamingCapability.maxBitrate = nil;
+
+ streamingLifecycleManager.customEncoderSettings = @{(__bridge NSString *)kVTCompressionPropertyKey_ExpectedFrameRate:@(111), (__bridge NSString *)kVTCompressionPropertyKey_AverageBitRate:@(testCustomBitRate)};
+
+ [streamingLifecycleManager sdl_applyVideoCapability:testVideoStreamingCapability];
+
+ expect(streamingLifecycleManager.videoEncoderSettings[(__bridge NSString *)kVTCompressionPropertyKey_AverageBitRate]).to(equal(@(testCustomBitRate)));
+ });
+
+ it(@"should use the default averageBitRate if a custom averageBitRate was not set by the developer", ^{
+ testVideoStreamingCapability = [[SDLVideoStreamingCapability alloc] init];
+ testVideoStreamingCapability.maxBitrate = nil;
+
+ streamingLifecycleManager.customEncoderSettings = nil;
+
+ [streamingLifecycleManager sdl_applyVideoCapability:testVideoStreamingCapability];
+
+ expect(streamingLifecycleManager.videoEncoderSettings[(__bridge NSString *)kVTCompressionPropertyKey_AverageBitRate]).to(equal(@(600000)));
+ });
+ });
+ });
+
+ describe(@"setting the framerate", ^{
+ context(@"the VideoStreamingCapability returns a preferredFPS", ^{
+ it(@"should use the custom expectedFrameRate set by the developer when it is less than the VideoStreamingCapability's preferredFPS", ^{
+ int testVideoStreamingCapabilityPreferredFPS = 1001;
+ float testCustomExpectedFrameRate = 66;
+
+ testVideoStreamingCapability = [[SDLVideoStreamingCapability alloc] init];
+ testVideoStreamingCapability.preferredFPS = @(testVideoStreamingCapabilityPreferredFPS);
+
+ streamingLifecycleManager.customEncoderSettings = @{(__bridge NSString *)kVTCompressionPropertyKey_ExpectedFrameRate:@(testCustomExpectedFrameRate), (__bridge NSString *)kVTCompressionPropertyKey_AverageBitRate:@(22)};
+
+ [streamingLifecycleManager sdl_applyVideoCapability:testVideoStreamingCapability];
+
+ expect(streamingLifecycleManager.videoEncoderSettings[(__bridge NSString *)kVTCompressionPropertyKey_ExpectedFrameRate]).to(equal(@(testCustomExpectedFrameRate)));
+ });
+
+ it(@"should use the the module's VideoStreamingCapability's preferredFPS if it is less than the expectedFrameRate set by the developer ", ^{
+ int testVideoStreamingCapabilityPreferredFPS = 66;
+ int testCustomExpectedFrameRate = 1001;
+
+ testVideoStreamingCapability = [[SDLVideoStreamingCapability alloc] init];
+ testVideoStreamingCapability.preferredFPS = @(testVideoStreamingCapabilityPreferredFPS);
+
+ streamingLifecycleManager.customEncoderSettings = @{(__bridge NSString *)kVTCompressionPropertyKey_ExpectedFrameRate:@(testCustomExpectedFrameRate), (__bridge NSString *)kVTCompressionPropertyKey_AverageBitRate:@(22)};
+
+ [streamingLifecycleManager sdl_applyVideoCapability:testVideoStreamingCapability];
+
+ expect(streamingLifecycleManager.videoEncoderSettings[(__bridge NSString *)kVTCompressionPropertyKey_ExpectedFrameRate]).to(equal(@(testVideoStreamingCapabilityPreferredFPS)));
+ });
+
+ it(@"should use the the module's VideoStreamingCapability's preferredFPS if no expectedFrameRate was set by the developer ", ^{
+ int testVideoStreamingCapabilityPreferredFPS = 66;
+
+ testVideoStreamingCapability = [[SDLVideoStreamingCapability alloc] init];
+ testVideoStreamingCapability.preferredFPS = @(testVideoStreamingCapabilityPreferredFPS);
+
+ streamingLifecycleManager.customEncoderSettings = nil;
+
+ [streamingLifecycleManager sdl_applyVideoCapability:testVideoStreamingCapability];
+
+ expect(streamingLifecycleManager.videoEncoderSettings[(__bridge NSString *)kVTCompressionPropertyKey_ExpectedFrameRate]).to(equal(@(testVideoStreamingCapabilityPreferredFPS)));
+ });
+ });
+
+ context(@"the VideoStreamingCapability returns a nil preferredFPS", ^{
+ it(@"should use the custom expectedFrameRate set by the developer even if it is larger than the default expectedFrameRate", ^{
+ int testCustomExpectedFrameRate = 990; // larger than the default of @15
+
+ testVideoStreamingCapability = [[SDLVideoStreamingCapability alloc] init];
+ testVideoStreamingCapability.preferredFPS = nil;
+
+ streamingLifecycleManager.customEncoderSettings = @{(__bridge NSString *)kVTCompressionPropertyKey_ExpectedFrameRate:@(testCustomExpectedFrameRate), (__bridge NSString *)kVTCompressionPropertyKey_AverageBitRate:@(22)};
+
+ [streamingLifecycleManager sdl_applyVideoCapability:testVideoStreamingCapability];
+
+ expect(streamingLifecycleManager.videoEncoderSettings[(__bridge NSString *)kVTCompressionPropertyKey_ExpectedFrameRate]).to(equal(@(testCustomExpectedFrameRate)));
+ });
+
+ it(@"should use the custom expectedFrameRate set by the developer even if it is smaller than the default expectedFrameRate", ^{
+ int testCustomExpectedFrameRate = 2; // less than the default of @15
+
+ testVideoStreamingCapability = [[SDLVideoStreamingCapability alloc] init];
+ testVideoStreamingCapability.preferredFPS = nil;
+
+ streamingLifecycleManager.customEncoderSettings = @{(__bridge NSString *)kVTCompressionPropertyKey_ExpectedFrameRate:@(testCustomExpectedFrameRate), (__bridge NSString *)kVTCompressionPropertyKey_AverageBitRate:@(22)};
+
+ [streamingLifecycleManager sdl_applyVideoCapability:testVideoStreamingCapability];
+
+ expect(streamingLifecycleManager.videoEncoderSettings[(__bridge NSString *)kVTCompressionPropertyKey_ExpectedFrameRate]).to(equal(@(testCustomExpectedFrameRate)));
+ });
+
+ it(@"should use the default expectedFrameRate if a custom expectedFrameRate was not set by the developer", ^{
+ testVideoStreamingCapability = [[SDLVideoStreamingCapability alloc] init];
+ testVideoStreamingCapability.preferredFPS = nil;
+
+ streamingLifecycleManager.customEncoderSettings = nil;
+
+ [streamingLifecycleManager sdl_applyVideoCapability:testVideoStreamingCapability];
+
+ expect(streamingLifecycleManager.videoEncoderSettings[(__bridge NSString *)kVTCompressionPropertyKey_ExpectedFrameRate]).to(equal(@(15)));
+ });
+ });
+ });
+ });
});
QuickSpecEnd
+
+#pragma mark - test Capabilities Filtering Logic
+
+QuickSpecBegin(SDLStreamingVideoLifecycleManagerSpec_CapabilitiesFiltering)
+// declare and init constants that do not change during test lifecycle
+SDLImageResolution *resolution1 = [[SDLImageResolution alloc] initWithWidth:800 height:380];
+SDLImageResolution *resolution2 = [[SDLImageResolution alloc] initWithWidth:320 height:200];
+SDLImageResolution *resolution3 = [[SDLImageResolution alloc] initWithWidth:480 height:320];
+SDLImageResolution *resolution4 = [[SDLImageResolution alloc] initWithWidth:400 height:380];
+SDLImageResolution *resolution5 = [[SDLImageResolution alloc] initWithWidth:800 height:240];
+SDLImageResolution *resolution6 = [[SDLImageResolution alloc] initWithWidth:200 height:400]; // portrait small
+SDLImageResolution *resolution7 = [[SDLImageResolution alloc] initWithWidth:2000 height:4000]; // portrait large
+SDLImageResolution *resolution8 = [[SDLImageResolution alloc] initWithWidth:200 height:200];
+
+SDLVideoStreamingCapability *capability1 = [[SDLVideoStreamingCapability alloc] init];
+capability1.preferredResolution = resolution1;
+capability1.hapticSpatialDataSupported = @YES;
+capability1.diagonalScreenSize = @8;
+capability1.scale = @1;
+
+SDLVideoStreamingCapability *capability2 = [[SDLVideoStreamingCapability alloc] init];
+capability2.preferredResolution = resolution2;
+capability2.hapticSpatialDataSupported = @NO;
+capability2.diagonalScreenSize = @3;
+
+SDLVideoStreamingCapability *capability3 = [[SDLVideoStreamingCapability alloc] init];
+capability3.preferredResolution = resolution3;
+capability3.hapticSpatialDataSupported = @YES;
+capability3.diagonalScreenSize = @5;
+
+SDLVideoStreamingCapability *capability4 = [[SDLVideoStreamingCapability alloc] init];
+capability4.preferredResolution = resolution4;
+capability4.hapticSpatialDataSupported = @YES;
+capability4.diagonalScreenSize = @4;
+
+SDLVideoStreamingCapability *capability5 = [[SDLVideoStreamingCapability alloc] init];
+capability5.preferredResolution = resolution5;
+capability5.hapticSpatialDataSupported = @YES;
+capability5.diagonalScreenSize = @4;
+
+SDLVideoStreamingCapability *capability6 = [[SDLVideoStreamingCapability alloc] init];
+capability6.preferredResolution = resolution1;
+capability6.hapticSpatialDataSupported = @YES;
+capability6.diagonalScreenSize = @5;
+capability6.scale = @1.5;
+
+SDLVideoStreamingCapability *capability7 = [[SDLVideoStreamingCapability alloc] init];
+capability7.preferredResolution = resolution1;
+capability7.hapticSpatialDataSupported = @YES;
+capability7.diagonalScreenSize = @4;
+capability7.scale = @2;
+
+SDLVideoStreamingCapability *capability8 = [[SDLVideoStreamingCapability alloc] init]; // portrait small
+capability8.preferredResolution = resolution6;
+capability8.hapticSpatialDataSupported = @YES;
+capability8.diagonalScreenSize = @4;
+
+SDLVideoStreamingCapability *capability9 = [[SDLVideoStreamingCapability alloc] init]; // portrait large
+capability9.preferredResolution = resolution7;
+capability9.hapticSpatialDataSupported = @YES;
+capability9.diagonalScreenSize = @4;
+
+SDLVideoStreamingCapability *capability10 = [[SDLVideoStreamingCapability alloc] init]; // square
+capability10.preferredResolution = resolution8;
+capability10.hapticSpatialDataSupported = @YES;
+capability10.diagonalScreenSize = @2;
+capability10.scale = @1;
+
+SDLVideoStreamingFormat *vsFormat1 = [[SDLVideoStreamingFormat alloc] initWithCodec:SDLVideoStreamingCodecH264 protocol:SDLVideoStreamingProtocolRAW];
+SDLVideoStreamingFormat *vsFormat2 = [[SDLVideoStreamingFormat alloc] initWithCodec:SDLVideoStreamingCodecH264 protocol:SDLVideoStreamingProtocolRTP];
+SDLVideoStreamingFormat *vsFormat3 = [[SDLVideoStreamingFormat alloc] initWithCodec:SDLVideoStreamingCodecTheora protocol:SDLVideoStreamingProtocolRTSP];
+SDLVideoStreamingFormat *vsFormat4 = [[SDLVideoStreamingFormat alloc] initWithCodec:SDLVideoStreamingCodecVP8 protocol:SDLVideoStreamingProtocolRTMP];
+SDLVideoStreamingFormat *vsFormat5 = [[SDLVideoStreamingFormat alloc] initWithCodec:SDLVideoStreamingCodecVP9 protocol:SDLVideoStreamingProtocolWebM];
+
+SDLVideoStreamingCapability *capability0 = [[SDLVideoStreamingCapability alloc] initWithPreferredResolution:resolution1 maxBitrate:@(400000) supportedFormats:@[vsFormat1, vsFormat2, vsFormat3, vsFormat4, vsFormat5] hapticSpatialDataSupported:@YES diagonalScreenSize:@(8) pixelPerInch:@(96) scale:@(1) preferredFPS:nil];
+capability0.additionalVideoStreamingCapabilities = @[capability1, capability2, capability3, capability4, capability5, capability6, capability7, capability8, capability9, capability10];
+
+describe(@"supported video capabilities and formats", ^{
+ TestSmartConnectionManager *testConnectionManager = [[TestSmartConnectionManager alloc] init];
+ SDLConfiguration *testConfig = [[SDLConfiguration alloc] init];
+ SDLStreamingVideoLifecycleManager *streamingLifecycleManager = [[SDLStreamingVideoLifecycleManager alloc] initWithConnectionManager:testConnectionManager configuration:testConfig systemCapabilityManager:nil];
+
+ context(@"neither landscape nor portrait constraint set", ^{
+ NSArray <SDLVideoStreamingCapability*>* allCapabilities = [capability0 allVideoStreamingCapabilities];
+
+ it(@"should let all capabilities in (nothing filtered out)", ^{
+ streamingLifecycleManager.supportedLandscapeStreamingRange = nil;
+ streamingLifecycleManager.supportedPortraitStreamingRange = nil;
+ NSArray *filteredCapabilities = [streamingLifecycleManager matchVideoCapability:capability0];
+ expect(filteredCapabilities).to(equal(allCapabilities));
+ });
+ });
+
+ context(@"landscape restricted and any portrait", ^{
+ SDLImageResolution *resMin = [[SDLImageResolution alloc] initWithWidth:320 height:200];
+ SDLImageResolution *resMax = [[SDLImageResolution alloc] initWithWidth:350 height:220];
+ SDLVideoStreamingRange *landRange = [[SDLVideoStreamingRange alloc] initWithMinimumResolution:resMin maximumResolution:resMax];
+
+ it(@"should filter 320x200 and small and large portrait", ^{
+ streamingLifecycleManager.supportedLandscapeStreamingRange = landRange;
+ streamingLifecycleManager.supportedPortraitStreamingRange = nil;
+
+ expect(streamingLifecycleManager.supportedLandscapeStreamingRange).to(equal(landRange));
+ expect(streamingLifecycleManager.supportedPortraitStreamingRange).to(beNil());
+ // 320x200 & portrait small & large & square are expected
+ NSArray *expectedArray = @[capability2, capability8, capability9, capability10];
+ NSArray *matchArray = [streamingLifecycleManager matchVideoCapability:capability0];
+ expect(matchArray).to(equal(expectedArray));
+ });
+ });
+
+ context(@"portrait restricted and wrong landscape", ^{
+ it(@"should filter portrait small", ^{
+ // wrong range: max < min, will throw an exception
+ SDLImageResolution *resMaxL = [[SDLImageResolution alloc] initWithWidth:320 height:200];
+ SDLImageResolution *resMinL = [[SDLImageResolution alloc] initWithWidth:350 height:220];
+
+ expectAction(^{
+ SDLVideoStreamingRange *landRange = [[SDLVideoStreamingRange alloc] initWithMinimumResolution:resMinL maximumResolution:resMaxL];
+ expect(landRange).to(beNil());
+ }).to(raiseException());
+
+ });
+ });
+
+ context(@"both landscape and portrait restricted", ^{
+ SDLImageResolution *resMinP = [[SDLImageResolution alloc] initWithWidth:200 height:320];
+ SDLImageResolution *resMaxP = [[SDLImageResolution alloc] initWithWidth:300 height:420];
+ SDLVideoStreamingRange *portRange = [[SDLVideoStreamingRange alloc] initWithMinimumResolution:resMinP maximumResolution:resMaxP];
+
+ SDLImageResolution *resMinL = [[SDLImageResolution alloc] initWithWidth:320 height:200];
+ SDLImageResolution *resMaxL = [[SDLImageResolution alloc] initWithWidth:350 height:220];
+ SDLVideoStreamingRange *landRange = [[SDLVideoStreamingRange alloc] initWithMinimumResolution:resMinL maximumResolution:resMaxL];
+
+ it(@"should filter 320x200 and portrait small", ^{
+ streamingLifecycleManager.supportedLandscapeStreamingRange = landRange;
+ streamingLifecycleManager.supportedPortraitStreamingRange = portRange;
+
+ expect(streamingLifecycleManager.supportedLandscapeStreamingRange).to(equal(landRange));
+ expect(streamingLifecycleManager.supportedPortraitStreamingRange).to(equal(portRange));
+ NSArray *expectedArray = @[capability2, capability8];
+ NSArray *matchArray = [streamingLifecycleManager matchVideoCapability:capability0];
+ expect(matchArray).to(equal(expectedArray));
+ });
+ });
+
+ context(@"square", ^{
+ SDLImageResolution *resMin = [[SDLImageResolution alloc] initWithWidth:100 height:100];
+ SDLImageResolution *resMax = [[SDLImageResolution alloc] initWithWidth:200 height:200];
+ SDLVideoStreamingRange *range = [[SDLVideoStreamingRange alloc] initWithMinimumResolution:resMin maximumResolution:resMax];
+ range.minimumAspectRatio = 1.0;
+ range.maximumAspectRatio = 1.0;
+ range.minimumDiagonal = 1;
+
+ it(@"expect all portraits and square", ^{
+ streamingLifecycleManager.supportedLandscapeStreamingRange = range;
+ streamingLifecycleManager.supportedPortraitStreamingRange = nil;
+
+ expect(streamingLifecycleManager.supportedLandscapeStreamingRange).to(equal(range));
+ expect(streamingLifecycleManager.supportedPortraitStreamingRange).to(beNil());
+
+ // no portrait restriction therefore all portrait & square
+ NSArray *expectedArray = @[capability8, capability9, capability10];
+ NSArray *matchArray = [streamingLifecycleManager matchVideoCapability:capability0];
+ expect(matchArray).to(equal(expectedArray));
+ });
+
+ it(@"expect all landscapes and square", ^{
+ streamingLifecycleManager.supportedLandscapeStreamingRange = nil;
+ streamingLifecycleManager.supportedPortraitStreamingRange = range;
+
+ expect(streamingLifecycleManager.supportedLandscapeStreamingRange).to(beNil());
+ expect(streamingLifecycleManager.supportedPortraitStreamingRange).to(equal(range));
+
+ NSArray *expectedArray = @[capability0, capability1, capability2, capability3, capability4, capability5, capability6, capability7, capability10];
+ NSArray *matchArray = [streamingLifecycleManager matchVideoCapability:capability0];
+ expect(matchArray).to(equal(expectedArray));
+ });
+
+ it(@"expect square alone", ^{
+ streamingLifecycleManager.supportedLandscapeStreamingRange = range;
+ streamingLifecycleManager.supportedPortraitStreamingRange = range;
+
+ expect(streamingLifecycleManager.supportedLandscapeStreamingRange).to(equal(range));
+ expect(streamingLifecycleManager.supportedPortraitStreamingRange).to(equal(range));
+
+ NSArray *expectedArray = @[capability10];
+ NSArray *matchArray = [streamingLifecycleManager matchVideoCapability:capability0];
+ expect(matchArray).to(equal(expectedArray));
+ });
+ });
+});
+
+QuickSpecEnd
+
+#pragma mark - helper functions
+
+static void postRAINotification() {
+ SDLRegisterAppInterfaceResponse *rai = [[SDLRegisterAppInterfaceResponse alloc] init];
+ rai.hmiCapabilities = [[SDLHMICapabilities alloc] initWithNavigation:@YES phoneCall:@YES videoStreaming:@YES remoteControl:@YES appServices:@YES displays:@YES seatLocation:@YES driverDistraction:@YES];
+ rai.success = @YES;
+ SDLRPCResponseNotification *note = [[SDLRPCResponseNotification alloc] initWithName:SDLDidReceiveRegisterAppInterfaceResponse object:nil rpcResponse:rai];
+ [[NSNotificationCenter defaultCenter] postNotification:note];
+}
+
+static void sendNotificationForHMILevel(SDLHMILevel hmiLevel, SDLVideoStreamingState streamState) {
+ SDLOnHMIStatus *hmiStatus = [[SDLOnHMIStatus alloc] init];
+ hmiStatus.hmiLevel = hmiLevel;
+ hmiStatus.videoStreamingState = streamState;
+ SDLRPCNotificationNotification *notification = [[SDLRPCNotificationNotification alloc] initWithName:SDLDidChangeHMIStatusNotification object:nil rpcNotification:hmiStatus];
+ [[NSNotificationCenter defaultCenter] postNotification:notification];
+};
+
+static SDLGetSystemCapabilityResponse* createSystemCapabilityResponse() {
+ SDLImageResolution *resolution = [[SDLImageResolution alloc] initWithWidth:testVSCResolutionWidth height:testVSCResolutionHeight];
+ SDLVideoStreamingFormat *format1 = [[SDLVideoStreamingFormat alloc] initWithCodec:SDLVideoStreamingCodecH265 protocol:SDLVideoStreamingProtocolRTMP];
+ SDLVideoStreamingFormat *format2 = [[SDLVideoStreamingFormat alloc] initWithCodec:SDLVideoStreamingCodecH264 protocol:SDLVideoStreamingProtocolRTP];
+ NSArray<SDLVideoStreamingFormat *> *testFormats = @[format1, format2];
+
+ SDLVideoStreamingCapability *videoStreamingCapability = [[SDLVideoStreamingCapability alloc] initWithPreferredResolution:resolution maxBitrate:@(testVSCMaxBitrate) supportedFormats:testFormats hapticSpatialDataSupported:@YES diagonalScreenSize:@(8.5) pixelPerInch:@(117) scale:@(testVSCScale) preferredFPS:nil];
+ SDLGetSystemCapabilityResponse *response = [[SDLGetSystemCapabilityResponse alloc] init];
+ response.success = @YES;
+ response.systemCapability = [[SDLSystemCapability alloc] initWithVideoStreamingCapability:videoStreamingCapability];
+
+ return response;
+}
diff --git a/SmartDeviceLinkTests/DevAPISpecs/SDLSystemInfoSpec.m b/SmartDeviceLinkTests/DevAPISpecs/SDLSystemInfoSpec.m
new file mode 100644
index 000000000..a1be29973
--- /dev/null
+++ b/SmartDeviceLinkTests/DevAPISpecs/SDLSystemInfoSpec.m
@@ -0,0 +1,83 @@
+//
+// SDLSystemInfoSpec.m
+// SmartDeviceLinkTests
+//
+// Created by Joel Fischer on 2/24/21.
+// Copyright © 2021 smartdevicelink. All rights reserved.
+//
+
+#import <Quick/Quick.h>
+#import <Nimble/Nimble.h>
+
+#import "SDLSystemInfo.h"
+#import "SDLVehicleType.h"
+
+QuickSpecBegin(SDLSystemInfoSpec)
+
+NSString *hardVersion = @"1.2.3";
+NSString *softVersion = @"9.8.7";
+NSString *make = @"Make";
+NSString *model = @"model";
+NSString *trim = @"trim";
+NSString *modelYear = @"2021";
+SDLVehicleType *vehicleType = [[SDLVehicleType alloc] initWithMake:make model:model modelYear:modelYear trim:trim];
+
+describe(@"system info", ^{
+ __block SDLSystemInfo *systemInfo = nil;
+
+ context(@"init", ^{
+ beforeEach(^{
+ systemInfo = [[SDLSystemInfo alloc] init];
+ });
+
+ it(@"expect all properties to be nil", ^{
+ expect(systemInfo).notTo(beNil());
+ expect(systemInfo.vehicleType).to(beNil());
+ expect(systemInfo.systemSoftwareVersion).to(beNil());
+ expect(systemInfo.systemHardwareVersion).to(beNil());
+ });
+ });
+
+ context(@"initWithMake:model:trim:modelYear:softwareVersion:hardwareVersion:", ^{
+ beforeEach(^{
+ systemInfo = [[SDLSystemInfo alloc] initWithMake:make model:model trim:trim modelYear:modelYear softwareVersion:softVersion hardwareVersion:hardVersion];
+ });
+
+ it(@"expect all properties to be set properly", ^{
+ expect(systemInfo).notTo(beNil());
+ expect(systemInfo.vehicleType.make).to(equal(make));
+ expect(systemInfo.vehicleType.model).to(equal(model));
+ expect(systemInfo.vehicleType.trim).to(equal(trim));
+ expect(systemInfo.vehicleType.modelYear).to(equal(modelYear));
+ expect(systemInfo.systemSoftwareVersion).to(equal(softVersion));
+ expect(systemInfo.systemHardwareVersion).to(equal(hardVersion));
+ });
+ });
+
+ context(@"initWithVehicleType:systemSoftwareVersion:systemHardwareVersion:", ^{
+ beforeEach(^{
+ systemInfo = [[SDLSystemInfo alloc] initWithVehicleType:vehicleType softwareVersion:softVersion hardwareVersion:hardVersion];
+ });
+
+ it(@"expect all properties to be set properly", ^{
+ expect(systemInfo).notTo(beNil());
+ expect(systemInfo.vehicleType).to(equal(vehicleType));
+ expect(systemInfo.systemSoftwareVersion).to(equal(softVersion));
+ expect(systemInfo.systemHardwareVersion).to(equal(hardVersion));
+ });
+ });
+
+ context(@"alloc and init", ^{
+ beforeEach(^{
+ systemInfo = [SDLSystemInfo alloc];
+ });
+
+ it(@"expect test object to be inited", ^{
+ expect(systemInfo.vehicleType).to(beNil());
+ expect(systemInfo.systemHardwareVersion).to(beNil());
+ expect(systemInfo.systemSoftwareVersion).to(beNil());
+ });
+ });
+});
+
+QuickSpecEnd
diff --git a/SmartDeviceLinkTests/DevAPISpecs/SDLTextAndGraphicManagerSpec.m b/SmartDeviceLinkTests/DevAPISpecs/SDLTextAndGraphicManagerSpec.m
index 41ff4ba56..ea230248c 100644
--- a/SmartDeviceLinkTests/DevAPISpecs/SDLTextAndGraphicManagerSpec.m
+++ b/SmartDeviceLinkTests/DevAPISpecs/SDLTextAndGraphicManagerSpec.m
@@ -42,7 +42,7 @@
@property (assign, nonatomic) BOOL isDirty;
-- (void)sdl_displayCapabilityDidUpdate:(SDLSystemCapability *)systemCapability;
+- (void)sdl_displayCapabilityDidUpdate;
@end
@@ -489,11 +489,11 @@ describe(@"text and graphic manager", ^{
beforeEach(^{
testHMIStatus = [[SDLOnHMIStatus alloc] init];
- testWindowCapability = [[SDLWindowCapability alloc] initWithWindowID:@(SDLPredefinedWindowsDefaultWindow) textFields:nil imageFields:nil imageTypeSupported:nil templatesAvailable:nil numCustomPresetsAvailable:nil buttonCapabilities:nil softButtonCapabilities:nil menuLayoutsAvailable:nil dynamicUpdateCapabilities:nil];
+ testWindowCapability = [[SDLWindowCapability alloc] initWithWindowID:@(SDLPredefinedWindowsDefaultWindow) textFields:nil imageFields:nil imageTypeSupported:nil templatesAvailable:nil numCustomPresetsAvailable:nil buttonCapabilities:nil softButtonCapabilities:nil menuLayoutsAvailable:nil dynamicUpdateCapabilities:nil keyboardCapabilities:nil];
testDisplayCapability = [[SDLDisplayCapability alloc] initWithDisplayName:@"Test display" windowCapabilities:@[testWindowCapability] windowTypeSupported:nil];
- testSystemCapability = [[SDLSystemCapability alloc] initWithDisplayCapabilities:@[testDisplayCapability]];
- [testManager sdl_displayCapabilityDidUpdate:testSystemCapability];
+ OCMStub([mockSystemCapabilityManager defaultMainWindowCapability]).andReturn(testWindowCapability);
+ [testManager sdl_displayCapabilityDidUpdate];
});
// with a non-default window
@@ -554,13 +554,14 @@ describe(@"text and graphic manager", ^{
SDLRPCNotificationNotification *notification = [[SDLRPCNotificationNotification alloc] initWithName:SDLDidChangeHMIStatusNotification object:nil rpcNotification:testHMIStatus];
[[NSNotificationCenter defaultCenter] postNotification:notification];
- testWindowCapability = [[SDLWindowCapability alloc] initWithWindowID:@(SDLPredefinedWindowsDefaultWindow) textFields:nil imageFields:nil imageTypeSupported:nil templatesAvailable:nil numCustomPresetsAvailable:nil buttonCapabilities:nil softButtonCapabilities:nil menuLayoutsAvailable:nil dynamicUpdateCapabilities:nil];
+ testWindowCapability = [[SDLWindowCapability alloc] initWithWindowID:@(SDLPredefinedWindowsDefaultWindow) textFields:nil imageFields:nil imageTypeSupported:nil templatesAvailable:nil numCustomPresetsAvailable:nil buttonCapabilities:nil softButtonCapabilities:nil menuLayoutsAvailable:nil dynamicUpdateCapabilities:nil keyboardCapabilities:nil];
testDisplayCapability = [[SDLDisplayCapability alloc] initWithDisplayName:@"Test display" windowCapabilities:@[testWindowCapability] windowTypeSupported:nil];
testSystemCapability = [[SDLSystemCapability alloc] initWithDisplayCapabilities:@[testDisplayCapability]];
});
it(@"should start the transaction queue and not send a transaction", ^{
- [testManager sdl_displayCapabilityDidUpdate:testSystemCapability];
+ OCMStub([mockSystemCapabilityManager defaultMainWindowCapability]).andReturn(testWindowCapability);
+ [testManager sdl_displayCapabilityDidUpdate];
expect(testManager.transactionQueue.isSuspended).to(beFalse());
expect(testManager.transactionQueue.operationCount).to(equal(0));
@@ -569,7 +570,8 @@ describe(@"text and graphic manager", ^{
context(@"if there's data", ^{
beforeEach(^{
testManager.textField1 = @"test";
- [testManager sdl_displayCapabilityDidUpdate:testSystemCapability];
+ OCMStub([mockSystemCapabilityManager defaultMainWindowCapability]).andReturn(testWindowCapability);
+ [testManager sdl_displayCapabilityDidUpdate];
});
it(@"should send an update and not supersede the previous update", ^{
diff --git a/SmartDeviceLinkTests/DevAPISpecs/SDLUploadFileOperationSpec.m b/SmartDeviceLinkTests/DevAPISpecs/SDLUploadFileOperationSpec.m
index 570d2bd9e..e18bda43f 100644
--- a/SmartDeviceLinkTests/DevAPISpecs/SDLUploadFileOperationSpec.m
+++ b/SmartDeviceLinkTests/DevAPISpecs/SDLUploadFileOperationSpec.m
@@ -5,6 +5,7 @@
#import "SDLFile.h"
#import "SDLFileWrapper.h"
#import "SDLGlobals.h"
+#import "SDLProtocolHeader.h"
#import "SDLPutFile.h"
#import "SDLPutFileResponse.h"
#import "SDLUploadFileOperation.h"
@@ -14,22 +15,26 @@
@interface UploadFileOperationSpecHelpers : NSObject
-+ (void)testPutFiles:(NSArray<SDLPutFile *> *)putFiles data:(NSData *)testFileData file:(SDLFile *)testFile;
++ (void)testPutFiles:(NSArray<SDLPutFile *> *)putFiles testFile:(SDLFile *)testFile mtuSize:(NSUInteger)mtuSize;
@end
@implementation UploadFileOperationSpecHelpers
-+ (void)testPutFiles:(NSArray<SDLPutFile *> *)putFiles data:(NSData *)testFileData file:(SDLFile *)testFile {
+/// Checks each of the PutFiles generated by the SDLUploadFileOperation to make sure all the properties (i.e. bulk data, offset, file name, etc.) were set correctly.
+/// @param putFiles The PutFiles generated by the SDLUploadFileOperation
+/// @param testFile The file used to generate the PutFiles
+/// @param mtuSize The max allowed size for the packet generated for the PutFile
++ (void)testPutFiles:(NSArray<SDLPutFile *> *)putFiles testFile:(SDLFile *)testFile mtuSize:(NSUInteger)mtuSize {
// Test all packets for offset, length, and data
for (NSUInteger index = 0; index < putFiles.count; index++) {
SDLPutFile *putFile = putFiles[index];
- NSUInteger mtuSize = [[SDLGlobals sharedGlobals] mtuSizeForServiceType:SDLServiceTypeBulkData];
- NSData *testBulkFileData = [testFileData subdataWithRange:NSMakeRange((index * mtuSize), MIN(putFile.length.unsignedIntegerValue, mtuSize))];
+ NSUInteger maxBulkDataSize = [self.class testMaxBulkDataSizeForFile:testFile mtuSize:mtuSize];
+ NSData *testBulkFileData = [testFile.data subdataWithRange:NSMakeRange((index * maxBulkDataSize), MIN(putFile.length.unsignedIntegerValue, maxBulkDataSize))];
unsigned long testBulkFileDataCrc = crc32(0, testBulkFileData.bytes, (uInt)testBulkFileData.length);
- expect(putFile.offset).to(equal(@(index * mtuSize)));
+ expect(putFile.offset).to(equal(@(index * maxBulkDataSize)));
expect(putFile.persistentFile).to(equal(@NO));
expect(putFile.sdlFileName).to(equal(testFile.name));
expect(putFile.bulkData).to(equal(testBulkFileData));
@@ -41,14 +46,39 @@
expect(putFile.length).to(equal(@([testFile fileSize])));
} else if (index == putFiles.count - 1) {
// The last pufile contains the remaining data size
- expect(putFile.length).to(equal(@([testFile fileSize] - (index * mtuSize))));
+ expect(putFile.length).to(equal(@([testFile fileSize] - (index * maxBulkDataSize))));
} else {
// All other putfiles contain the max data size for a putfile packet
- expect(putFile.length).to(equal(@(mtuSize)));
+ expect(putFile.length).to(equal(@(maxBulkDataSize)));
}
}
}
+/// Calculates the number of PutFiles that should be generated by the SDLUploadFileOperation for the file's data.
+/// @param file The file to be uploaded
+/// @param mtuSize The max allowed size of packet generated for the PutFile
+/// @return The number of PutFiles that should be created
++ (NSUInteger)testNumberOfPutFiles:(SDLFile *)file mtuSize:(NSUInteger)mtuSize {
+ NSUInteger maxBulkDataSize = [self.class testMaxBulkDataSizeForFile:file mtuSize:mtuSize];
+ return (((file.fileSize - 1) / maxBulkDataSize) + 1);
+}
+
+/// Calculates the max size of the data that can be set in the bulk data field for a PutFile. The size of the binary header, JSON, and frame header must be taken into account in order to make sure the packet size does not exceed the max MTU size allowed by SDL Core.
+/// @param file The file to be uploaded
+/// @param mtuSize The max allowed size of packet generated for the SDLPutFile
+/// @return The size of the bulk data that can be set in the PutFiles
++ (NSUInteger)testMaxBulkDataSizeForFile:(SDLFile *)file mtuSize:(NSUInteger)mtuSize {
+ NSUInteger frameHeaderSize = [SDLProtocolHeader headerForVersion:(UInt8)[SDLGlobals sharedGlobals].protocolVersion.major].size;
+ NSUInteger binaryHeaderSize = 12;
+
+ SDLPutFile *putFile = [[SDLPutFile alloc] initWithFileName:file.name fileType:file.fileType persistentFile:file.persistent systemFile:NO offset:(UInt32)file.fileSize length:(UInt32)file.fileSize bulkData:file.data];
+ putFile.crc = @(UINT32_MAX);
+ NSData *jsonData = [NSJSONSerialization dataWithJSONObject:[putFile serializeAsDictionary:(Byte)[SDLGlobals sharedGlobals].protocolVersion.major] options:kNilOptions error:nil];
+ NSUInteger maxJSONSize = jsonData.length;
+
+ return mtuSize - (binaryHeaderSize + maxJSONSize + frameHeaderSize);
+}
+
@end
QuickSpecBegin(SDLUploadFileOperationSpec)
@@ -58,7 +88,7 @@ describe(@"Streaming upload of data", ^{
__block NSData *testFileData = nil;
__block SDLFile *testFile = nil;
__block SDLFileWrapper *testFileWrapper = nil;
- __block NSUInteger numberOfPutFiles = 0;
+ __block NSUInteger expectedNumberOfPutFiles = 0;
__block TestConnectionManager *testConnectionManager = nil;
__block SDLUploadFileOperation *testOperation = nil;
@@ -67,14 +97,18 @@ describe(@"Streaming upload of data", ^{
__block NSUInteger bytesAvailableResult = NO;
__block NSError *errorResult = nil;
+ __block int testMTUSize = 1024;
+
beforeEach(^{
[SDLGlobals sharedGlobals].maxHeadUnitProtocolVersion = [SDLVersion versionWithString:@"2.0.0"];
+ // Since SDLGlobals is a singleton, the MTU size can be set by other test classes (i.e. the value returned can vary based on when these tests are run in relation to all the tests). Set the MTU size for this test suite to ensure the retrieved MTU size is the same every time this set of tests is run.
+ [[SDLGlobals sharedGlobals] setDynamicMTUSize:(NSUInteger)testMTUSize forServiceType:SDLServiceTypeRPC];
testFileName = nil;
testFileData = nil;
testFile = nil;
testFileWrapper = nil;
- numberOfPutFiles = 0;
+ expectedNumberOfPutFiles = 0;
testOperation = nil;
testConnectionManager = [[TestConnectionManager alloc] init];
@@ -85,12 +119,18 @@ describe(@"Streaming upload of data", ^{
});
describe(@"When uploading data", ^{
+ __block NSInteger spaceLeft = 0;
+ __block SDLPutFileResponse *successResponse = nil;
+
+ beforeEach(^{
+ spaceLeft = 11212512;
+ });
+
context(@"data should be split into smaller packets if too large to send all at once", ^{
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"];
- __block NSInteger spaceLeft = 11212512;
testFileWrapper = [SDLFileWrapper wrapperWithFile:testFile completionHandler:^(BOOL success, NSUInteger bytesAvailable, NSError * _Nullable error) {
expect(success).to(beTrue());
@@ -98,24 +138,22 @@ describe(@"Streaming upload of data", ^{
expect(error).to(beNil());
}];
- numberOfPutFiles = ((([testFile fileSize] - 1) / [[SDLGlobals sharedGlobals] mtuSizeForServiceType:SDLServiceTypeBulkData]) + 1);
+ expectedNumberOfPutFiles = [UploadFileOperationSpecHelpers testNumberOfPutFiles:testFile mtuSize:testMTUSize];
testOperation = [[SDLUploadFileOperation alloc] initWithFile:testFileWrapper connectionManager:testConnectionManager];
[testOperation start];
- NSArray<SDLPutFile *> *putFiles = testConnectionManager.receivedRequests;
- expect(@(putFiles.count)).to(equal(@(numberOfPutFiles)));
- [UploadFileOperationSpecHelpers testPutFiles:putFiles data:testFileData file:testFile];
-
- __block SDLPutFileResponse *goodResponse = nil;
-
- // We must do some cleanup here otherwise the unit test cases will crash
- 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];
+ NSArray<SDLPutFile *> *testPutFiles = testConnectionManager.receivedRequests;
+ expect(@(testPutFiles.count)).to(equal(@(expectedNumberOfPutFiles)));
+
+ [UploadFileOperationSpecHelpers testPutFiles:testPutFiles testFile:testFile mtuSize:testMTUSize];
+
+ // Respond to each PutFile request
+ for (int i = 0; i < expectedNumberOfPutFiles; i++) {
+ successResponse = [[SDLPutFileResponse alloc] init];
+ successResponse.success = @YES;
+ successResponse.spaceAvailable = @(spaceLeft -= 1024);
+ [testConnectionManager respondToRequestWithResponse:successResponse requestNumber:i error:nil];
}
expect(testOperation.finished).toEventually(beTrue());
@@ -127,7 +165,6 @@ describe(@"Streaming upload of data", ^{
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"];
- __block NSInteger spaceLeft = 11212512;
testFileWrapper = [SDLFileWrapper wrapperWithFile:testFile completionHandler:^(BOOL success, NSUInteger bytesAvailable, NSError * _Nullable error) {
expect(success).to(beTrue());
@@ -135,24 +172,21 @@ describe(@"Streaming upload of data", ^{
expect(error).to(beNil());
}];
- numberOfPutFiles = ((([testFile fileSize] - 1) / [[SDLGlobals sharedGlobals] mtuSizeForServiceType:SDLServiceTypeBulkData]) + 1);
+ expectedNumberOfPutFiles = [UploadFileOperationSpecHelpers testNumberOfPutFiles:testFile mtuSize:testMTUSize];
testOperation = [[SDLUploadFileOperation alloc] initWithFile:testFileWrapper connectionManager:testConnectionManager];
[testOperation start];
NSArray<SDLPutFile *> *putFiles = testConnectionManager.receivedRequests;
- expect(@(putFiles.count)).to(equal(@(numberOfPutFiles)));
- [UploadFileOperationSpecHelpers testPutFiles:putFiles data:testFileData file:testFile];
-
- __block SDLPutFileResponse *goodResponse = nil;
-
- // We must do some cleanup here otherwise the unit test cases will crash
- 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(@(putFiles.count)).to(equal(@(expectedNumberOfPutFiles)));
+ [UploadFileOperationSpecHelpers testPutFiles:putFiles testFile:testFile mtuSize:testMTUSize];
+
+ // Respond to each PutFile request
+ for (int i = 0; i < expectedNumberOfPutFiles; i++) {
+ successResponse = [[SDLPutFileResponse alloc] init];
+ successResponse.success = @YES;
+ successResponse.spaceAvailable = @(spaceLeft -= 1024);
+ [testConnectionManager respondToRequestWithResponse:successResponse requestNumber:i error:nil];
}
expect(testOperation.finished).toEventually(beTrue());
@@ -160,13 +194,11 @@ describe(@"Streaming upload of data", ^{
});
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"];
+ testFileName = @"testFileJSON";
+ NSString *textFilePath = [[NSBundle bundleForClass:[self class]] pathForResource:testFileName ofType:@"json"];
NSURL *textFileURL = [[NSURL alloc] initFileURLWithPath:textFilePath];
- testFile = [SDLFile fileAtFileURL:textFileURL name:fileName];
+ testFile = [SDLFile fileAtFileURL:textFileURL name:testFileName];
testFileData = [[NSData alloc] initWithContentsOfURL:textFileURL];
- __block NSInteger spaceLeft = 11212512;
testFileWrapper = [SDLFileWrapper wrapperWithFile:testFile completionHandler:^(BOOL success, NSUInteger bytesAvailable, NSError * _Nullable error) {
expect(success).to(beTrue());
@@ -174,24 +206,21 @@ describe(@"Streaming upload of data", ^{
expect(error).to(beNil());
}];
- numberOfPutFiles = ((([testFile fileSize] - 1) / [[SDLGlobals sharedGlobals] mtuSizeForServiceType:SDLServiceTypeBulkData]) + 1);
+ expectedNumberOfPutFiles = [UploadFileOperationSpecHelpers testNumberOfPutFiles:testFile mtuSize:testMTUSize];
testOperation = [[SDLUploadFileOperation alloc] initWithFile:testFileWrapper connectionManager:testConnectionManager];
[testOperation start];
NSArray<SDLPutFile *> *putFiles = testConnectionManager.receivedRequests;
- expect(@(putFiles.count)).to(equal(@(numberOfPutFiles)));
- [UploadFileOperationSpecHelpers testPutFiles:putFiles data:testFileData file:testFile];
-
- __block SDLPutFileResponse *goodResponse = nil;
-
- // We must do some cleanup here otherwise the unit test cases will crash
- 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(@(putFiles.count)).to(equal(@(expectedNumberOfPutFiles)));
+ [UploadFileOperationSpecHelpers testPutFiles:putFiles testFile:testFile mtuSize:testMTUSize];
+
+ // Respond to each PutFile request
+ for (int i = 0; i < expectedNumberOfPutFiles; i++) {
+ successResponse = [[SDLPutFileResponse alloc] init];
+ successResponse.success = @YES;
+ successResponse.spaceAvailable = @(spaceLeft -= 1024);
+ [testConnectionManager respondToRequestWithResponse:successResponse requestNumber:i error:nil];
}
expect(testOperation.finished).toEventually(beTrue());
@@ -204,9 +233,6 @@ describe(@"Streaming upload of data", ^{
NSString *imageFilePath = [[NSBundle bundleForClass:[self class]] pathForResource:fileName ofType:@"png"];
NSURL *imageFileURL = [[NSURL alloc] initFileURLWithPath:imageFilePath];
testFile = [SDLFile fileAtFileURL:imageFileURL name:fileName];
- __block NSInteger spaceLeft = 11212512;
-
- // For testing: get data to check if data chunks are being created correctly
testFileData = [[NSData alloc] initWithContentsOfURL:imageFileURL];
testFileWrapper = [SDLFileWrapper wrapperWithFile:testFile completionHandler:^(BOOL success, NSUInteger bytesAvailable, NSError * _Nullable error) {
@@ -215,24 +241,21 @@ describe(@"Streaming upload of data", ^{
expect(error).to(beNil());
}];
- numberOfPutFiles = ((([testFile fileSize] - 1) / [[SDLGlobals sharedGlobals] mtuSizeForServiceType:SDLServiceTypeBulkData]) + 1);
+ expectedNumberOfPutFiles = [UploadFileOperationSpecHelpers testNumberOfPutFiles:testFile mtuSize:testMTUSize];
testOperation = [[SDLUploadFileOperation alloc] initWithFile:testFileWrapper connectionManager:testConnectionManager];
[testOperation start];
NSArray<SDLPutFile *> *putFiles = testConnectionManager.receivedRequests;
- expect(@(putFiles.count)).to(equal(@(numberOfPutFiles)));
- [UploadFileOperationSpecHelpers testPutFiles:putFiles data:testFileData file:testFile];
-
- __block SDLPutFileResponse *goodResponse = nil;
-
- // We must do some cleanup here otherwise the unit test cases will crash
- 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(@(putFiles.count)).to(equal(@(expectedNumberOfPutFiles)));
+ [UploadFileOperationSpecHelpers testPutFiles:putFiles testFile:testFile mtuSize:testMTUSize];
+
+ // Respond to each PutFile request
+ for (int i = 0; i < expectedNumberOfPutFiles; i++) {
+ successResponse = [[SDLPutFileResponse alloc] init];
+ successResponse.success = @YES;
+ successResponse.spaceAvailable = @(spaceLeft -= 1024);
+ [testConnectionManager respondToRequestWithResponse:successResponse requestNumber:i error:nil];
}
expect(testOperation.finished).toEventually(beTrue());
@@ -253,9 +276,7 @@ describe(@"Streaming upload of data", ^{
errorResult = error;
}];
- NSUInteger mtuSize = [[SDLGlobals sharedGlobals] mtuSizeForServiceType:SDLServiceTypeBulkData];
-
- numberOfPutFiles = ((([testFile fileSize] - 1) / mtuSize) + 1);
+ expectedNumberOfPutFiles = [UploadFileOperationSpecHelpers testNumberOfPutFiles:testFile mtuSize:testMTUSize];
testConnectionManager = [[TestConnectionManager alloc] init];
testOperation = [[SDLUploadFileOperation alloc] initWithFile:testFileWrapper connectionManager:testConnectionManager];
@@ -263,21 +284,19 @@ describe(@"Streaming upload of data", ^{
});
context(@"If data was sent successfully", ^{
- __block SDLPutFileResponse *goodResponse = nil;
__block NSInteger spaceLeft = 0;
+ __block SDLPutFileResponse *successResponse = nil;
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];
+ for (int i = 0; i < expectedNumberOfPutFiles; i++) {
+ successResponse = [[SDLPutFileResponse alloc] init];
+ successResponse.success = @YES;
+ successResponse.spaceAvailable = @(spaceLeft -= 1024);
+ [testConnectionManager respondToRequestWithResponse:successResponse requestNumber:i error:nil];
}
expect(successResult).toEventually(beTrue());
@@ -293,21 +312,19 @@ describe(@"Streaming upload of data", ^{
__block SDLPutFileResponse *response = nil;
__block NSString *responseErrorDescription = nil;
__block NSString *responseErrorReason = nil;
+ __block NSError *error = 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;
+ for (int i = 0; i < expectedNumberOfPutFiles; i++) {
response = [[SDLPutFileResponse alloc] init];
- response.spaceAvailable = @(spaceLeft);
- NSError *error = nil;
+ response.spaceAvailable = @(spaceLeft -= 1024);
if (i == 0) {
// Only the first packet is sent unsuccessfully
@@ -317,6 +334,7 @@ describe(@"Streaming upload of data", ^{
error = [NSError sdl_lifecycle_unknownRemoteErrorWithDescription:responseErrorDescription andReason:responseErrorReason];
} else {
response.success = @YES;
+ error = nil;
}
[testConnectionManager respondToRequestWithResponse:response requestNumber:i error:error];
@@ -328,13 +346,11 @@ describe(@"Streaming upload of data", ^{
});
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;
+ for (int i = 0; i < expectedNumberOfPutFiles; i++) {
response = [[SDLPutFileResponse alloc] init];
- response.spaceAvailable = @(spaceLeft);
- NSError *error = nil;
+ response.spaceAvailable = @(spaceLeft -= 1024);
- if (i == (numberOfPutFiles - 1)) {
+ if (i == (expectedNumberOfPutFiles - 1)) {
// Only the last packet is sent unsuccessfully
response.success = @NO;
responseErrorDescription = @"some description";
@@ -342,6 +358,7 @@ describe(@"Streaming upload of data", ^{
error = [NSError sdl_lifecycle_unknownRemoteErrorWithDescription:responseErrorDescription andReason:responseErrorReason];
} else {
response.success = @YES;
+ error = nil;
}
[testConnectionManager respondToRequestWithResponse:response requestNumber:i error:error];
@@ -353,11 +370,10 @@ describe(@"Streaming upload of data", ^{
});
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;
+ for (int i = 0; i < expectedNumberOfPutFiles; i++) {
response = [[SDLPutFileResponse alloc] init];
response.success = @NO;
- response.spaceAvailable = @(spaceLeft);
+ response.spaceAvailable = @(spaceLeft -= 1024);
responseErrorDescription = @"some description";
responseErrorReason = @"some reason";
diff --git a/SmartDeviceLinkTests/DevAPISpecs/SDLVideoStreamingRangeSpec.m b/SmartDeviceLinkTests/DevAPISpecs/SDLVideoStreamingRangeSpec.m
new file mode 100644
index 000000000..d40ed3735
--- /dev/null
+++ b/SmartDeviceLinkTests/DevAPISpecs/SDLVideoStreamingRangeSpec.m
@@ -0,0 +1,221 @@
+//
+// SDLVideoStreamingRangeSpec.m
+// SmartDeviceLinkTests
+//
+// Created by Joel Fischer on 6/21/21.
+// Copyright © 2021 smartdevicelink. All rights reserved.
+//
+
+#import <Quick/Quick.h>
+#import <Nimble/Nimble.h>
+
+#import <SmartDeviceLink/SmartDeviceLink.h>
+
+QuickSpecBegin(SDLVideoStreamingRangeSpec)
+
+describe(@"video streaming range", ^{
+ __block SDLVideoStreamingRange *testRange = nil;
+ SDLImageResolution *disabledResolution = [[SDLImageResolution alloc] initWithWidth:0 height:0];
+ SDLImageResolution *lowResolution = [[SDLImageResolution alloc] initWithWidth:1 height:1];
+ SDLImageResolution *highResolution = [[SDLImageResolution alloc] initWithWidth:999 height:999];
+
+ float defaultMinimumAspectRatio = 1.0;
+ float defaultMaximumAspectRatio = 9999.0;
+ float defaultMinimumDiagonal = 0.0;
+
+ float testMinimumAspectRatio = 4.0;
+ float testMaximumAspectRatio = 12.0;
+ float testMinimumDiagonal = 6.0;
+
+ beforeEach(^{
+ testRange = [[SDLVideoStreamingRange alloc] initWithMinimumResolution:lowResolution maximumResolution:highResolution];;
+ });
+
+ context(@"initialized with initWithMinimumResolution:maximumResolution", ^{
+ it(@"should set all parameters correctly", ^{
+ expect(testRange.minimumResolution).to(equal(lowResolution));
+ expect(testRange.maximumResolution).to(equal(highResolution));
+ expect(testRange.minimumAspectRatio).to(equal(1.0));
+ expect(testRange.maximumAspectRatio).to(equal(9999.0));
+ expect(testRange.minimumDiagonal).to(equal(0.0));
+ });
+ });
+
+ context(@"initialized with initWithMinimumResolution:maximumResolution:minimumAspectRatio:maximumAspectRatio:minimumDiagonal:", ^{
+ beforeEach(^{
+ testRange = [[SDLVideoStreamingRange alloc] initWithMinimumResolution:lowResolution maximumResolution:highResolution minimumAspectRatio:testMinimumAspectRatio maximumAspectRatio:testMaximumAspectRatio minimumDiagonal:testMinimumDiagonal];
+ });
+
+ it(@"should set all parameters correctly", ^{
+ expect(testRange.minimumResolution).to(equal(lowResolution));
+ expect(testRange.maximumResolution).to(equal(highResolution));
+ expect(testRange.minimumAspectRatio).to(equal(testMinimumAspectRatio));
+ expect(testRange.maximumAspectRatio).to(equal(testMaximumAspectRatio));
+ expect(testRange.minimumDiagonal).to(equal(testMinimumDiagonal));
+ });
+ });
+
+ context(@"initialized with disabled", ^{
+ beforeEach(^{
+ testRange = [SDLVideoStreamingRange disabled];
+ });
+
+ it(@"should set all parameters correctly", ^{
+ expect(testRange.minimumResolution).to(equal(disabledResolution));
+ expect(testRange.maximumResolution).to(equal(disabledResolution));
+ expect(testRange.minimumAspectRatio).to(equal(defaultMinimumAspectRatio));
+ expect(testRange.maximumAspectRatio).to(equal(defaultMaximumAspectRatio));
+ expect(testRange.minimumDiagonal).to(equal(defaultMinimumDiagonal));
+ });
+ });
+
+ describe(@"setting float parameters", ^{
+ describe(@"minimum aspect ratio", ^{
+ context(@"below the minimum", ^{
+ it(@"should be set to the minimum", ^{
+ testRange.minimumAspectRatio = -2.0;
+ expect(testRange.minimumAspectRatio).to(equal(defaultMinimumAspectRatio));
+ });
+ });
+
+ context(@"above the minimum", ^{
+ it(@"should set the value", ^{
+ testRange.minimumAspectRatio = testMinimumAspectRatio;
+ expect(testRange.minimumAspectRatio).to(equal(testMinimumAspectRatio));
+ });
+ });
+ });
+
+ describe(@"maximum aspect ratio", ^{
+ context(@"below the minimum", ^{
+ it(@"should be set to the minimum", ^{
+ testRange.maximumAspectRatio = -2.0;
+ expect(testRange.maximumAspectRatio).to(equal(defaultMinimumAspectRatio));
+ });
+ });
+
+ context(@"above the minimum", ^{
+ it(@"should set the value", ^{
+ testRange.maximumAspectRatio = testMaximumAspectRatio;
+ expect(testRange.maximumAspectRatio).to(equal(testMaximumAspectRatio));
+ });
+ });
+ });
+
+ describe(@"minimum diagonal", ^{
+ context(@"below the minimum", ^{
+ it(@"should be set to the minimum", ^{
+ testRange.minimumDiagonal = -2.0;
+ expect(testRange.minimumDiagonal).to(equal(defaultMinimumDiagonal));
+ });
+ });
+
+ context(@"above the minimum", ^{
+ it(@"should set the value", ^{
+ testRange.minimumDiagonal = testMinimumDiagonal;
+ expect(testRange.minimumDiagonal).to(equal(testMinimumDiagonal));
+ });
+ });
+ });
+ });
+
+ describe(@"checking if the resolution is in a given range", ^{
+ beforeEach(^{
+ testRange = [[SDLVideoStreamingRange alloc] initWithMinimumResolution:lowResolution maximumResolution:highResolution];
+ });
+
+ context(@"when there is no minimum or maximum resolution", ^{
+ beforeEach(^{
+ testRange = [[SDLVideoStreamingRange alloc] initWithMinimumResolution:nil maximumResolution:nil];
+ });
+
+ it(@"should return NO", ^{
+ expect([testRange isImageResolutionInRange:[[SDLImageResolution alloc] initWithWidth:2 height:2]]).to(beFalse());
+ });
+ });
+
+ context(@"when there is no maximum resolution", ^{
+ beforeEach(^{
+ testRange = [[SDLVideoStreamingRange alloc] initWithMinimumResolution:lowResolution maximumResolution:nil];
+ });
+
+ it(@"should return YES", ^{
+ expect([testRange isImageResolutionInRange:[[SDLImageResolution alloc] initWithWidth:2 height:2]]).to(beTrue());
+ });
+ });
+
+ context(@"when there is no minimum resolution", ^{
+ beforeEach(^{
+ testRange = [[SDLVideoStreamingRange alloc] initWithMinimumResolution:nil maximumResolution:highResolution];
+ });
+
+ it(@"should return YES", ^{
+ expect([testRange isImageResolutionInRange:[[SDLImageResolution alloc] initWithWidth:2 height:2]]).to(beTrue());
+ });
+ });
+
+ context(@"when the resolution is below the range", ^{
+ it(@"should return NO", ^{
+ expect([testRange isImageResolutionInRange:[[SDLImageResolution alloc] initWithWidth:0 height:0]]).to(beFalse());
+ });
+ });
+
+ context(@"when the resolution is above the range", ^{
+ it(@"should return NO", ^{
+ expect([testRange isImageResolutionInRange:[[SDLImageResolution alloc] initWithWidth:34463 height:34463]]).to(beFalse());
+ });
+ });
+
+ context(@"when the resolution is in the range", ^{
+ it(@"should return YES", ^{
+ expect([testRange isImageResolutionInRange:[[SDLImageResolution alloc] initWithWidth:2 height:2]]).to(beTrue());
+ });
+ });
+ });
+
+ describe(@"checking if the aspect ratio is in a given range", ^{
+ beforeEach(^{
+ testRange = [[SDLVideoStreamingRange alloc] initWithMinimumResolution:nil maximumResolution:nil minimumAspectRatio:testMinimumAspectRatio maximumAspectRatio:testMaximumAspectRatio minimumDiagonal:1.0];
+ });
+
+ context(@"when there is no maximum aspect ratio", ^{
+ beforeEach(^{
+ testRange = [[SDLVideoStreamingRange alloc] initWithMinimumResolution:nil maximumResolution:nil minimumAspectRatio:testMinimumAspectRatio maximumAspectRatio:0.0 minimumDiagonal:1.0];
+ });
+
+ it(@"should return NO", ^{
+ expect([testRange isAspectRatioInRange:10.0]).to(beTrue());
+ });
+ });
+
+ context(@"when there is no minimum aspect ratio", ^{
+ beforeEach(^{
+ testRange = [[SDLVideoStreamingRange alloc] initWithMinimumResolution:nil maximumResolution:nil minimumAspectRatio:0.0 maximumAspectRatio:testMaximumAspectRatio minimumDiagonal:1.0];
+ });
+
+ it(@"should return NO", ^{
+ expect([testRange isAspectRatioInRange:10.0]).to(beTrue());
+ });
+ });
+
+ context(@"when the aspect ratio is below the range", ^{
+ it(@"should return NO", ^{
+ expect([testRange isAspectRatioInRange:2.0]).to(beFalse());
+ });
+ });
+
+ context(@"when the aspect ratio is above the range", ^{
+ it(@"should return NO", ^{
+ expect([testRange isAspectRatioInRange:99.0]).to(beFalse());
+ });
+ });
+
+ context(@"when the aspect ratio is in the range", ^{
+ it(@"should return NO", ^{
+ expect([testRange isAspectRatioInRange:10.0]).to(beTrue());
+ });
+ });
+ });
+});
+
+QuickSpecEnd
diff --git a/SmartDeviceLinkTests/DevAPISpecs/SDLVoiceCommandManagerSpec.m b/SmartDeviceLinkTests/DevAPISpecs/SDLVoiceCommandManagerSpec.m
index 58d5ff591..612d89aa2 100644
--- a/SmartDeviceLinkTests/DevAPISpecs/SDLVoiceCommandManagerSpec.m
+++ b/SmartDeviceLinkTests/DevAPISpecs/SDLVoiceCommandManagerSpec.m
@@ -1,6 +1,5 @@
#import <Quick/Quick.h>
#import <Nimble/Nimble.h>
-#import <OCMock/OCMock.h>
#import "SDLAddCommand.h"
#import "SDLAddCommandResponse.h"
@@ -37,6 +36,8 @@
@property (assign, nonatomic) UInt32 lastVoiceCommandId;
@property (copy, nonatomic) NSArray<SDLVoiceCommand *> *currentVoiceCommands;
++ (BOOL)sdl_arePendingVoiceCommandsUnique:(NSArray<SDLVoiceCommand *> *)voiceCommands;
+
@end
UInt32 const VoiceCommandIdMin = 1900000000;
@@ -49,6 +50,11 @@ describe(@"voice command manager", ^{
__block SDLVoiceCommand *testVoiceCommand = [[SDLVoiceCommand alloc] initWithVoiceCommands:@[@"Test 1"] handler:^{}];
__block SDLVoiceCommand *testVoiceCommand2 = [[SDLVoiceCommand alloc] initWithVoiceCommands:@[@"Test 2"] handler:^{}];
+ __block SDLVoiceCommand *testVoiceCommand3 = [[SDLVoiceCommand alloc] initWithVoiceCommands:@[@"Test 3", @" ", @"Test 4", @"\t"] handler:^{}];
+ __block SDLVoiceCommand *testVoiceCommand4 = [[SDLVoiceCommand alloc] initWithVoiceCommands:@[@"\t"] handler:^{}];
+ __block SDLVoiceCommand *testVoiceCommand5 = [[SDLVoiceCommand alloc] initWithVoiceCommands:@[@""] handler:^{}];
+ __block SDLVoiceCommand *testVoiceCommand6 = [[SDLVoiceCommand alloc] init];
+ __block SDLVoiceCommand *testVoiceCommand7 = [[SDLVoiceCommand alloc] initWithVoiceCommands:@[@"Test 1", @"Test 2"] handler:^{}];
__block SDLOnHMIStatus *newHMIStatus = [[SDLOnHMIStatus alloc] init];
__block NSArray<SDLVoiceCommand *> *testVCArray = nil;
@@ -98,60 +104,105 @@ describe(@"voice command manager", ^{
});
});
- // updating voice commands
- describe(@"when voice commands are set", ^{
+ // when the hmi is ready
+ describe(@"when the hmi is ready", ^{
beforeEach(^{
newHMIStatus.hmiLevel = SDLHMILevelFull;
newHMIStatus.windowID = @(SDLPredefinedWindowsDefaultWindow);
SDLRPCNotificationNotification *notification = [[SDLRPCNotificationNotification alloc] initWithName:SDLDidChangeHMIStatusNotification object:nil rpcNotification:newHMIStatus];
[[NSNotificationCenter defaultCenter] postNotification:notification];
-
- testManager.voiceCommands = testVCArray;
});
- // should properly update a command
- it(@"should properly update a command", ^{
- expect(testManager.voiceCommands.firstObject.commandId).to(equal(VoiceCommandIdMin));
+ // should update the transactionQueue's suspension to false
+ it(@"should update the transactionQueue's suspension to false", ^{
expect(testManager.transactionQueue.isSuspended).to(beFalse());
- expect(testManager.transactionQueue.operations).to(haveCount(1));
});
- // when new voice commands is identical to the existing ones
- describe(@"when new voice commands is identical to the existing ones", ^{
+ // when setting voiceCommands
+ describe(@"when setting voiceCommands", ^{
beforeEach(^{
+ testManager.transactionQueue.suspended = YES;
testManager.voiceCommands = testVCArray;
});
- // should only have one operation
- it(@"should only have one operation", ^{
+ // should properly update a command
+ it(@"should properly update a command", ^{
+ expect(testManager.voiceCommands.firstObject.commandId).to(equal(VoiceCommandIdMin));
expect(testManager.transactionQueue.operations).to(haveCount(1));
+ expect(testManager.transactionQueue.operations.firstObject.isExecuting).to(beFalse());
});
- });
- // when new voice commands are set
- describe(@"when new voice commands are set", ^{
- beforeEach(^{
- testManager.voiceCommands = @[testVoiceCommand2];
- });
+ // when new voice commands are identical to the existing ones
+ describe(@"when new voice commands are identical to the existing ones", ^{
+ beforeEach(^{
+ testManager.voiceCommands = testVCArray;
+ });
- // should queue another operation
- it(@"should queue another operation", ^{
- expect(testManager.transactionQueue.operations).to(haveCount(2));
+ // should only have one operation
+ it(@"should only have one operation", ^{
+ expect(testManager.transactionQueue.operations).to(haveCount(1));
+ });
});
- // when the first operation finishes and updates the current voice commands
- describe(@"when the first operation finishes and updates the current voice commands", ^{
+ // when new voice commands are different from the existing ones
+ describe(@"when new voice commands are different from the existing ones", ^{
beforeEach(^{
- SDLVoiceCommandUpdateOperation *firstOp = testManager.transactionQueue.operations[0];
- firstOp.currentVoiceCommands = [@[testVoiceCommand2] mutableCopy];
- [firstOp finishOperation];
+ testManager.voiceCommands = @[testVoiceCommand2];
+ });
+
+ it(@"should queue another operation", ^{
+ expect(testManager.transactionQueue.operations).to(haveCount(2));
+ });
+
+ // when the first operation finishes and updates the current voice commands
+ describe(@"when the first operation finishes and updates the current voice commands", ^{
+ beforeEach(^{
+ testManager.transactionQueue.suspended = NO;
+
+ SDLVoiceCommandUpdateOperation *firstOp = testManager.transactionQueue.operations[0];
+ firstOp.currentVoiceCommands = [@[testVoiceCommand2] mutableCopy];
+ [firstOp finishOperation];
+ });
+
+ it(@"should update the second operation", ^{
+ expect(((SDLVoiceCommandUpdateOperation *)testManager.transactionQueue.operations.firstObject).oldVoiceCommands.firstObject).to(equal(testVoiceCommand2));
+ });
+ });
+ });
+
+ // if any of the voice commands contains an empty string
+ context(@"if any of the voice commands contains an empty string", ^{
+ // should remove the empty strings and queue another operation
+ it(@"should remove the empty strings and queue another operation", ^{
+ testManager.voiceCommands = @[testVoiceCommand2, testVoiceCommand3, testVoiceCommand4, testVoiceCommand5, testVoiceCommand6];
+ expect(testManager.transactionQueue.operations).to(haveCount(2));
+ expect(testManager.voiceCommands).to(haveCount(2));
+ expect(testManager.voiceCommands[0].voiceCommands).to(haveCount(1));
+ expect(testManager.voiceCommands[0].voiceCommands).to(equal(@[@"Test 2"]));
+ expect(testManager.voiceCommands[1].voiceCommands).to(haveCount(2));
+ expect(testManager.voiceCommands[1].voiceCommands).to(equal(@[@"Test 3", @"Test 4"]));
});
- it(@"should update the second operation", ^{
- SDLVoiceCommandUpdateOperation *secondOp = testManager.transactionQueue.operations[0];
+ // should not queue another operation if all the voice command strings are empty strings
+ it(@"should not queue another operation if all the voice command strings are empty strings", ^{
+ testManager.voiceCommands = @[testVoiceCommand4, testVoiceCommand5];
+ expect(testManager.transactionQueue.operations).to(haveCount(1));
+ expect(testManager.voiceCommands).to(haveCount(1));
+ expect(testManager.voiceCommands.firstObject.voiceCommands).to(haveCount(1));
+ expect(testManager.voiceCommands.firstObject.voiceCommands).to(equal(@[@"Test 1"]));
+ });
+ });
+
+ // updating voice commands with duplicate string in different voice commands
+ describe(@"when new voice commands are set and have duplicate strings in different voice commands", ^{
+ beforeEach(^{
+ testManager.voiceCommands = @[testVoiceCommand2, testVoiceCommand7];
+ });
- expect(secondOp.oldVoiceCommands.firstObject).toEventually(equal(testVoiceCommand2));
+ it(@"should only have one operation", ^{
+ expect(testManager.transactionQueue.operations).to(haveCount(1));
+ expect([testManager.class sdl_arePendingVoiceCommandsUnique:@[testVoiceCommand2, testVoiceCommand7]]).to(equal(NO));
});
});
});
@@ -163,6 +214,7 @@ describe(@"voice command manager", ^{
[testManager stop];
});
+ // should reset correctly
it(@"should reset correctly", ^{
expect(testManager.connectionManager).to(equal(mockConnectionManager));
expect(testManager.voiceCommands).to(beEmpty());
diff --git a/SmartDeviceLinkTests/DevAPISpecs/SDLVoiceCommandSpec.m b/SmartDeviceLinkTests/DevAPISpecs/SDLVoiceCommandSpec.m
index 8e8a2e7c3..0763f6bc4 100644
--- a/SmartDeviceLinkTests/DevAPISpecs/SDLVoiceCommandSpec.m
+++ b/SmartDeviceLinkTests/DevAPISpecs/SDLVoiceCommandSpec.m
@@ -7,12 +7,15 @@ QuickSpecBegin(SDLVoiceCommandSpec)
describe(@"a voice command", ^{
__block SDLVoiceCommand *testCommand = nil;
+ __block SDLVoiceCommand *testCommand2 = nil;
describe(@"initializing", ^{
__block NSArray<NSString *> *someVoiceCommands = nil;
+ __block NSArray<NSString *> *someVoiceCommands2 = nil;
beforeEach(^{
someVoiceCommands = @[@"some command"];
+ someVoiceCommands2 = @[@"Test 1", @"Test 1", @"Test 1"];
});
it(@"should initialize properly", ^{
@@ -20,6 +23,13 @@ describe(@"a voice command", ^{
expect(testCommand.voiceCommands).to(equal(someVoiceCommands));
});
+
+ it(@"should initialize properly if it have multiple of the same command string", ^{
+ testCommand2 = [[SDLVoiceCommand alloc] initWithVoiceCommands:someVoiceCommands2 handler:^{}];
+
+ expect(testCommand2.voiceCommands).toNot(equal(someVoiceCommands2));
+ expect(testCommand2.voiceCommands).to(haveCount(1));
+ });
});
});
diff --git a/SmartDeviceLinkTests/DevAPISpecs/SDLVoiceCommandUpdateOperationSpec.m b/SmartDeviceLinkTests/DevAPISpecs/SDLVoiceCommandUpdateOperationSpec.m
index ca8c9cfbd..2eb5f9eb3 100644
--- a/SmartDeviceLinkTests/DevAPISpecs/SDLVoiceCommandUpdateOperationSpec.m
+++ b/SmartDeviceLinkTests/DevAPISpecs/SDLVoiceCommandUpdateOperationSpec.m
@@ -19,6 +19,12 @@
@end
+@interface SDLVoiceCommandUpdateOperation()
+
+@property (strong, nonatomic) NSMutableArray<SDLVoiceCommand *> *currentVoiceCommands;
+
+@end
+
QuickSpecBegin(SDLVoiceCommandUpdateOperationSpec)
__block SDLDeleteCommandResponse *successDelete = nil;
@@ -28,6 +34,7 @@ __block SDLAddCommandResponse *failAdd = nil;
__block SDLVoiceCommand *newVoiceCommand1 = [[SDLVoiceCommand alloc] initWithVoiceCommands:@[@"NewVC1"] handler:^{}];
__block SDLVoiceCommand *newVoiceCommand2 = [[SDLVoiceCommand alloc] initWithVoiceCommands:@[@"NewVC2"] handler:^{}];
+__block SDLVoiceCommand *oldVoiceCommand1Dupe = [[SDLVoiceCommand alloc] initWithVoiceCommands:@[@"OldVC1"] handler:^{}];
__block SDLVoiceCommand *oldVoiceCommand1 = [[SDLVoiceCommand alloc] initWithVoiceCommands:@[@"OldVC1"] handler:^{}];
__block SDLVoiceCommand *oldVoiceCommand2 = [[SDLVoiceCommand alloc] initWithVoiceCommands:@[@"OldVC2"] handler:^{}];
@@ -83,6 +90,20 @@ describe(@"a voice command operation", ^{
expect(testOp.oldVoiceCommands).to(equal(@[oldVoiceCommand1, oldVoiceCommand2]));
});
+ // when updating oldVoiceCommands
+ describe(@"when updating oldVoiceCommands", ^{
+ beforeEach(^{
+ testOp = [[SDLVoiceCommandUpdateOperation alloc] init];
+ testOp.oldVoiceCommands = @[newVoiceCommand1, newVoiceCommand2];
+ });
+
+ // should update both oldVoiceCommands and currentVoiceCommands
+ it(@"should update both oldVoiceCommands and currentVoiceCommands", ^{
+ expect(testOp.oldVoiceCommands).to(equal(@[newVoiceCommand1, newVoiceCommand2]));
+ expect(testOp.currentVoiceCommands).to(equal(testOp.oldVoiceCommands));
+ });
+ });
+
// starting the operation
describe(@"starting the operation", ^{
@@ -166,6 +187,129 @@ describe(@"a voice command operation", ^{
});
});
+ // if it has pending voice commands identical to old voice commands
+ context(@"if it has pending voice commands identical to old voice commands", ^{
+ beforeEach(^{
+ testOp = [[SDLVoiceCommandUpdateOperation alloc] initWithConnectionManager:testConnectionManager pendingVoiceCommands:@[oldVoiceCommand1, oldVoiceCommand2] oldVoiceCommands:@[oldVoiceCommand1, oldVoiceCommand2] updateCompletionHandler:^(NSArray<SDLVoiceCommand *> * _Nonnull newCurrentVoiceCommands, NSError * _Nullable error) {
+ callbackCurrentVoiceCommands = newCurrentVoiceCommands;
+ callbackError = error;
+ }];
+ [testOp start];
+ });
+
+ it(@"should not delete or upload the voiceCommands", ^{
+ expect(testConnectionManager.receivedRequests).to(haveCount(0));
+ expect(callbackCurrentVoiceCommands).to(haveCount(2));
+ expect(callbackError).to(beNil());
+ });
+ });
+
+ // going from voice commands [AB] to [A]
+ context(@"going from voice commands [AB] to [A]", ^{
+ beforeEach(^{
+ testOp = [[SDLVoiceCommandUpdateOperation alloc] initWithConnectionManager:testConnectionManager pendingVoiceCommands:@[newVoiceCommand1] oldVoiceCommands:@[newVoiceCommand1, newVoiceCommand2] updateCompletionHandler:^(NSArray<SDLVoiceCommand *> * _Nonnull newCurrentVoiceCommands, NSError * _Nullable error) {
+ callbackCurrentVoiceCommands = newCurrentVoiceCommands;
+ callbackError = error;
+ }];
+ [testOp start];
+ });
+
+ // and the delete succeeds
+ describe(@"and the delete succeeds", ^{
+ beforeEach(^{
+ SDLDeleteCommandResponse *deleteOld1 = [[SDLDeleteCommandResponse alloc] init];
+ deleteOld1.success = @YES;
+ deleteOld1.resultCode = SDLResultSuccess;
+
+ [testConnectionManager respondToRequestWithResponse:deleteOld1 requestNumber:0 error:nil];
+ [testConnectionManager respondToLastMultipleRequestsWithSuccess:YES];
+ });
+
+ it(@"Should only delete voiceCommands thats not in common", ^{
+ expect(callbackCurrentVoiceCommands).to(haveCount(1));
+ expect(callbackError).to(beNil());
+ expect(testConnectionManager.receivedRequests).to(haveCount(1));
+ });
+ });
+ });
+
+
+ // going from voice commands [A] to [AB]
+ context(@"going from voice commands [A] to [AB]", ^{
+ beforeEach(^{
+ testOp = [[SDLVoiceCommandUpdateOperation alloc] initWithConnectionManager:testConnectionManager pendingVoiceCommands:@[newVoiceCommand1, oldVoiceCommand1Dupe] oldVoiceCommands:@[oldVoiceCommand1] updateCompletionHandler:^(NSArray<SDLVoiceCommand *> * _Nonnull newCurrentVoiceCommands, NSError * _Nullable error) {
+ callbackCurrentVoiceCommands = newCurrentVoiceCommands;
+ callbackError = error;
+ }];
+ [testOp start];
+ });
+
+ // and the add succeeds
+ describe(@"and the add succeeds", ^{
+ beforeEach(^{
+ SDLAddCommandResponse *addNew1 = [[SDLAddCommandResponse alloc] init];
+ addNew1.success = @YES;
+ addNew1.resultCode = SDLResultSuccess;
+
+ [testConnectionManager respondToRequestWithResponse:addNew1 requestNumber:0 error:nil];
+ [testConnectionManager respondToLastMultipleRequestsWithSuccess:YES];
+ });
+
+ it(@"should only upload the voiceCommand thats not in common and update the handler for voiceCommand in common", ^{
+ expect(callbackCurrentVoiceCommands).to(haveCount(2));
+ expect(callbackError).to(beNil());
+ expect(testConnectionManager.receivedRequests).to(haveCount(1));
+ expect(testOp.currentVoiceCommands.firstObject.handler == oldVoiceCommand1Dupe.handler).to(beTrue());
+ });
+ });
+ });
+
+ // going from voice commands [AB] to [CD]
+ context(@"going from voice commands [AB] to [CD]", ^{
+ beforeEach(^{
+ testOp = [[SDLVoiceCommandUpdateOperation alloc] initWithConnectionManager:testConnectionManager pendingVoiceCommands:@[newVoiceCommand1, newVoiceCommand2] oldVoiceCommands:@[oldVoiceCommand1, oldVoiceCommand2] updateCompletionHandler:^(NSArray<SDLVoiceCommand *> * _Nonnull newCurrentVoiceCommands, NSError * _Nullable error) {
+ callbackCurrentVoiceCommands = newCurrentVoiceCommands;
+ callbackError = error;
+ }];
+ [testOp start];
+ });
+
+ // the delete and add commands succeeds
+ describe(@"the delete and add commands succeeds", ^{
+ beforeEach(^{
+ SDLDeleteCommandResponse *deleteOld1 = [[SDLDeleteCommandResponse alloc] init];
+ deleteOld1.success = @YES;
+ deleteOld1.resultCode = SDLResultSuccess;
+
+ SDLDeleteCommandResponse *deleteOld2 = [[SDLDeleteCommandResponse alloc] init];
+ deleteOld2.success = @YES;
+ deleteOld2.resultCode = SDLResultSuccess;
+
+ [testConnectionManager respondToRequestWithResponse:deleteOld1 requestNumber:0 error:nil];
+ [testConnectionManager respondToRequestWithResponse:deleteOld2 requestNumber:1 error:nil];
+ [testConnectionManager respondToLastMultipleRequestsWithSuccess:YES];
+
+ SDLAddCommandResponse *addNew1 = [[SDLAddCommandResponse alloc] init];
+ addNew1.success = @YES;
+ addNew1.resultCode = SDLResultSuccess;
+
+ SDLAddCommandResponse *addNew2 = [[SDLAddCommandResponse alloc] init];
+ addNew2.success = @YES;
+ addNew2.resultCode = SDLResultSuccess;
+
+ [testConnectionManager respondToRequestWithResponse:addNew1 requestNumber:2 error:nil];
+ [testConnectionManager respondToRequestWithResponse:addNew2 requestNumber:3 error:nil];
+ [testConnectionManager respondToLastMultipleRequestsWithSuccess:YES];
+ });
+
+ it(@"should delete and upload the voiceCommands", ^{
+ expect(callbackCurrentVoiceCommands).to(haveCount(2));
+ expect(callbackError).to(beNil());
+ expect(testConnectionManager.receivedRequests).to(haveCount(4));
+ });
+ });
+ });
+
context(@"if it doesn't have any voice commands to delete", ^{
beforeEach(^{
testOp = [[SDLVoiceCommandUpdateOperation alloc] initWithConnectionManager:testConnectionManager pendingVoiceCommands:@[newVoiceCommand1, newVoiceCommand2] oldVoiceCommands:@[] updateCompletionHandler:^(NSArray<SDLVoiceCommand *> * _Nonnull newCurrentVoiceCommands, NSError * _Nullable error) {