summaryrefslogtreecommitdiff
path: root/SmartDeviceLinkTests/DevAPISpecs
diff options
context:
space:
mode:
authorJoel Fischer <joeljfischer@gmail.com>2021-09-27 09:54:30 -0400
committerJoel Fischer <joeljfischer@gmail.com>2021-09-27 09:54:30 -0400
commitcbc318c64aadd04600d14bd70d04cb13717953c8 (patch)
tree7dc36e8f753e901a535aa950d29a81a8bdaa3606 /SmartDeviceLinkTests/DevAPISpecs
parent6685595ac5d8db73ccfd64542d8af0df61f95ef2 (diff)
parent6eaf543e6b6ecea478bd760be9cbf37564398681 (diff)
downloadsdl_ios-cbc318c64aadd04600d14bd70d04cb13717953c8.tar.gz
Merge branch 'develop' into bugfix/issue-2034-file-manager-multiple-uploads
# Conflicts: # SmartDeviceLink/private/SDLDeleteFileOperation.m # SmartDeviceLink/public/SDLFileManager.m
Diffstat (limited to 'SmartDeviceLinkTests/DevAPISpecs')
-rw-r--r--SmartDeviceLinkTests/DevAPISpecs/SDLMenuCellSpec.m7
-rw-r--r--SmartDeviceLinkTests/DevAPISpecs/SDLMenuConfigurationUpdateOperationSpec.m174
-rw-r--r--SmartDeviceLinkTests/DevAPISpecs/SDLMenuManagerSpec.m887
-rw-r--r--SmartDeviceLinkTests/DevAPISpecs/SDLMenuReplaceOperationSpec.m745
-rw-r--r--SmartDeviceLinkTests/DevAPISpecs/SDLMenuReplaceUtilitiesSpec.m532
-rw-r--r--SmartDeviceLinkTests/DevAPISpecs/SDLMenuReplaceUtilitiesSpecHelpers.h22
-rw-r--r--SmartDeviceLinkTests/DevAPISpecs/SDLMenuReplaceUtilitiesSpecHelpers.m70
-rw-r--r--SmartDeviceLinkTests/DevAPISpecs/SDLMenuShowOperationSpec.m167
8 files changed, 1892 insertions, 712 deletions
diff --git a/SmartDeviceLinkTests/DevAPISpecs/SDLMenuCellSpec.m b/SmartDeviceLinkTests/DevAPISpecs/SDLMenuCellSpec.m
index f6d982a57..4958e7674 100644
--- a/SmartDeviceLinkTests/DevAPISpecs/SDLMenuCellSpec.m
+++ b/SmartDeviceLinkTests/DevAPISpecs/SDLMenuCellSpec.m
@@ -108,6 +108,13 @@ describe(@"a menu cell", ^{
expect([testCell isEqual:testCell2]).to(beFalse());
});
+ it(@"should compare cells and return false if one cell has subcells empty and another has subcells nil", ^{
+ testCell = [[SDLMenuCell alloc] initWithTitle:someTitle secondaryText:someSecondaryTitle tertiaryText:someTertiaryTitle icon:nil secondaryArtwork:someSecondaryArtwork submenuLayout:testLayout subCells:nil];
+ testCell2 = [[SDLMenuCell alloc] initWithTitle:someTitle secondaryText:someSecondaryTitle tertiaryText:someTertiaryTitle icon:nil secondaryArtwork:someSecondaryArtwork submenuLayout:testLayout subCells:@[]];
+
+ expect([testCell isEqual:testCell2]).to(beFalse());
+ });
+
it(@"should compare cells and return true if cells equal", ^{
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
diff --git a/SmartDeviceLinkTests/DevAPISpecs/SDLMenuConfigurationUpdateOperationSpec.m b/SmartDeviceLinkTests/DevAPISpecs/SDLMenuConfigurationUpdateOperationSpec.m
new file mode 100644
index 000000000..3678d4ecc
--- /dev/null
+++ b/SmartDeviceLinkTests/DevAPISpecs/SDLMenuConfigurationUpdateOperationSpec.m
@@ -0,0 +1,174 @@
+//
+// SDLMenuConfigurationUpdateOperationSpec.m
+// SmartDeviceLinkTests
+//
+// Created by Joel Fischer on 2/16/21.
+// Copyright © 2021 smartdevicelink. All rights reserved.
+//
+
+#import <Nimble/Nimble.h>
+#import <OCMock/OCMock.h>
+#import <Quick/Quick.h>
+
+#import <SmartDeviceLink/SmartDeviceLink.h>
+#import "SDLMenuConfigurationUpdateOperation.h"
+#import "TestConnectionManager.h"
+
+QuickSpecBegin(SDLMenuConfigurationUpdateOperationSpec)
+
+describe(@"a menu configuration update operation", ^{
+ __block SDLMenuConfigurationUpdateOperation *testOp = nil;
+
+ __block TestConnectionManager *testConnectionManager = nil;
+ __block SDLFileManager *testFileManager = nil;
+ __block SDLWindowCapability *testWindowCapability = nil;
+ SDLMenuConfiguration *testMenuConfiguration = [[SDLMenuConfiguration alloc] initWithMainMenuLayout:SDLMenuLayoutList defaultSubmenuLayout:SDLMenuLayoutTiles];
+
+ __block SDLMenuConfigurationUpdatedBlock testUpdatedBlock = nil;
+ __block SDLMenuConfiguration *resultMenuConfiguration = nil;
+ __block NSError *resultError = nil;
+
+ beforeEach(^{
+ testConnectionManager = [[TestConnectionManager alloc] init];
+ testFileManager = OCMClassMock([SDLFileManager class]);
+ testWindowCapability = [[SDLWindowCapability alloc] initWithWindowID:@0 textFields:nil imageFields:nil imageTypeSupported:nil templatesAvailable:nil numCustomPresetsAvailable:nil buttonCapabilities:nil softButtonCapabilities:nil menuLayoutsAvailable:@[] dynamicUpdateCapabilities:nil keyboardCapabilities:nil];
+
+ resultMenuConfiguration = nil;
+ resultError = nil;
+ testUpdatedBlock = ^(SDLMenuConfiguration *newConfiguration, NSError *_Nullable error) {
+ resultMenuConfiguration = newConfiguration;
+ resultError = error;
+ };
+ });
+
+ // when the layout check fails
+ describe(@"when the layout check fails", ^{
+ // when there are no known menu layouts
+ context(@"when there are no known menu layouts", ^{
+ it(@"should return an error and finish", ^{
+ testOp = [[SDLMenuConfigurationUpdateOperation alloc] initWithConnectionManager:testConnectionManager windowCapability:testWindowCapability newMenuConfiguration:testMenuConfiguration configurationUpdatedHandler:testUpdatedBlock];
+ [testOp start];
+
+ expect(testConnectionManager.receivedRequests).to(beEmpty());
+ expect(testOp.isFinished).to(beTrue());
+ expect(resultMenuConfiguration).to(beNil());
+ expect(resultError).toNot(beNil());
+ expect(testOp.error).toNot(beNil());
+ });
+ });
+
+ // when the set main menu layout is not available
+ context(@"when the set main menu layout is not available", ^{
+ beforeEach(^{
+ testWindowCapability.menuLayoutsAvailable = @[SDLMenuLayoutTiles];
+ });
+
+ it(@"should return an error and finish", ^{
+ testOp = [[SDLMenuConfigurationUpdateOperation alloc] initWithConnectionManager:testConnectionManager windowCapability:testWindowCapability newMenuConfiguration:testMenuConfiguration configurationUpdatedHandler:testUpdatedBlock];
+ [testOp start];
+
+ expect(testConnectionManager.receivedRequests).to(beEmpty());
+ expect(testOp.isFinished).to(beTrue());
+ expect(resultMenuConfiguration).to(beNil());
+ expect(resultError).toNot(beNil());
+ expect(testOp.error).toNot(beNil());
+ });
+ });
+
+ // when the set default submenu layout is not available
+ context(@"when the set default submenu layout is not available", ^{
+ beforeEach(^{
+ testWindowCapability.menuLayoutsAvailable = @[SDLMenuLayoutList];
+ });
+
+ it(@"should return an error and finish", ^{
+ testOp = [[SDLMenuConfigurationUpdateOperation alloc] initWithConnectionManager:testConnectionManager windowCapability:testWindowCapability newMenuConfiguration:testMenuConfiguration configurationUpdatedHandler:testUpdatedBlock];
+ [testOp start];
+
+ expect(testConnectionManager.receivedRequests).to(beEmpty());
+ expect(testOp.isFinished).to(beTrue());
+ expect(resultMenuConfiguration).to(beNil());
+ expect(resultError).toNot(beNil());
+ expect(testOp.error).toNot(beNil());
+ });
+ });
+ });
+
+ // when the set layouts are available
+ describe(@"when the set layouts are available", ^{
+ __block SDLSetGlobalPropertiesResponse *response = [[SDLSetGlobalPropertiesResponse alloc] init];
+
+ beforeEach(^{
+ testWindowCapability.menuLayoutsAvailable = @[SDLMenuLayoutList, SDLMenuLayoutTiles];
+
+ testOp = [[SDLMenuConfigurationUpdateOperation alloc] initWithConnectionManager:testConnectionManager windowCapability:testWindowCapability newMenuConfiguration:testMenuConfiguration configurationUpdatedHandler:testUpdatedBlock];
+ [testOp start];
+ });
+
+ // should send the RPC
+ it(@"should send the RPC", ^{
+ expect(testOp.error).to(beNil());
+ expect(testConnectionManager.receivedRequests).toNot(beEmpty());
+ expect(testOp.isFinished).to(beFalse());
+ expect(resultMenuConfiguration).to(beNil());
+ expect(resultError).to(beNil());
+
+ SDLSetGlobalProperties *receivedSGP = (SDLSetGlobalProperties *)testConnectionManager.receivedRequests[0];
+ expect(receivedSGP.menuLayout).to(equal(testMenuConfiguration.mainMenuLayout));
+ });
+
+ // if an error returned
+ context(@"if an error returned", ^{
+ beforeEach(^{
+ response.success = @NO;
+ response.resultCode = SDLResultRejected;
+ });
+
+ it(@"should return an error and finish", ^{
+ [testConnectionManager respondToLastRequestWithResponse:response];
+
+ expect(testOp.error).toNot(beNil());
+ expect(testConnectionManager.receivedRequests).toNot(beEmpty());
+ expect(testOp.isFinished).to(beTrue());
+ expect(resultMenuConfiguration).to(beNil());
+ });
+ });
+
+ // if it succeeded
+ context(@"if it succeeded", ^{
+ beforeEach(^{
+ response.success = @YES;
+ response.resultCode = SDLResultSuccess;
+ });
+
+ it(@"should not return an error and finish", ^{
+ [testConnectionManager respondToLastRequestWithResponse:response];
+
+ expect(testOp.error).to(beNil());
+ expect(testConnectionManager.receivedRequests).toNot(beEmpty());
+ expect(testOp.isFinished).to(beTrue());
+ expect(resultMenuConfiguration).to(equal(testMenuConfiguration));
+ expect(resultError).to(beNil());
+ });
+ });
+ });
+
+ describe(@"cancelling the operation before it starts", ^{
+ testOp = [[SDLMenuConfigurationUpdateOperation alloc] initWithConnectionManager:testConnectionManager windowCapability:testWindowCapability newMenuConfiguration:testMenuConfiguration configurationUpdatedHandler:testUpdatedBlock];
+
+ beforeEach(^{
+ [testOp cancel];
+ [testOp start];
+ });
+
+ it(@"should finish without any callbacks", ^{
+ expect(testOp.error).to(beNil());
+ expect(testConnectionManager.receivedRequests).to(beEmpty());
+ expect(testOp.isFinished).to(beTrue());
+ expect(resultMenuConfiguration).to(beNil());
+ expect(resultError).to(beNil());
+ });
+ });
+});
+
+QuickSpecEnd
diff --git a/SmartDeviceLinkTests/DevAPISpecs/SDLMenuManagerSpec.m b/SmartDeviceLinkTests/DevAPISpecs/SDLMenuManagerSpec.m
index 4b20b08de..c663205e2 100644
--- a/SmartDeviceLinkTests/DevAPISpecs/SDLMenuManagerSpec.m
+++ b/SmartDeviceLinkTests/DevAPISpecs/SDLMenuManagerSpec.m
@@ -4,9 +4,10 @@
#import <SmartDeviceLink/SmartDeviceLink.h>
+#import "SDLGlobals.h"
#import "SDLMenuManager.h"
+#import "SDLMenuReplaceOperation.h"
#import "TestConnectionManager.h"
-#import "SDLGlobals.h"
@interface SDLMenuCell()
@@ -22,20 +23,13 @@
@property (weak, nonatomic) SDLFileManager *fileManager;
@property (weak, nonatomic) SDLSystemCapabilityManager *systemCapabilityManager;
-@property (copy, nonatomic, nullable) SDLHMILevel currentHMILevel;
-@property (copy, nonatomic, nullable) SDLSystemContext currentSystemContext;
+@property (strong, nonatomic) NSOperationQueue *transactionQueue;
@property (strong, nonatomic, nullable) SDLWindowCapability *windowCapability;
-@property (strong, nonatomic, nullable) NSArray<SDLRPCRequest *> *inProgressUpdate;
-@property (assign, nonatomic) BOOL hasQueuedUpdate;
-@property (assign, nonatomic) BOOL waitingOnHMIUpdate;
-@property (copy, nonatomic) NSArray<SDLMenuCell *> *waitingUpdateMenuCells;
-
-@property (assign, nonatomic) UInt32 lastMenuId;
-@property (copy, nonatomic) NSArray<SDLMenuCell *> *oldMenuCells;
-
-- (BOOL)sdl_shouldRPCsIncludeImages:(NSArray<SDLMenuCell *> *)cells;
-- (void)sdl_displayCapabilityDidUpdate;
+@property (copy, nonatomic, nullable) SDLHMILevel currentHMILevel;
+@property (copy, nonatomic, nullable) SDLSystemContext currentSystemContext;
+@property (copy, nonatomic) NSArray<SDLMenuCell *> *currentMenuCells;
+@property (strong, nonatomic, nullable) SDLMenuConfiguration *currentMenuConfiguration;
@end
@@ -46,40 +40,15 @@ describe(@"menu manager", ^{
__block TestConnectionManager *mockConnectionManager = nil;
__block SDLFileManager *mockFileManager = nil;
__block SDLSystemCapabilityManager *mockSystemCapabilityManager = nil;
- __block SDLArtwork *testArtwork = nil;
- __block SDLArtwork *testArtwork2 = nil;
- __block SDLArtwork *testArtwork3 = nil;
-
- __block SDLMenuCell *textOnlyCell = nil;
- __block SDLMenuCell *textOnlyCell2 = nil;
- __block SDLMenuCell *textAndImageCell = nil;
- __block SDLMenuCell *textAndImageCell2 = nil;
- __block SDLMenuCell *submenuCell = nil;
- __block SDLMenuCell *submenuCell2 = nil;
- __block SDLMenuCell *submenuImageCell = nil;
__block SDLMenuConfiguration *testMenuConfiguration = nil;
- __block SDLImageField *commandIconField = nil;
- __block SDLImageField *commandSecondaryArtworkField = nil;
- __block SDLImageField *submenuIconField = nil;
- __block SDLImageField *subMenuSecondaryArtworkField = nil;
-
- __block SDLVersion *menuUniquenessActiveVersion = nil;
+ __block SDLMenuCell *textOnlyCell = nil;
+ __block SDLMenuCell *submenuCell = nil;
beforeEach(^{
- testArtwork = [[SDLArtwork alloc] initWithData:[@"Test data" dataUsingEncoding:NSUTF8StringEncoding] name:@"some artwork name" fileExtension:@"png" persistent:NO];
- testArtwork2 = [[SDLArtwork alloc] initWithData:[@"Test data 2" dataUsingEncoding:NSUTF8StringEncoding] name:@"some artwork name 2" fileExtension:@"png" persistent:NO];
- testArtwork3 = [[SDLArtwork alloc] initWithData:[@"Test data 3" dataUsingEncoding:NSUTF8StringEncoding] name:@"some artwork name" fileExtension:@"png" persistent:NO];
- testArtwork3.overwrite = YES;
-
textOnlyCell = [[SDLMenuCell alloc] initWithTitle:@"Test 1" secondaryText:nil tertiaryText:nil icon:nil secondaryArtwork:nil voiceCommands:nil handler:^(SDLTriggerSource _Nonnull triggerSource) {}];
- textAndImageCell = [[SDLMenuCell alloc] initWithTitle:@"Test 2" secondaryText:nil tertiaryText:nil icon:testArtwork secondaryArtwork:nil voiceCommands:nil handler:^(SDLTriggerSource _Nonnull triggerSource) {}];
- textAndImageCell2 = [[SDLMenuCell alloc] initWithTitle:@"Test 2" secondaryText:nil tertiaryText:nil icon:testArtwork2 secondaryArtwork:nil voiceCommands:nil handler:^(SDLTriggerSource _Nonnull triggerSource) {}];
- submenuCell = [[SDLMenuCell alloc] initWithTitle:@"Test 3" secondaryText:nil tertiaryText:nil icon:nil secondaryArtwork:nil submenuLayout:nil subCells:@[textOnlyCell, textAndImageCell]];
- submenuCell2 = [[SDLMenuCell alloc] initWithTitle:@"Test 3" secondaryText:nil tertiaryText:nil icon:nil secondaryArtwork:nil submenuLayout:nil subCells:@[textAndImageCell, textAndImageCell2]];
- submenuImageCell = [[SDLMenuCell alloc] initWithTitle:@"Test 4" secondaryText:nil tertiaryText:nil icon:testArtwork2 secondaryArtwork:nil submenuLayout:SDLMenuLayoutTiles subCells:@[textOnlyCell]];
- textOnlyCell2 = [[SDLMenuCell alloc] initWithTitle:@"Test 5" secondaryText:nil tertiaryText:nil icon:nil secondaryArtwork:nil voiceCommands:nil handler:^(SDLTriggerSource _Nonnull triggerSource) {}];
+ submenuCell = [[SDLMenuCell alloc] initWithTitle:@"Test 3" secondaryText:nil tertiaryText:nil icon:nil secondaryArtwork:nil submenuLayout:nil subCells:@[textOnlyCell]];
testMenuConfiguration = [[SDLMenuConfiguration alloc] initWithMainMenuLayout:SDLMenuLayoutTiles defaultSubmenuLayout:SDLMenuLayoutList];
@@ -88,620 +57,257 @@ describe(@"menu manager", ^{
mockSystemCapabilityManager = OCMClassMock([SDLSystemCapabilityManager class]);
testManager = [[SDLMenuManager alloc] initWithConnectionManager:mockConnectionManager fileManager:mockFileManager systemCapabilityManager:mockSystemCapabilityManager];
- commandIconField = [[SDLImageField alloc] initWithName:SDLImageFieldNameCommandIcon imageTypeSupported:@[SDLFileTypePNG] imageResolution:nil];
- commandSecondaryArtworkField = [[SDLImageField alloc] initWithName:SDLImageFieldNameMenuCommandSecondaryImage imageTypeSupported:@[SDLFileTypePNG] imageResolution:nil];
- submenuIconField = [[SDLImageField alloc] initWithName:SDLImageFieldNameSubMenuIcon imageTypeSupported:@[SDLFileTypePNG] imageResolution:nil];
- subMenuSecondaryArtworkField = [[SDLImageField alloc] initWithName:SDLImageFieldNameMenuSubMenuSecondaryImage imageTypeSupported:@[SDLFileTypePNG] imageResolution:nil];
- SDLTextField *commandSecondaryTextField = [[SDLTextField alloc] initWithName:SDLTextFieldNameMenuCommandSecondaryText characterSet:SDLCharacterSetAscii width:100 rows:1];
- SDLTextField *commandTertiaryTextField = [[SDLTextField alloc] initWithName:SDLTextFieldNameMenuCommandTertiaryText characterSet:SDLCharacterSetAscii width:100 rows:1];
- SDLTextField *submenuSecondaryTextField = [[SDLTextField alloc] initWithName:SDLTextFieldNameMenuSubMenuSecondaryText characterSet:SDLCharacterSetAscii width:100 rows:1];
- SDLTextField *submenuTertiaryTextField = [[SDLTextField alloc] initWithName:SDLTextFieldNameMenuSubMenuTertiaryText characterSet:SDLCharacterSetAscii width:100 rows:1];
+ SDLImageField *commandIconField = [[SDLImageField alloc] init];
+ commandIconField.name = SDLImageFieldNameCommandIcon;
SDLWindowCapability *windowCapability = [[SDLWindowCapability alloc] init];
windowCapability.windowID = @(SDLPredefinedWindowsDefaultWindow);
- windowCapability.imageFields = @[commandIconField, commandSecondaryArtworkField, submenuIconField, subMenuSecondaryArtworkField];
- windowCapability.textFields = @[commandSecondaryTextField, commandTertiaryTextField, submenuSecondaryTextField, submenuTertiaryTextField];
+ windowCapability.imageFields = @[commandIconField];
windowCapability.imageTypeSupported = @[SDLImageTypeDynamic, SDLImageTypeStatic];
windowCapability.menuLayoutsAvailable = @[SDLMenuLayoutList, SDLMenuLayoutTiles];
testManager.windowCapability = windowCapability;
- menuUniquenessActiveVersion = [[SDLVersion alloc] initWithMajor:7 minor:1 patch:0];
});
- // should instantiate correctly
it(@"should instantiate correctly", ^{
expect(testManager.menuCells).to(beEmpty());
+
+ expect(@(testManager.dynamicMenuUpdatesMode)).to(equal(@(SDLDynamicMenuUpdatesModeOnWithCompatibility)));
expect(testManager.connectionManager).to(equal(mockConnectionManager));
expect(testManager.fileManager).to(equal(mockFileManager));
expect(testManager.systemCapabilityManager).to(equal(mockSystemCapabilityManager));
+ expect(testManager.transactionQueue).toNot(beNil());
+ expect(testManager.windowCapability).toNot(beNil());
expect(testManager.currentHMILevel).to(beNil());
- expect(testManager.inProgressUpdate).to(beNil());
- expect(testManager.hasQueuedUpdate).to(beFalse());
- expect(testManager.waitingOnHMIUpdate).to(beFalse());
- expect(testManager.lastMenuId).to(equal(1));
- expect(testManager.oldMenuCells).to(beEmpty());
- expect(testManager.waitingUpdateMenuCells).to(beNil());
- expect(testManager.menuConfiguration).toNot(beNil());
- });
-
- // updating menu cells before HMI is ready
- describe(@"updating menu cells before HMI is ready", ^{
- // when in HMI NONE
- context(@"when in HMI NONE", ^{
- beforeEach(^{
- testManager.currentHMILevel = SDLHMILevelNone;
- testManager.menuCells = @[textOnlyCell];
- });
-
- it(@"should not update", ^{
- expect(mockConnectionManager.receivedRequests).to(beEmpty());
- });
-
- describe(@"when entering the foreground", ^{
- beforeEach(^{
- SDLOnHMIStatus *onHMIStatus = [[SDLOnHMIStatus alloc] init];
- onHMIStatus.hmiLevel = SDLHMILevelFull;
- onHMIStatus.systemContext = SDLSystemContextMain;
-
- SDLRPCNotificationNotification *testSystemContextNotification = [[SDLRPCNotificationNotification alloc] initWithName:SDLDidChangeHMIStatusNotification object:nil rpcNotification:onHMIStatus];
- [[NSNotificationCenter defaultCenter] postNotification:testSystemContextNotification];
- });
-
- it(@"should update", ^{
- expect(mockConnectionManager.receivedRequests).toNot(beEmpty());
- });
- });
- });
-
- // when no HMI level has been received
- context(@"when no HMI level has been received", ^{
- beforeEach(^{
- testManager.currentHMILevel = nil;
- });
-
- it(@"should not update the menu configuration", ^{
- testManager.menuConfiguration = testMenuConfiguration;
- expect(mockConnectionManager.receivedRequests).to(beEmpty());
- expect(testManager.menuConfiguration).toNot(equal(testMenuConfiguration));
- });
-
- it(@"should not update the menu cells", ^{
- testManager.menuCells = @[textOnlyCell];
- expect(mockConnectionManager.receivedRequests).to(beEmpty());
- });
- });
-
- // when in the menu
- context(@"when in the menu", ^{
- beforeEach(^{
- [SDLGlobals sharedGlobals].rpcVersion = [SDLVersion versionWithString:@"6.0.0"];
- testManager.currentHMILevel = SDLHMILevelFull;
- testManager.currentSystemContext = SDLSystemContextMenu;
- });
-
- it(@"should update the menu configuration", ^{
- testManager.menuConfiguration = testMenuConfiguration;
- expect(mockConnectionManager.receivedRequests).toNot(beEmpty());
- expect(testManager.menuConfiguration).to(equal(testMenuConfiguration));
- });
- });
+ expect(testManager.currentSystemContext).to(beNil());
+ expect(testManager.currentMenuCells).to(beEmpty());
+ expect(testManager.currentMenuConfiguration).to(beNil());
});
- // display capability updates
- describe(@"display capability updates", ^{
+ describe(@"when the manager stops", ^{
beforeEach(^{
- testManager.currentHMILevel = SDLHMILevelFull;
- testManager.currentSystemContext = SDLSystemContextMain;
+ [testManager stop];
});
- it(@"should save the new window capability", ^{
- SDLWindowCapability *testWindowCapability = [[SDLWindowCapability alloc] init];
- testWindowCapability.textFields = @[[[SDLTextField alloc] initWithName:SDLTextFieldNameMenuName characterSet:SDLCharacterSetUtf8 width:500 rows:1]];
- OCMStub([mockSystemCapabilityManager defaultMainWindowCapability]).andReturn(testWindowCapability);
- [testManager sdl_displayCapabilityDidUpdate];
+ it(@"should reset correctly", ^{
+ expect(testManager.menuCells).to(beEmpty());
- expect(testManager.windowCapability).to(equal(testWindowCapability));
+ expect(@(testManager.dynamicMenuUpdatesMode)).to(equal(@(SDLDynamicMenuUpdatesModeOnWithCompatibility)));
+ expect(testManager.connectionManager).to(equal(mockConnectionManager));
+ expect(testManager.fileManager).to(equal(mockFileManager));
+ expect(testManager.systemCapabilityManager).to(equal(mockSystemCapabilityManager));
+ expect(testManager.transactionQueue).toNot(beNil());
+ expect(testManager.windowCapability).to(beNil());
+ expect(testManager.currentHMILevel).to(beNil());
+ expect(testManager.currentSystemContext).to(beNil());
+ expect(testManager.currentMenuCells).to(beEmpty());
+ expect(testManager.currentMenuConfiguration).to(beNil());
});
});
- // updating menu cells
- describe(@"updating menu cells", ^{
+ context(@"when in HMI NONE", ^{
beforeEach(^{
- testManager.currentHMILevel = SDLHMILevelFull;
- testManager.currentSystemContext = SDLSystemContextMain;
+ SDLOnHMIStatus *noneStatus = [[SDLOnHMIStatus alloc] initWithHMILevel:SDLHMILevelNone systemContext:SDLSystemContextMain audioStreamingState:SDLAudioStreamingStateNotAudible videoStreamingState:nil windowID:nil];
+ [[NSNotificationCenter defaultCenter] postNotification:[[SDLRPCNotificationNotification alloc] initWithName:SDLDidChangeHMIStatusNotification object:nil rpcNotification:noneStatus]];
});
- // HMI does not support a command secondary image
- context(@"HMI does not support a command secondary image", ^{
- SDLArtwork *staticArtwork = [[SDLArtwork alloc] initWithStaticIcon:SDLStaticIconNameKey];
-
- beforeEach(^{
- testManager.windowCapability.imageFields = @[commandIconField, submenuIconField, subMenuSecondaryArtworkField];
- textAndImageCell = [[SDLMenuCell alloc] initWithTitle:@"Test 2" secondaryText:nil tertiaryText:nil icon:nil secondaryArtwork:staticArtwork voiceCommands:nil handler:^(SDLTriggerSource _Nonnull triggerSource) {}];
- textAndImageCell2 = [[SDLMenuCell alloc] initWithTitle:@"Test 3" secondaryText:nil tertiaryText:nil icon:nil secondaryArtwork:staticArtwork submenuLayout:SDLMenuLayoutList subCells:@[textOnlyCell]];
- testManager.menuCells = @[textAndImageCell, textAndImageCell2];
- });
-
- it(@"should not send secondaryArtwork in our request for addCommand but send it with addSubMenu", ^{
- SDLAddCommand *cellCommand = (SDLAddCommand *)testManager.inProgressUpdate.firstObject;
- SDLAddSubMenu *cellSubMenu = (SDLAddSubMenu *)testManager.inProgressUpdate[1];
- expect(cellCommand.menuParams.menuName).to(equal(@"Test 2"));
- expect(cellCommand.secondaryImage).to(beNil());
- expect(cellSubMenu.secondaryImage).toNot(beNil());
- });
+ it(@"should not suspend the transaction queue", ^{
+ expect(testManager.transactionQueue.isSuspended).to(beTrue());
});
- // HMI does not support a submenu secondary image
- context(@"HMI does not support a submenu secondary image", ^{
- SDLArtwork *staticArtwork = [[SDLArtwork alloc] initWithStaticIcon:SDLStaticIconNameKey];
-
+ // when entering HMI FULL
+ describe(@"when entering HMI FULL", ^{
beforeEach(^{
- testManager.windowCapability.imageFields = @[commandIconField, submenuIconField, commandSecondaryArtworkField];
- textAndImageCell = [[SDLMenuCell alloc] initWithTitle:@"Test 2" secondaryText:nil tertiaryText:nil icon:nil secondaryArtwork:staticArtwork submenuLayout:SDLMenuLayoutList subCells:@[textOnlyCell]];
- textAndImageCell2 = [[SDLMenuCell alloc] initWithTitle:@"Test 3" secondaryText:nil tertiaryText:nil icon:nil secondaryArtwork:staticArtwork voiceCommands:nil handler:^(SDLTriggerSource _Nonnull triggerSource) {}];
- testManager.menuCells = @[textAndImageCell, textAndImageCell2];
- });
+ SDLOnHMIStatus *onHMIStatus = [[SDLOnHMIStatus alloc] init];
+ onHMIStatus.hmiLevel = SDLHMILevelFull;
+ onHMIStatus.systemContext = SDLSystemContextMain;
- it(@"should not send secondaryArtwork in our request for addSubMenu but send it with addCommand", ^{
- SDLAddSubMenu *cellSubMenu = (SDLAddSubMenu *)testManager.inProgressUpdate.firstObject;
- SDLAddCommand *cellCommand = (SDLAddCommand *)testManager.inProgressUpdate[1];
- expect(cellSubMenu.menuName).to(equal(@"Test 2"));
- expect(cellSubMenu.secondaryImage).to(beNil());
- expect(cellCommand.secondaryImage).toNot(beNil());
- });
- });
-
- // duplicate titles version >= 7.1.0
- context(@"duplicate titles version >= 7.1.0", ^{
- beforeEach(^{
- [SDLGlobals sharedGlobals].rpcVersion = menuUniquenessActiveVersion;
- });
-
- // if there are duplicate cells once you strip unused menu properties
- context(@"if there are duplicate cells once you strip unused menu properties", ^{
- beforeEach(^{
- testManager.windowCapability.textFields = @[];
- testManager.windowCapability.imageFields = @[];
- });
-
- it(@"should update the cells' unique title to include unique data", ^{
- testManager.menuCells = @[textAndImageCell, textAndImageCell2];
- expect(testManager.menuCells).toNot(beEmpty());
- expect(testManager.menuCells.firstObject.uniqueTitle).to(equal("Test 2"));
- expect(testManager.menuCells.lastObject.uniqueTitle).to(equal("Test 2 (2)"));
- });
-
- it(@"should update subcells' unique title to include unique data", ^{
- testManager.menuCells = @[submenuCell2];
- expect(testManager.menuCells).toNot(beEmpty());
- expect(testManager.menuCells.firstObject.subCells.firstObject.uniqueTitle).to(equal("Test 2"));
- expect(testManager.menuCells.firstObject.subCells.lastObject.uniqueTitle).to(equal("Test 2 (2)"));
- });
+ SDLRPCNotificationNotification *testSystemContextNotification = [[SDLRPCNotificationNotification alloc] initWithName:SDLDidChangeHMIStatusNotification object:nil rpcNotification:onHMIStatus];
+ [[NSNotificationCenter defaultCenter] postNotification:testSystemContextNotification];
});
- // if there are no duplicate cells
- context(@"if there are no duplicate cells", ^{
- it(@"should not update the cells' unique title", ^{
- testManager.menuCells = @[textAndImageCell, textAndImageCell2];
- expect(testManager.menuCells).toNot(beEmpty());
- expect(testManager.menuCells.firstObject.uniqueTitle).to(equal("Test 2"));
- expect(testManager.menuCells.lastObject.uniqueTitle).to(equal("Test 2"));
- });
-
- it(@"should not update subcells' unique title", ^{
- testManager.menuCells = @[submenuCell2];
- expect(testManager.menuCells).toNot(beEmpty());
- expect(testManager.menuCells.firstObject.subCells.firstObject.uniqueTitle).to(equal("Test 2"));
- expect(testManager.menuCells.firstObject.subCells.lastObject.uniqueTitle).to(equal("Test 2"));
- });
+ it(@"should run the transaction queue", ^{
+ expect(testManager.transactionQueue.isSuspended).to(beFalse());
});
});
+ });
- // duplicate titles version <= 7.1.0
- context(@"duplicate titles version <= 7.1.0", ^{
- beforeEach(^{
- [SDLGlobals sharedGlobals].rpcVersion = [[SDLVersion alloc] initWithMajor:7 minor:0 patch:0];
- });
-
- it(@"append a number to the unique text for main menu cells", ^{
- testManager.menuCells = @[textAndImageCell, textAndImageCell2];
- expect(testManager.menuCells).toNot(beEmpty());
- expect(testManager.menuCells.firstObject.uniqueTitle).to(equal("Test 2"));
- expect(testManager.menuCells.lastObject.uniqueTitle).to(equal("Test 2 (2)"));
- });
-
- it(@"should append a number to the unique text for subcells", ^{
- testManager.menuCells = @[submenuCell2];
- expect(testManager.menuCells).toNot(beEmpty());
- expect(testManager.menuCells.firstObject.subCells.firstObject.uniqueTitle).to(equal("Test 2"));
- expect(testManager.menuCells.firstObject.subCells.lastObject.uniqueTitle).to(equal("Test 2 (2)"));
- });
+ context(@"when the HMI is ready", ^{
+ beforeEach(^{
+ testManager.currentHMILevel = SDLHMILevelFull;
+ testManager.currentSystemContext = SDLSystemContextMain;
});
- // when there are complete duplicates
- describe(@"when there are complete duplicates", ^{
- // when the cells contain duplicates
- context(@"when the cells contain duplicates", ^{
- SDLMenuCell *textCell = [[SDLMenuCell alloc] initWithTitle:@"Test 1" secondaryText:nil tertiaryText:nil icon:nil secondaryArtwork:nil voiceCommands:@[@"no", @"yes"] handler:^(SDLTriggerSource _Nonnull triggerSource) {}];
- SDLMenuCell *textCell2 = [[SDLMenuCell alloc] initWithTitle:@"Test 1" secondaryText:nil tertiaryText:nil icon:nil secondaryArtwork:nil voiceCommands:@[@"no", @"yes"] handler:^(SDLTriggerSource _Nonnull triggerSource) {}];
-
- it(@"should fail with duplicate cells", ^{
- testManager.menuCells = @[textCell, textCell2];
+ describe(@"setting new menu cells", ^{
+ context(@"containing duplicate titles", ^{
+ it(@"should not start an operation", ^{
+ testManager.menuCells = @[textOnlyCell, textOnlyCell];
expect(testManager.menuCells).to(beEmpty());
+ expect(testManager.transactionQueue.operationCount).to(equal(0));
});
});
- // when cells contain duplicate subcells
- context(@"when cells contain duplicate subcells", ^{
- SDLMenuCell *subCell1 = [[SDLMenuCell alloc] initWithTitle:@"subCell 1" secondaryText:nil tertiaryText:nil icon:nil secondaryArtwork:nil voiceCommands:nil handler:^(SDLTriggerSource _Nonnull triggerSource) {}];
- SDLMenuCell *subCell2 = [[SDLMenuCell alloc] initWithTitle:@"subCell 1" secondaryText:nil tertiaryText:nil icon:nil secondaryArtwork:nil voiceCommands:nil handler:^(SDLTriggerSource _Nonnull triggerSource) {}];
- SDLMenuCell *textCell = [[SDLMenuCell alloc] initWithTitle:@"Test 1" secondaryText:nil tertiaryText:nil icon:nil secondaryArtwork:nil submenuLayout:nil subCells:@[subCell1, subCell2]];
+ context(@"containing duplicate VR commands", ^{
+ SDLMenuCell *textAndVRCell1 = [[SDLMenuCell alloc] initWithTitle:@"Test 1" secondaryText:nil tertiaryText:nil icon:nil secondaryArtwork:nil voiceCommands:@[@"Cat", @"Turtle"] handler:^(SDLTriggerSource _Nonnull triggerSource) {}];
+ SDLMenuCell *textAndVRCell2 = [[SDLMenuCell alloc] initWithTitle:@"Test 3" secondaryText:nil tertiaryText:nil icon:nil secondaryArtwork:nil voiceCommands:@[@"Cat", @"Dog"] handler:^(SDLTriggerSource _Nonnull triggerSource) {}];
- it(@"should fail with duplicate cells", ^{
- testManager.menuCells = @[textCell];
+ it(@"should not start an operation", ^{
+ testManager.menuCells = @[textAndVRCell1, textAndVRCell2];
expect(testManager.menuCells).to(beEmpty());
+ expect(testManager.transactionQueue.operationCount).to(equal(0));
});
});
- context(@"duplicate VR commands", ^{
- __block SDLMenuCell *textAndVRCell1 = [[SDLMenuCell alloc] initWithTitle:@"Test 1" secondaryText:nil tertiaryText:nil icon:nil secondaryArtwork:nil voiceCommands:@[@"Cat", @"Turtle"] handler:^(SDLTriggerSource _Nonnull triggerSource) {}];
- __block SDLMenuCell *textAndVRCell2 = [[SDLMenuCell alloc] initWithTitle:@"Test 3" secondaryText:nil tertiaryText:nil icon:nil secondaryArtwork:nil voiceCommands:@[@"Cat", @"Dog"] handler:^(SDLTriggerSource _Nonnull triggerSource) {}];
+ context(@"if the new menu cells are identical to the old menu cells", ^{
+ it(@"should queue two transactions and let the operation handle not updating", ^{
+ testManager.menuCells = @[textOnlyCell];
+ testManager.menuCells = @[textOnlyCell];
- it(@"should fail when menu items have duplicate vr commands", ^{
- testManager.menuCells = @[textAndVRCell1, textAndVRCell2];
- expect(testManager.menuCells).to(beEmpty());
+ expect(testManager.menuCells).to(equal(@[textOnlyCell]));
+ expect(testManager.transactionQueue.operationCount).to(equal(2));
});
});
- context(@"when there are duplicate VR commands in subCells", ^{
- SDLMenuCell *textAndVRSubCell1 = [[SDLMenuCell alloc] initWithTitle:@"subCell 1" secondaryText:nil tertiaryText:nil icon:nil secondaryArtwork:nil voiceCommands:@[@"Cat"] handler:^(SDLTriggerSource _Nonnull triggerSource) {}];
- SDLMenuCell *textAndVRSubCell2 = [[SDLMenuCell alloc] initWithTitle:@"subCell 2" secondaryText:nil tertiaryText:nil icon:nil secondaryArtwork:nil voiceCommands:nil handler:^(SDLTriggerSource _Nonnull triggerSource) {}];
- SDLMenuCell *textAndVRCell1 = [[SDLMenuCell alloc] initWithTitle:@"Test 1" secondaryText:nil tertiaryText:nil icon:nil secondaryArtwork:nil voiceCommands:@[@"Cat", @"Turtle"] handler:^(SDLTriggerSource _Nonnull triggerSource) {}];
- SDLMenuCell *textAndVRCell2 = [[SDLMenuCell alloc] initWithTitle:@"Test 2" secondaryText:nil tertiaryText:nil icon:nil secondaryArtwork:nil submenuLayout:nil subCells:@[textAndVRSubCell1, textAndVRSubCell2]];
+ context(@"when a second menu cells update is queued before the first is done", ^{
+ it(@"should cancel the first operation", ^{
+ testManager.menuCells = @[textOnlyCell];
+ testManager.menuCells = @[submenuCell];
- it(@"should fail when menu items have duplicate vr commands", ^{
- testManager.menuCells = @[textAndVRCell1, textAndVRCell2];
- expect(testManager.menuCells).to(beEmpty());
+ expect(testManager.menuCells).to(equal(@[submenuCell]));
+ expect(testManager.transactionQueue.operationCount).to(equal(2));
+ expect(testManager.transactionQueue.operations[0].isCancelled).to(beTrue());
});
});
- });
- // should check if all artworks are uploaded and return NO
- it(@"should check if all artworks are uploaded and return NO", ^{
- textAndImageCell = [[SDLMenuCell alloc] initWithTitle:@"Test 2" secondaryText:nil tertiaryText:nil icon:nil secondaryArtwork:testArtwork voiceCommands:nil handler:^(SDLTriggerSource _Nonnull triggerSource) {}];
- testManager.menuCells = @[textAndImageCell, textOnlyCell];
- OCMVerify([testManager sdl_shouldRPCsIncludeImages:testManager.menuCells]);
- expect([testManager sdl_shouldRPCsIncludeImages:testManager.menuCells]).to(beFalse());
- });
+ context(@"if cells are formed properly", ^{
+ it(@"should properly prepare and queue the transaction", ^{
+ testManager.menuCells = @[textOnlyCell];
- // should properly update a text cell
- it(@"should properly update a text cell", ^{
- testManager.menuCells = @[textOnlyCell];
+ expect(testManager.transactionQueue.operationCount).to(equal(1));
+ expect(testManager.transactionQueue.operations[0]).to(beAnInstanceOf([SDLMenuReplaceOperation class]));
- NSPredicate *deleteCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass: %@", [SDLDeleteCommand class]];
- NSArray *deletes = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:deleteCommandPredicate];
- expect(deletes).to(beEmpty());
+ // Assign proper current menu
+ SDLMenuReplaceOperation *testOp = testManager.transactionQueue.operations[0];
+ expect(testOp.currentMenu).to(haveCount(0));
- NSPredicate *addCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass: %@", [SDLAddCommand class]];
- NSArray *add = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:addCommandPredicate];
- expect(add).toNot(beEmpty());
+ // Callback proper current menu
+ testOp.currentMenu = @[textOnlyCell];
+ [testOp finishOperation];
+ expect(testManager.currentMenuCells).to(haveCount(1));
+ });
+ });
});
- // should properly update with subcells
- it(@"should properly update with subcells", ^{
- OCMStub([mockFileManager uploadArtworks:[OCMArg any] completionHandler:[OCMArg invokeBlock]]);
- testManager.menuCells = @[submenuCell];
- [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES];
-
- NSPredicate *addCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass: %@", [SDLAddCommand class]];
- NSArray *adds = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:addCommandPredicate];
-
- NSPredicate *submenuCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass: %@", [SDLAddSubMenu class]];
- NSArray *submenus = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:submenuCommandPredicate];
-
- expect(adds).to(haveCount(2));
- expect(submenus).to(haveCount(1));
- });
+ describe(@"updating the menu configuration", ^{
+ beforeEach(^{
+ testManager.currentHMILevel = SDLHMILevelFull;
+ testManager.currentSystemContext = SDLSystemContextMain;
+ });
- // updating with an image
- describe(@"updating with an image", ^{
- context(@"when the image is already on the head unit", ^{
+ context(@"if the connection RPC version is less than 6.0.0", ^{
beforeEach(^{
- OCMStub([mockFileManager hasUploadedFile:[OCMArg isNotNil]]).andReturn(YES);
- });
-
- it(@"should check if all artworks are uploaded", ^{
- textAndImageCell = [[SDLMenuCell alloc] initWithTitle:@"Test 2" secondaryText:nil tertiaryText:nil icon:testArtwork3 secondaryArtwork:nil voiceCommands:nil handler:^(SDLTriggerSource _Nonnull triggerSource) {}];
- testManager.menuCells = @[textAndImageCell, textOnlyCell];
- OCMVerify([testManager sdl_shouldRPCsIncludeImages:testManager.menuCells]);
- expect([testManager sdl_shouldRPCsIncludeImages:testManager.menuCells]).to(beTrue());
+ [SDLGlobals sharedGlobals].rpcVersion = [SDLVersion versionWithString:@"5.0.0"];
});
- it(@"should properly update an image cell", ^{
- testManager.menuCells = @[textAndImageCell, submenuImageCell];
+ it(@"should not queue a menu configuration update", ^{
+ testManager.menuConfiguration = testMenuConfiguration;
- NSPredicate *addCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass: %@", [SDLAddCommand class]];
- NSArray *add = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:addCommandPredicate];
- SDLAddCommand *sentCommand = add.firstObject;
-
- NSPredicate *addSubmenuPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass: %@", [SDLAddSubMenu class]];
- NSArray *submenu = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:addSubmenuPredicate];
- SDLAddSubMenu *sentSubmenu = submenu.firstObject;
-
- expect(add).to(haveCount(1));
- expect(submenu).to(haveCount(1));
- expect(sentCommand.cmdIcon.value).to(equal(testArtwork.name));
- expect(sentSubmenu.menuIcon.value).to(equal(testArtwork2.name));
- OCMReject([mockFileManager uploadArtworks:[OCMArg any] completionHandler:[OCMArg any]]);
- });
-
- it(@"should properly overwrite an image cell", ^{
- OCMStub([mockFileManager fileNeedsUpload:[OCMArg isNotNil]]).andReturn(YES);
- textAndImageCell = [[SDLMenuCell alloc] initWithTitle:@"Test 2" secondaryText:nil tertiaryText:nil icon:testArtwork3 secondaryArtwork:nil voiceCommands:nil handler:^(SDLTriggerSource _Nonnull triggerSource) {}];
- testManager.menuCells = @[textAndImageCell, submenuImageCell];
- OCMVerify([mockFileManager uploadArtworks:[OCMArg any] completionHandler:[OCMArg any]]);
+ expect(testManager.menuConfiguration).toNot(equal(testMenuConfiguration));
+ expect(testManager.transactionQueue.operationCount).to(equal(0));
});
});
- // No longer a valid unit test
- context(@"when the image is not on the head unit", ^{
+ context(@"if the connection RPC version is greater than or equal to 6.0.0", ^{
beforeEach(^{
- testManager.dynamicMenuUpdatesMode = SDLDynamicMenuUpdatesModeForceOff;
- OCMStub([mockFileManager uploadArtworks:[OCMArg any] completionHandler:[OCMArg invokeBlock]]);
+ [SDLGlobals sharedGlobals].rpcVersion = [SDLVersion versionWithString:@"6.0.0"];
});
- it(@"should wait till image is on head unit and attempt to update without the image", ^{
- testManager.menuCells = @[textAndImageCell, submenuImageCell];
-
- NSPredicate *addCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass: %@", [SDLAddCommand class]];
- NSArray *add = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:addCommandPredicate];
- SDLAddCommand *sentCommand = add.firstObject;
-
- NSPredicate *addSubmenuPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass: %@", [SDLAddSubMenu class]];
- NSArray *submenu = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:addSubmenuPredicate];
- SDLAddSubMenu *sentSubmenu = submenu.firstObject;
-
- expect(add).to(haveCount(1));
- expect(submenu).to(haveCount(1));
- expect(sentCommand.cmdIcon.value).to(beNil());
- expect(sentSubmenu.menuIcon.value).to(beNil());
- });
- });
- });
-
- describe(@"updating when a menu already exists with dynamic updates on", ^{
- beforeEach(^{
- testManager.dynamicMenuUpdatesMode = SDLDynamicMenuUpdatesModeForceOn;
- OCMStub([mockFileManager uploadArtworks:[OCMArg any] completionHandler:[OCMArg invokeBlock]]);
- });
-
- it(@"should send deletes first", ^{
- testManager.menuCells = @[textOnlyCell];
- [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES];
- [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES];
-
- testManager.menuCells = @[textAndImageCell];
- [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES];
- [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES];
-
- NSPredicate *deleteCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass:%@", [SDLDeleteCommand class]];
- NSArray *deletes = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:deleteCommandPredicate];
-
- NSPredicate *addCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass:%@", [SDLAddCommand class]];
- NSArray *adds = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:addCommandPredicate];
-
- expect(deletes).to(haveCount(1));
- expect(adds).to(haveCount(2));
- });
-
- it(@"should send dynamic deletes first then dynamic adds case with 2 submenu cells", ^{
- testManager.menuCells = @[textOnlyCell, submenuCell, submenuImageCell];
- [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES];
- [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES];
- [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES];
-
- testManager.menuCells = @[submenuCell, submenuImageCell, textOnlyCell];
- [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES];
- [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES];
-
- NSPredicate *deleteCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass:%@", [SDLDeleteCommand class]];
- NSArray *deletes = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:deleteCommandPredicate];
-
- NSPredicate *addCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass:%@", [SDLAddCommand class]];
- NSArray *adds = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:addCommandPredicate];
-
- NSPredicate *addSubmenuPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass: %@", [SDLAddSubMenu class]];
- NSArray *submenu = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:addSubmenuPredicate];
-
- expect(deletes).to(haveCount(1));
- expect(adds).to(haveCount(5));
- expect(submenu).to(haveCount(2));
- });
-
- it(@"should send dynamic deletes first then dynamic adds when removing one submenu cell", ^{
- testManager.menuCells = @[textOnlyCell, textAndImageCell, submenuCell, submenuImageCell];
- [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES];
- [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES];
-
- testManager.menuCells = @[textOnlyCell, textAndImageCell, submenuCell];
- [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES];
- [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES];
-
- NSPredicate *deleteCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass:%@", [SDLDeleteCommand class]];
- NSArray *deletes = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:deleteCommandPredicate];
-
- NSPredicate *deleteSubCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass:%@", [SDLDeleteSubMenu class]];
- NSArray *subDeletes = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:deleteSubCommandPredicate];
-
- NSPredicate *addCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass:%@", [SDLAddCommand class]];
- NSArray *adds = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:addCommandPredicate];
-
- NSPredicate *addSubmenuPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass: %@", [SDLAddSubMenu class]];
- NSArray *submenu = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:addSubmenuPredicate];
-
- expect(deletes).to(haveCount(0));
- expect(subDeletes).to(haveCount(1));
- expect(adds).to(haveCount(5));
- expect(submenu).to(haveCount(2));
- });
-
- it(@"should send dynamic deletes first then dynamic adds when adding one new cell", ^{
- testManager.menuCells = @[textOnlyCell, textAndImageCell, submenuCell, submenuImageCell];
- [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES];
- [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES];
+ it(@"should should queue a menu configuration update", ^{
+ testManager.menuConfiguration = testMenuConfiguration;
- testManager.menuCells = @[textOnlyCell, textAndImageCell, submenuCell, submenuImageCell, textOnlyCell2];
- [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES];
- [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES];
-
- NSPredicate *deleteCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass:%@", [SDLDeleteCommand class]];
- NSArray *deletes = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:deleteCommandPredicate];
-
- NSPredicate *addCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass:%@", [SDLAddCommand class]];
- NSArray *adds = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:addCommandPredicate];
-
- NSPredicate *addSubmenuPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass: %@", [SDLAddSubMenu class]];
- NSArray *submenu = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:addSubmenuPredicate];
-
- expect(deletes).to(haveCount(0));
- expect(adds).to(haveCount(6));
- expect(submenu).to(haveCount(2));
- });
-
- it(@"should send dynamic deletes first then dynamic adds when cells stay the same", ^{
- testManager.menuCells = @[textOnlyCell, textOnlyCell2, textAndImageCell];
- [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES];
- [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES];
-
- testManager.menuCells = @[textOnlyCell, textOnlyCell2, textAndImageCell];
- [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES];
- [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES];
-
- NSPredicate *deleteCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass:%@", [SDLDeleteCommand class]];
- NSArray *deletes = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:deleteCommandPredicate];
+ expect(testManager.menuConfiguration).to(equal(testMenuConfiguration));
+ expect(testManager.transactionQueue.operationCount).to(equal(1));
+ });
- NSPredicate *addCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass:%@", [SDLAddCommand class]];
- NSArray *adds = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:addCommandPredicate];
+ context(@"when queueing a second task after the first", ^{
+ it(@"should cancel the first task", ^{
+ testManager.menuConfiguration = testMenuConfiguration;
+ testManager.menuConfiguration = [[SDLMenuConfiguration alloc] initWithMainMenuLayout:SDLMenuLayoutList defaultSubmenuLayout:SDLMenuLayoutList];
- expect(deletes).to(haveCount(0));
- expect(adds).to(haveCount(3));
+ expect(testManager.transactionQueue.operationCount).to(equal(2));
+ expect(testManager.transactionQueue.operations[0].isCancelled).to(beTrue());
+ });
+ });
});
});
- describe(@"updating when a menu already exists with dynamic updates off", ^{
+ describe(@"opening the menu", ^{
beforeEach(^{
- testManager.dynamicMenuUpdatesMode = SDLDynamicMenuUpdatesModeForceOff;
- OCMStub([mockFileManager uploadArtworks:[OCMArg any] completionHandler:[OCMArg invokeBlock]]);
- });
-
- it(@"should send deletes first", ^{
- testManager.menuCells = @[textOnlyCell];
- [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES];
- [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES];
-
- testManager.menuCells = @[textAndImageCell];
- [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES];
- [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES];
-
- NSPredicate *deleteCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass:%@", [SDLDeleteCommand class]];
- NSArray *deletes = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:deleteCommandPredicate];
-
- NSPredicate *addCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass:%@", [SDLAddCommand class]];
- NSArray *adds = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:addCommandPredicate];
-
- expect(deletes).to(haveCount(1));
- expect(adds).to(haveCount(2));
- });
-
- it(@"should deletes first case 2", ^{
- testManager.menuCells = @[textOnlyCell, textAndImageCell];
- [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES];
- [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES];
-
- testManager.menuCells = @[textAndImageCell, textOnlyCell];
- [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES];
- [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES];
-
- NSPredicate *deleteCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass:%@", [SDLDeleteCommand class]];
- NSArray *deletes = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:deleteCommandPredicate];
-
- NSPredicate *addCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass:%@", [SDLAddCommand class]];
- NSArray *adds = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:addCommandPredicate];
-
- expect(deletes).to(haveCount(2));
- expect(adds).to(haveCount(4));
+ testManager.currentHMILevel = SDLHMILevelFull;
+ testManager.currentSystemContext = SDLSystemContextMain;
});
- it(@"should send deletes first case 3", ^{
- testManager.menuCells = @[textOnlyCell, textAndImageCell, submenuCell, submenuImageCell];
- [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES];
- [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES];
+ context(@"when open menu RPC can be sent", ^{
+ beforeEach(^{
+ SDLVersion *oldVersion = [SDLVersion versionWithMajor:6 minor:0 patch:0];
+ id globalMock = OCMPartialMock([SDLGlobals sharedGlobals]);
+ OCMStub([globalMock rpcVersion]).andReturn(oldVersion);
+ });
- testManager.menuCells = @[textOnlyCell, textAndImageCell, submenuCell];
- [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES];
- [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES];
+ // should queue an open menu operation for the main menu
+ it(@"should queue an open menu operation for the main menu", ^{
+ BOOL canSendRPC = [testManager openMenu:nil];
- NSPredicate *deleteCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass:%@", [SDLDeleteCommand class]];
- NSArray *deletes = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:deleteCommandPredicate];
+ expect(testManager.transactionQueue.operationCount).to(equal(1));
+ expect(canSendRPC).to(equal(YES));
+ });
- NSPredicate *deleteSubCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass:%@", [SDLDeleteSubMenu class]];
- NSArray *subDeletes = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:deleteSubCommandPredicate];
+ // should queue an open menu operation for a submenu cell
+ it(@"should queue an open menu operation for a submenu cell", ^ {
+ testManager.menuCells = @[submenuCell];
+ BOOL canSendRPC = [testManager openMenu:submenuCell];
- NSPredicate *addCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass:%@", [SDLAddCommand class]];
- NSArray *adds = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:addCommandPredicate];
+ expect(testManager.transactionQueue.operationCount).to(equal(2));
+ expect(canSendRPC).to(equal(YES));
+ });
- NSPredicate *addSubmenuPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass: %@", [SDLAddSubMenu class]];
- NSArray *submenu = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:addSubmenuPredicate];
+ it(@"should cancel the first task if a second is queued", ^{
+ testManager.menuCells = @[submenuCell];
+ [testManager openMenu:nil];
+ [testManager openMenu:submenuCell];
- expect(deletes).to(haveCount(2));
- expect(subDeletes).to(haveCount(2));
- expect(adds).to(haveCount(9));
- expect(submenu).to(haveCount(3));
+ expect(testManager.transactionQueue.operationCount).to(equal(3));
+ expect(testManager.transactionQueue.operations[1].isCancelled).to(beTrue());
+ });
});
- it(@"should send deletes first case 4", ^{
- testManager.menuCells = @[textOnlyCell, textAndImageCell, submenuCell];
- [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES];
- [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES];
-
- testManager.menuCells = @[textOnlyCell, textAndImageCell, submenuCell, textOnlyCell2];
- [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES];
- [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES];
-
-
- NSPredicate *deleteCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass:%@", [SDLDeleteCommand class]];
- NSArray *deletes = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:deleteCommandPredicate];
-
- NSPredicate *addCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass:%@", [SDLAddCommand class]];
- NSArray *adds = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:addCommandPredicate];
+ context(@"when the open menu RPC can not be sent", ^{
+ it(@"should not queue an open menu operation when cell has no subcells", ^ {
+ BOOL canSendRPC = [testManager openMenu:textOnlyCell];
- NSPredicate *addSubmenuPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass: %@", [SDLAddSubMenu class]];
- NSArray *submenu = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:addSubmenuPredicate];
-
- NSPredicate *deleteSubCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass:%@", [SDLDeleteSubMenu class]];
- NSArray *subDeletes = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:deleteSubCommandPredicate];
+ expect(testManager.transactionQueue.operationCount).to(equal(0));
+ expect(canSendRPC).to(equal(NO));
+ });
- expect(deletes).to(haveCount(2));
- expect(adds).to(haveCount(9));
- expect(submenu).to(haveCount(2));
- expect(subDeletes).to(haveCount(1));
- });
+ it(@"should not queue an open menu operation when RPC version is not at least 6.0.0", ^ {
+ SDLVersion *oldVersion = [SDLVersion versionWithMajor:5 minor:0 patch:0];
+ id globalMock = OCMPartialMock([SDLGlobals sharedGlobals]);
+ OCMStub([globalMock rpcVersion]).andReturn(oldVersion);
- it(@"should deletes first case 5", ^{
- testManager.menuCells = @[textOnlyCell, textOnlyCell2, textAndImageCell];
- [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES];
- [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES];
+ BOOL canSendRPC = [testManager openMenu:submenuCell];
- testManager.menuCells = @[textOnlyCell, textOnlyCell2, textAndImageCell];
- [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES];
- [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES];
+ expect(testManager.transactionQueue.operationCount).to(equal(0));
+ expect(canSendRPC).to(equal(NO));
+ });
- NSPredicate *deleteCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass:%@", [SDLDeleteCommand class]];
- NSArray *deletes = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:deleteCommandPredicate];
+ it(@"should not queue an open menu operation when the cell is not in the menu array", ^ {
+ SDLVersion *oldVersion = [SDLVersion versionWithMajor:6 minor:0 patch:0];
+ id globalMock = OCMPartialMock([SDLGlobals sharedGlobals]);
+ OCMStub([globalMock rpcVersion]).andReturn(oldVersion);
- NSPredicate *addCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass:%@", [SDLAddCommand class]];
- NSArray *adds = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:addCommandPredicate];
+ BOOL canSendRPC = [testManager openMenu:submenuCell];
- expect(deletes).to(haveCount(3));
- expect(adds).to(haveCount(6));
+ expect(testManager.transactionQueue.operationCount).to(equal(0));
+ expect(canSendRPC).to(equal(NO));
+ });
});
});
});
@@ -725,8 +331,9 @@ describe(@"menu manager", ^{
cellCalled = YES;
testTriggerSource = triggerSource;
}];
+ cellWithHandler.cellId = 1;
- testManager.menuCells = @[cellWithHandler];
+ testManager.currentMenuCells = @[cellWithHandler];
});
it(@"should call the cell handler", ^{
@@ -748,10 +355,14 @@ describe(@"menu manager", ^{
cellCalled = YES;
testTriggerSource = triggerSource;
}];
+ cellWithHandler.cellId = 2;
SDLMenuCell *submenuCell = [[SDLMenuCell alloc] initWithTitle:@"Submenu" secondaryText:nil tertiaryText:nil icon:nil secondaryArtwork:nil submenuLayout:SDLMenuLayoutTiles subCells:@[cellWithHandler]];
+ submenuCell.cellId = 1;
+
+ cellWithHandler.parentCellId = 1;
- testManager.menuCells = @[submenuCell];
+ testManager.currentMenuCells = @[submenuCell];
});
it(@"should call the cell handler", ^{
@@ -767,154 +378,6 @@ describe(@"menu manager", ^{
});
});
});
-
- describe(@"updating the menu configuration", ^{
- beforeEach(^{
- testManager.currentHMILevel = SDLHMILevelFull;
- testManager.currentSystemContext = SDLSystemContextMain;
- });
-
- context(@"if the connection RPC version is less than 6.0.0", ^{
- beforeEach(^{
- [SDLGlobals sharedGlobals].rpcVersion = [SDLVersion versionWithString:@"5.0.0"];
- });
-
- it(@"should fail to send a SetGlobalProperties RPC update", ^{
- testManager.menuConfiguration = testMenuConfiguration;
-
- expect(testManager.menuConfiguration).toNot(equal(testMenuConfiguration));
- expect(mockConnectionManager.receivedRequests).to(haveCount(0));
- });
- });
-
- context(@"if the connection RPC version is greater than or equal to 6.0.0", ^{
- beforeEach(^{
- [SDLGlobals sharedGlobals].rpcVersion = [SDLVersion versionWithString:@"6.0.0"];
- });
-
- it(@"should send a SetGlobalProperties RPC update", ^{
- testManager.menuConfiguration = testMenuConfiguration;
-
- expect(testManager.menuConfiguration).to(equal(testMenuConfiguration));
- expect(mockConnectionManager.receivedRequests).to(haveCount(1));
-
- SDLSetGlobalPropertiesResponse *response = [[SDLSetGlobalPropertiesResponse alloc] init];
- response.success = @YES;
- [mockConnectionManager respondToLastRequestWithResponse:response];
-
- expect(testManager.menuConfiguration).to(equal(testMenuConfiguration));
- });
- });
- });
-
- context(@"when the manager stops", ^{
- beforeEach(^{
- [testManager stop];
- });
-
- it(@"should reset correctly", ^{
- expect(testManager.connectionManager).to(equal(mockConnectionManager));
- expect(testManager.fileManager).to(equal(mockFileManager));
-
- expect(testManager.menuCells).to(beEmpty());
- expect(testManager.currentHMILevel).to(beNil());
- expect(testManager.inProgressUpdate).to(beNil());
- expect(testManager.hasQueuedUpdate).to(beFalse());
- expect(testManager.waitingOnHMIUpdate).to(beFalse());
- expect(testManager.lastMenuId).to(equal(1));
- expect(testManager.oldMenuCells).to(beEmpty());
- expect(testManager.waitingUpdateMenuCells).to(beEmpty());
- expect(testManager.menuConfiguration).toNot(beNil());
- });
- });
-
- describe(@"ShowMenu RPC", ^{
- beforeEach(^{
- testManager.currentHMILevel = SDLHMILevelFull;
- testManager.currentSystemContext = SDLSystemContextMain;
- });
-
- context(@"when open menu RPC can be sent", ^{
- beforeEach(^{
- SDLVersion *oldVersion = [SDLVersion versionWithMajor:6 minor:0 patch:0];
- id globalMock = OCMPartialMock([SDLGlobals sharedGlobals]);
- OCMStub([globalMock rpcVersion]).andReturn(oldVersion);
- });
-
- it(@"should send showAppMenu RPC", ^{
- BOOL canSendRPC = [testManager openMenu];
-
- NSPredicate *showMenu = [NSPredicate predicateWithFormat:@"self isMemberOfClass: %@", [SDLShowAppMenu class]];
- NSArray *openMenu = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:showMenu];
-
- expect(mockConnectionManager.receivedRequests).toNot(beEmpty());
- expect(openMenu).to(haveCount(1));
- expect(canSendRPC).to(equal(YES));
- });
-
- it(@"should send showAppMenu RPC with cellID", ^ {
- testManager.menuCells = @[submenuCell];
- [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES];
- [mockConnectionManager respondToLastMultipleRequestsWithSuccess:YES];
-
- BOOL canSendRPC = [testManager openSubmenu:submenuCell];
-
- NSPredicate *addSubmenuPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass: %@", [SDLShowAppMenu class]];
- NSArray *openMenu = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:addSubmenuPredicate];
-
- expect(mockConnectionManager.receivedRequests).toNot(beEmpty());
- expect(openMenu).to(haveCount(1));
- expect(canSendRPC).to(equal(YES));
- });
- });
-
- context(@"when open menu RPC can not be sent", ^{
- it(@"should not send a showAppMenu RPC when cell has no subcells", ^ {
- BOOL canSendRPC = [testManager openSubmenu:textOnlyCell];
-
- NSPredicate *addSubmenuPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass: %@", [SDLShowAppMenu class]];
- NSArray *openMenu = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:addSubmenuPredicate];
-
- expect(mockConnectionManager.receivedRequests).to(beEmpty());
- expect(openMenu).to(haveCount(0));
- expect(canSendRPC).to(equal(NO));
- });
-
- it(@"should not send a showAppMenu RPC when RPC verison is not at least 6.0.0", ^ {
- SDLVersion *oldVersion = [SDLVersion versionWithMajor:5 minor:0 patch:0];
- id globalMock = OCMPartialMock([SDLGlobals sharedGlobals]);
- OCMStub([globalMock rpcVersion]).andReturn(oldVersion);
-
- BOOL canSendRPC = [testManager openSubmenu:submenuCell];
-
- NSPredicate *addSubmenuPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass: %@", [SDLShowAppMenu class]];
- NSArray *openMenu = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:addSubmenuPredicate];
-
- expect(mockConnectionManager.receivedRequests).to(beEmpty());
- expect(openMenu).to(haveCount(0));
- expect(canSendRPC).to(equal(NO));
- });
-
- it(@"should not send a showAppMenu RPC when the cell is not in the menu array", ^ {
- SDLVersion *oldVersion = [SDLVersion versionWithMajor:6 minor:0 patch:0];
- id globalMock = OCMPartialMock([SDLGlobals sharedGlobals]);
- OCMStub([globalMock rpcVersion]).andReturn(oldVersion);
-
- BOOL canSendRPC = [testManager openSubmenu:submenuCell];
-
- NSPredicate *addSubmenuPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass: %@", [SDLShowAppMenu class]];
- NSArray *openMenu = [[mockConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:addSubmenuPredicate];
-
- expect(mockConnectionManager.receivedRequests).to(beEmpty());
- expect(openMenu).to(haveCount(0));
- expect(canSendRPC).to(equal(NO));
- });
- });
- });
-
- afterEach(^{
- testManager = nil;
- });
});
QuickSpecEnd
diff --git a/SmartDeviceLinkTests/DevAPISpecs/SDLMenuReplaceOperationSpec.m b/SmartDeviceLinkTests/DevAPISpecs/SDLMenuReplaceOperationSpec.m
new file mode 100644
index 000000000..bb3c662bb
--- /dev/null
+++ b/SmartDeviceLinkTests/DevAPISpecs/SDLMenuReplaceOperationSpec.m
@@ -0,0 +1,745 @@
+//
+// SDLMenuReplaceOperationSpec.m
+// SmartDeviceLinkTests
+//
+// Created by Joel Fischer on 2/16/21.
+// Copyright © 2021 smartdevicelink. All rights reserved.
+//
+
+#import <Nimble/Nimble.h>
+#import <OCMock/OCMock.h>
+#import <Quick/Quick.h>
+
+#import <SmartDeviceLink/SmartDeviceLink.h>
+#import "SDLGlobals.h"
+#import "SDLMenuReplaceOperation.h"
+#import "SDLMenuManagerPrivateConstants.h"
+#import "SDLMenuReplaceUtilities.h"
+#import "TestConnectionManager.h"
+
+@interface SDLMenuCell ()
+
+@property (assign, nonatomic) UInt32 parentCellId;
+@property (assign, nonatomic) UInt32 cellId;
+@property (strong, nonatomic, readwrite) NSString *uniqueTitle;
+
+@property (copy, nonatomic, readwrite) NSString *title;
+@property (strong, nonatomic, readwrite, nullable) SDLArtwork *icon;
+@property (copy, nonatomic, readwrite, nullable) NSArray<NSString *> *voiceCommands;
+@property (copy, nonatomic, readwrite, nullable) NSString *secondaryText;
+@property (copy, nonatomic, readwrite, nullable) NSString *tertiaryText;
+@property (strong, nonatomic, readwrite, nullable) SDLArtwork *secondaryArtwork;
+@property (copy, nonatomic, readwrite, nullable) NSArray<SDLMenuCell *> *subCells;
+@property (copy, nonatomic, readwrite, nullable) SDLMenuCellSelectionHandler handler;
+
+@end
+
+QuickSpecBegin(SDLMenuReplaceOperationSpec)
+
+describe(@"a menu replace operation", ^{
+ __block SDLMenuReplaceOperation *testOp = nil;
+
+ __block TestConnectionManager *testConnectionManager = nil;
+ __block SDLFileManager *testFileManager = nil;
+ __block SDLMenuConfiguration *testMenuConfiguration = nil;
+ __block NSArray<SDLMenuCell *> *testCurrentMenu = nil;
+ __block NSArray<SDLMenuCell *> *testNewMenu = nil;
+
+ SDLTextField *commandSecondaryTextField = [[SDLTextField alloc] initWithName:SDLTextFieldNameMenuCommandSecondaryText characterSet:SDLCharacterSetUtf8 width:200 rows:1];
+ SDLTextField *commandTertiaryTextField = [[SDLTextField alloc] initWithName:SDLTextFieldNameMenuCommandTertiaryText characterSet:SDLCharacterSetUtf8 width:200 rows:1];
+ SDLTextField *submenuSecondaryTextField = [[SDLTextField alloc] initWithName:SDLTextFieldNameMenuSubMenuSecondaryText characterSet:SDLCharacterSetUtf8 width:200 rows:1];
+ SDLTextField *submenuTertiaryTextField = [[SDLTextField alloc] initWithName:SDLTextFieldNameMenuSubMenuTertiaryText characterSet:SDLCharacterSetUtf8 width:200 rows:1];
+ SDLImageField *commandImageField = [[SDLImageField alloc] initWithName:SDLImageFieldNameCommandIcon imageTypeSupported:@[SDLFileTypePNG] imageResolution:nil];
+ SDLImageField *submenuImageField = [[SDLImageField alloc] initWithName:SDLImageFieldNameSubMenuIcon imageTypeSupported:@[SDLFileTypePNG] imageResolution:nil];
+ __block SDLWindowCapability *testWindowCapability = [[SDLWindowCapability alloc] initWithWindowID:@0 textFields:@[commandSecondaryTextField, commandTertiaryTextField, submenuSecondaryTextField, submenuTertiaryTextField] imageFields:@[commandImageField, submenuImageField] imageTypeSupported:nil templatesAvailable:nil numCustomPresetsAvailable:nil buttonCapabilities:nil softButtonCapabilities:nil menuLayoutsAvailable:nil dynamicUpdateCapabilities:nil keyboardCapabilities:nil];
+ __block SDLWindowCapability *testTitleOnlyWindowCapability = [[SDLWindowCapability alloc] initWithWindowID:@0 textFields:@[] imageFields:@[commandImageField, submenuImageField] imageTypeSupported:nil templatesAvailable:nil numCustomPresetsAvailable:nil buttonCapabilities:nil softButtonCapabilities:nil menuLayoutsAvailable:nil dynamicUpdateCapabilities:nil keyboardCapabilities:nil];
+
+ __block SDLArtwork *testArtwork = nil;
+ __block SDLArtwork *testArtwork2 = nil;
+ __block SDLArtwork *testArtwork3 = nil;
+
+ __block SDLMenuCell *textOnlyCell = nil;
+ __block SDLMenuCell *textOnlyCell2 = nil;
+ __block SDLMenuCell *textAndImageCell = nil;
+ __block SDLMenuCell *submenuCell = nil;
+ __block SDLMenuCell *submenuCellReversed = nil;
+ __block SDLMenuCell *submenuImageCell = nil;
+
+ __block SDLAddCommandResponse *addCommandSuccessResponse = nil;
+ __block SDLAddSubMenuResponse *addSubMenuSuccessResponse = nil;
+ __block SDLDeleteCommandResponse *deleteCommandSuccessResponse = nil;
+ __block SDLDeleteSubMenuResponse *deleteSubMenuSuccessResponse = nil;
+
+ __block NSMutableArray<SDLMenuCell *> *basicCellArray = [NSMutableArray array];
+
+ __block NSArray<SDLMenuCell *> *resultMenuCells = nil;
+ __block NSError *resultError = nil;
+ __block SDLCurrentMenuUpdatedBlock testCurrentMenuUpdatedBlock = nil;
+
+ __block SDLMenuReplaceUtilities *mockReplaceUtilities = nil;
+
+ beforeSuite(^{
+ for (int i = 0; i < 50; i++) {
+ NSString *cellTitle = [NSString stringWithFormat:@"Cell %@", @(i)];
+ [basicCellArray addObject:[[SDLMenuCell alloc] initWithTitle:cellTitle secondaryText:nil tertiaryText:nil icon:nil secondaryArtwork:nil voiceCommands:@[cellTitle] handler:^(SDLTriggerSource _Nonnull triggerSource) {
+ NSLog(@"%@ pressed", cellTitle);
+ }]];
+ }
+ });
+
+ beforeEach(^{
+ [SDLGlobals sharedGlobals].rpcVersion = [SDLVersion versionWithMajor:7 minor:1 patch:0];
+
+ testArtwork = [[SDLArtwork alloc] initWithData:[@"Test data" dataUsingEncoding:NSUTF8StringEncoding] name:@"some artwork name" fileExtension:@"png" persistent:NO];
+ testArtwork2 = [[SDLArtwork alloc] initWithData:[@"Test data 2" dataUsingEncoding:NSUTF8StringEncoding] name:@"some artwork name 2" fileExtension:@"png" persistent:NO];
+ testArtwork3 = [[SDLArtwork alloc] initWithData:[@"Test data 3" dataUsingEncoding:NSUTF8StringEncoding] name:@"some artwork name" fileExtension:@"png" persistent:NO];
+ testArtwork3.overwrite = YES;
+
+ textOnlyCell = [[SDLMenuCell alloc] initWithTitle:@"Test 1" secondaryText:nil tertiaryText:nil icon:nil secondaryArtwork:nil voiceCommands:nil handler:^(SDLTriggerSource _Nonnull triggerSource) {}];
+ textOnlyCell2 = [[SDLMenuCell alloc] initWithTitle:@"Test 5" secondaryText:nil tertiaryText:nil icon:nil secondaryArtwork:nil voiceCommands:nil handler:^(SDLTriggerSource _Nonnull triggerSource) {}];
+ textAndImageCell = [[SDLMenuCell alloc] initWithTitle:@"Test 2" secondaryText:nil tertiaryText:nil icon:testArtwork secondaryArtwork:nil voiceCommands:nil handler:^(SDLTriggerSource _Nonnull triggerSource) {}];
+ submenuCell = [[SDLMenuCell alloc] initWithTitle:@"Cell with Subcells" secondaryText:nil tertiaryText:nil icon:nil secondaryArtwork:nil submenuLayout:nil subCells:basicCellArray];
+ submenuCellReversed = [[SDLMenuCell alloc] initWithTitle:@"Cell with Subcells" secondaryText:nil tertiaryText:nil icon:nil secondaryArtwork:nil submenuLayout:nil subCells:basicCellArray.reverseObjectEnumerator.allObjects];
+ submenuImageCell = [[SDLMenuCell alloc] initWithTitle:@"Cell with Image and Subcell" secondaryText:nil tertiaryText:nil icon:testArtwork2 secondaryArtwork:nil submenuLayout:SDLMenuLayoutTiles subCells:@[textOnlyCell]];
+
+ addCommandSuccessResponse = [[SDLAddCommandResponse alloc] init];
+ addCommandSuccessResponse.success = @YES;
+ addCommandSuccessResponse.resultCode = SDLResultSuccess;
+ addSubMenuSuccessResponse = [[SDLAddSubMenuResponse alloc] init];
+ addSubMenuSuccessResponse.success = @YES;
+ addSubMenuSuccessResponse.resultCode = SDLResultSuccess;
+ deleteCommandSuccessResponse = [[SDLDeleteCommandResponse alloc] init];
+ deleteCommandSuccessResponse.success = @YES;
+ deleteCommandSuccessResponse.resultCode = SDLResultSuccess;
+ deleteSubMenuSuccessResponse = [[SDLDeleteSubMenuResponse alloc] init];
+ deleteSubMenuSuccessResponse.success = @YES;
+ deleteSubMenuSuccessResponse.resultCode = SDLResultSuccess;
+
+ testOp = nil;
+ testConnectionManager = [[TestConnectionManager alloc] init];
+ testFileManager = OCMClassMock([SDLFileManager class]);
+
+ testMenuConfiguration = [[SDLMenuConfiguration alloc] initWithMainMenuLayout:SDLMenuLayoutList defaultSubmenuLayout:SDLMenuLayoutList];
+ testCurrentMenu = @[];
+ testNewMenu = nil;
+
+ resultMenuCells = nil;
+ resultError = nil;
+ testCurrentMenuUpdatedBlock = ^(NSArray<SDLMenuCell *> *currentMenuCells, NSError *error) {
+ resultMenuCells = currentMenuCells;
+ resultError = error;
+ };
+
+ mockReplaceUtilities = OCMClassMock([SDLMenuReplaceUtilities class]);
+ });
+
+ context(@"sending initial batch of cells", ^{
+ context(@"when setting no cells", ^{
+ it(@"should finish without doing anything", ^{
+ testOp = [[SDLMenuReplaceOperation alloc] initWithConnectionManager:testConnectionManager fileManager:testFileManager windowCapability:testWindowCapability menuConfiguration:testMenuConfiguration currentMenu:testCurrentMenu updatedMenu:testNewMenu compatibilityModeEnabled:YES currentMenuUpdatedHandler:testCurrentMenuUpdatedBlock];
+ [testOp start];
+
+ expect(testConnectionManager.receivedRequests).to(beEmpty());
+ expect(testOp.isFinished).to(beTrue());
+ expect(resultError).to(beNil());
+ expect(resultMenuCells).to(beEmpty());
+ });
+ });
+
+ context(@"when starting while cancelled", ^{
+ it(@"should finish without doing anything", ^{
+ testOp = [[SDLMenuReplaceOperation alloc] initWithConnectionManager:testConnectionManager fileManager:testFileManager windowCapability:testWindowCapability menuConfiguration:testMenuConfiguration currentMenu:testCurrentMenu updatedMenu:testNewMenu compatibilityModeEnabled:YES currentMenuUpdatedHandler:testCurrentMenuUpdatedBlock];
+ [testOp cancel];
+ [testOp start];
+
+ expect(testConnectionManager.receivedRequests).to(beEmpty());
+ expect(testOp.isFinished).to(beTrue());
+ expect(resultError).to(beNil());
+ expect(resultMenuCells).to(beNil());
+ });
+ });
+
+ context(@"when uploading a text-only cell", ^{
+ beforeEach(^{
+ testNewMenu = @[textOnlyCell];
+ OCMStub([testFileManager fileNeedsUpload:[OCMArg any]]).andReturn(NO);
+ });
+
+ it(@"should properly send the RPCs and finish the operation", ^{
+ testOp = [[SDLMenuReplaceOperation alloc] initWithConnectionManager:testConnectionManager fileManager:testFileManager windowCapability:testWindowCapability menuConfiguration:testMenuConfiguration currentMenu:testCurrentMenu updatedMenu:testNewMenu compatibilityModeEnabled:YES currentMenuUpdatedHandler:testCurrentMenuUpdatedBlock];
+ [testOp start];
+
+ OCMReject([testFileManager uploadArtworks:[OCMArg any] progressHandler:[OCMArg any] completionHandler:[OCMArg any]]);
+
+ NSPredicate *deleteCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass: %@", [SDLDeleteCommand class]];
+ NSArray *deletes = [[testConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:deleteCommandPredicate];
+ expect(deletes).to(beEmpty());
+
+ NSPredicate *addCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass: %@", [SDLAddCommand class]];
+ NSArray *add = [[testConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:addCommandPredicate];
+ expect(add).to(haveCount(1));
+
+ [testConnectionManager respondToLastRequestWithResponse:addCommandSuccessResponse];
+ [testConnectionManager respondToLastMultipleRequestsWithSuccess:YES];
+
+ expect(testOp.isFinished).to(beTrue());
+ expect(resultError).to(beNil());
+ expect(resultMenuCells).to(haveCount(1));
+ });
+ });
+
+ context(@"when uploading text and image cell", ^{
+ beforeEach(^{
+ testNewMenu = @[textAndImageCell];
+
+ OCMStub([testFileManager uploadArtworks:[OCMArg any] progressHandler:([OCMArg invokeBlockWithArgs:textAndImageCell.icon.name, @1.0, [NSNull null], nil]) completionHandler:([OCMArg invokeBlockWithArgs: @[textAndImageCell.icon.name], [NSNull null], nil])]);
+ });
+
+ // when the image is already on the head unit
+ context(@"when the image is already on the head unit", ^{
+ beforeEach(^{
+ OCMStub([testFileManager hasUploadedFile:[OCMArg isNotNil]]).andReturn(YES);
+ OCMStub([testFileManager fileNeedsUpload:[OCMArg isNotNil]]).andReturn(NO);
+ });
+
+ it(@"should properly update an image cell", ^{
+ OCMReject([testFileManager uploadArtworks:[OCMArg any] progressHandler:[OCMArg any] completionHandler:[OCMArg any]]);
+
+
+ testOp = [[SDLMenuReplaceOperation alloc] initWithConnectionManager:testConnectionManager fileManager:testFileManager windowCapability:testWindowCapability menuConfiguration:testMenuConfiguration currentMenu:testCurrentMenu updatedMenu:testNewMenu compatibilityModeEnabled:YES currentMenuUpdatedHandler:testCurrentMenuUpdatedBlock];
+ [testOp start];
+
+ NSPredicate *addCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass: %@", [SDLAddCommand class]];
+ NSArray *add = [[testConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:addCommandPredicate];
+ SDLAddCommand *sentCommand = add.firstObject;
+
+ expect(add).to(haveCount(1));
+ expect(sentCommand.cmdIcon.value).to(equal(testArtwork.name));
+
+ [testConnectionManager respondToLastRequestWithResponse:addCommandSuccessResponse];
+ [testConnectionManager respondToLastMultipleRequestsWithSuccess:YES];
+
+ expect(testOp.isFinished).to(beTrue());
+ expect(resultError).to(beNil());
+ expect(resultMenuCells).to(haveCount(1));
+ });
+ });
+
+ // when the image is not on the head unit
+ context(@"when the image is not on the head unit", ^{
+ beforeEach(^{
+ OCMStub([testFileManager hasUploadedFile:[OCMArg isNotNil]]).andReturn(NO);
+ OCMStub([testFileManager fileNeedsUpload:[OCMArg isNotNil]]).andReturn(YES);
+ });
+
+ it(@"should attempt to upload artworks then send the add", ^{
+ testOp = [[SDLMenuReplaceOperation alloc] initWithConnectionManager:testConnectionManager fileManager:testFileManager windowCapability:testWindowCapability menuConfiguration:testMenuConfiguration currentMenu:testCurrentMenu updatedMenu:testNewMenu compatibilityModeEnabled:YES currentMenuUpdatedHandler:testCurrentMenuUpdatedBlock];
+ [testOp start];
+
+ OCMVerify([testFileManager uploadArtworks:[OCMArg any] progressHandler:[OCMArg any] completionHandler:[OCMArg any]]);
+
+ NSPredicate *deleteCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass: %@", [SDLDeleteCommand class]];
+ NSArray *deletesArray = [[testConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:deleteCommandPredicate];
+ expect(deletesArray).to(beEmpty());
+
+ NSPredicate *addCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass: %@", [SDLAddCommand class]];
+ NSArray *addsArray = [[testConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:addCommandPredicate];
+ expect(addsArray).toNot(beEmpty());
+
+ [testConnectionManager respondToLastRequestWithResponse:addCommandSuccessResponse];
+ [testConnectionManager respondToLastMultipleRequestsWithSuccess:YES];
+
+ expect(testOp.isFinished).to(beTrue());
+ expect(resultError).to(beNil());
+ expect(resultMenuCells).to(haveCount(1));
+ });
+ });
+ });
+
+ context(@"when uploading a cell with subcells", ^{
+ beforeEach(^{
+ testNewMenu = @[submenuCell];
+ });
+
+ it(@"should send an appropriate number of AddSubmenu and AddCommandRequests", ^{
+ testOp = [[SDLMenuReplaceOperation alloc] initWithConnectionManager:testConnectionManager fileManager:testFileManager windowCapability:testWindowCapability menuConfiguration:testMenuConfiguration currentMenu:testCurrentMenu updatedMenu:testNewMenu compatibilityModeEnabled:YES currentMenuUpdatedHandler:testCurrentMenuUpdatedBlock];
+ [testOp start];
+
+ [testConnectionManager respondToLastRequestWithResponse:addSubMenuSuccessResponse];
+ [testConnectionManager respondToLastMultipleRequestsWithSuccess:YES];
+
+ NSPredicate *addCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass: %@", [SDLAddCommand class]];
+ NSArray *adds = [[testConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:addCommandPredicate];
+
+ NSPredicate *submenuCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass: %@", [SDLAddSubMenu class]];
+ NSArray *submenus = [[testConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:submenuCommandPredicate];
+
+ expect(adds).to(haveCount(50));
+ expect(submenus).to(haveCount(1));
+
+ [testConnectionManager respondToRequestWithResponse:addCommandSuccessResponse requestNumber:1 error:nil];
+ [testConnectionManager respondToRequestWithResponse:addCommandSuccessResponse requestNumber:2 error:nil];
+ [testConnectionManager respondToLastMultipleRequestsWithSuccess:YES];
+
+ expect(testOp.isFinished).to(beTrue());
+ expect(resultError).to(beNil());
+ expect(resultMenuCells).to(haveCount(1));
+ expect(resultMenuCells[0].subCells).to(haveCount(2));
+ });
+ });
+ });
+
+ context(@"updating a menu without dynamic updates", ^{
+ describe(@"basic cell updates", ^{
+ context(@"adding a text cell", ^{
+ beforeEach(^{
+ testCurrentMenu = [[NSArray alloc] initWithArray:@[textOnlyCell] copyItems:YES];
+ [SDLMenuReplaceUtilities addIdsToMenuCells:testCurrentMenu parentId:ParentIdNotFound];
+
+ testNewMenu = [[NSArray alloc] initWithArray:@[textOnlyCell, textOnlyCell2] copyItems:YES];
+ });
+
+ it(@"should send a delete and two adds", ^{
+ testOp = [[SDLMenuReplaceOperation alloc] initWithConnectionManager:testConnectionManager fileManager:testFileManager windowCapability:testWindowCapability menuConfiguration:testMenuConfiguration currentMenu:testCurrentMenu updatedMenu:testNewMenu compatibilityModeEnabled:YES currentMenuUpdatedHandler:testCurrentMenuUpdatedBlock];
+ [testOp start];
+
+ [testConnectionManager respondToLastRequestWithResponse:deleteCommandSuccessResponse];
+ [testConnectionManager respondToLastMultipleRequestsWithSuccess:YES];
+
+ [testConnectionManager respondToRequestWithResponse:addCommandSuccessResponse requestNumber:1 error:nil];
+ [testConnectionManager respondToRequestWithResponse:addCommandSuccessResponse requestNumber:2 error:nil];
+ [testConnectionManager respondToLastMultipleRequestsWithSuccess:YES];
+
+ NSPredicate *deleteCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass:%@", [SDLDeleteCommand class]];
+ NSArray *deletes = [[testConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:deleteCommandPredicate];
+
+ NSPredicate *addCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass:%@", [SDLAddCommand class]];
+ NSArray *adds = [[testConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:addCommandPredicate];
+
+ expect(deletes).to(haveCount(1));
+ expect(adds).to(haveCount(2));
+
+ expect(testOp.isFinished).to(beTrue());
+ expect(resultError).to(beNil());
+ expect(resultMenuCells).to(haveCount(2));
+ expect(resultMenuCells[0]).to(equal(textOnlyCell));
+ expect(resultMenuCells[1]).to(equal(textOnlyCell2));
+ });
+ });
+
+ context(@"when all cells remain the same", ^{
+ beforeEach(^{
+ testCurrentMenu = [[NSArray alloc] initWithArray:@[textOnlyCell, textOnlyCell2, textAndImageCell] copyItems:YES];
+ [SDLMenuReplaceUtilities addIdsToMenuCells:testCurrentMenu parentId:ParentIdNotFound];
+
+ testNewMenu = [[NSArray alloc] initWithArray:@[textOnlyCell, textOnlyCell2, textAndImageCell] copyItems:YES];
+ });
+
+ it(@"should delete all cells and add the new ones", ^{
+ testOp = [[SDLMenuReplaceOperation alloc] initWithConnectionManager:testConnectionManager fileManager:testFileManager windowCapability:testWindowCapability menuConfiguration:testMenuConfiguration currentMenu:testCurrentMenu updatedMenu:testNewMenu compatibilityModeEnabled:YES currentMenuUpdatedHandler:testCurrentMenuUpdatedBlock];
+ [testOp start];
+
+ [testConnectionManager respondToRequestWithResponse:deleteCommandSuccessResponse requestNumber:0 error:nil];
+ [testConnectionManager respondToRequestWithResponse:deleteCommandSuccessResponse requestNumber:1 error:nil];
+ [testConnectionManager respondToRequestWithResponse:deleteCommandSuccessResponse requestNumber:2 error:nil];
+ [testConnectionManager respondToLastMultipleRequestsWithSuccess:YES];
+
+ [testConnectionManager respondToRequestWithResponse:addCommandSuccessResponse requestNumber:3 error:nil];
+ [testConnectionManager respondToRequestWithResponse:addCommandSuccessResponse requestNumber:4 error:nil];
+ [testConnectionManager respondToRequestWithResponse:addCommandSuccessResponse requestNumber:5 error:nil];
+ [testConnectionManager respondToLastMultipleRequestsWithSuccess:YES];
+
+ NSPredicate *deleteCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass:%@", [SDLDeleteCommand class]];
+ NSArray *deletes = [[testConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:deleteCommandPredicate];
+
+ NSPredicate *addCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass:%@", [SDLAddCommand class]];
+ NSArray *adds = [[testConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:addCommandPredicate];
+
+ expect(deletes).to(haveCount(3));
+ expect(adds).to(haveCount(3));
+
+ expect(testOp.isFinished).to(beTrue());
+ expect(resultError).to(beNil());
+ expect(resultMenuCells).to(haveCount(3));
+ });
+ });
+ });
+
+ describe(@"unique cell updates", ^{
+ context(@"with cell uniqueness", ^{
+ beforeEach(^{
+ [SDLGlobals sharedGlobals].rpcVersion = [SDLVersion versionWithMajor:7 minor:1 patch:0];
+ });
+
+ context(@"when cells have the same title but are unique", ^{
+ beforeEach(^{
+ testCurrentMenu = @[];
+
+ SDLMenuCell *textOnlyCellDupe = [textOnlyCell copy];
+ textOnlyCellDupe.secondaryText = @"Secondary Text";
+
+ testNewMenu = @[textOnlyCell, textOnlyCellDupe];
+ });
+
+ it(@"should send the cells unchanged", ^{
+ testOp = [[SDLMenuReplaceOperation alloc] initWithConnectionManager:testConnectionManager fileManager:testFileManager windowCapability:testWindowCapability menuConfiguration:testMenuConfiguration currentMenu:testCurrentMenu updatedMenu:testNewMenu compatibilityModeEnabled:NO currentMenuUpdatedHandler:testCurrentMenuUpdatedBlock];
+ [testOp start];
+
+ NSPredicate *deleteCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass:%@", [SDLDeleteCommand class]];
+ NSArray *deletes = [[testConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:deleteCommandPredicate];
+
+ NSPredicate *addCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass:%@", [SDLAddCommand class]];
+ NSArray *adds = [[testConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:addCommandPredicate];
+
+ expect(deletes).to(haveCount(0));
+ expect(adds).to(haveCount(2));
+
+ [testConnectionManager respondToRequestWithResponse:addCommandSuccessResponse requestNumber:0 error:nil];
+ [testConnectionManager respondToRequestWithResponse:addCommandSuccessResponse requestNumber:1 error:nil];
+ [testConnectionManager respondToLastMultipleRequestsWithSuccess:YES];
+
+ expect(testOp.isFinished).to(beTrue());
+ expect(resultError).to(beNil());
+ expect(resultMenuCells).to(haveCount(2));
+ expect(resultMenuCells[0].uniqueTitle).to(equal(textOnlyCell.uniqueTitle));
+ expect(resultMenuCells[0].secondaryText).to(beNil());
+ expect(resultMenuCells[1].uniqueTitle).to(equal(textOnlyCell.uniqueTitle));
+ expect(resultMenuCells[1].secondaryText).toNot(beNil());
+ });
+ });
+
+ context(@"when cells are unique but are identical when stripped", ^{
+ beforeEach(^{
+ testCurrentMenu = @[];
+
+ SDLMenuCell *textOnlyCellDupe = [textOnlyCell copy];
+ textOnlyCellDupe.secondaryText = @"Secondary Text";
+
+ testNewMenu = [[NSArray alloc] initWithArray:@[textOnlyCell, textOnlyCellDupe] copyItems:YES];
+ });
+
+ it(@"should change the second cell's title", ^{
+ testOp = [[SDLMenuReplaceOperation alloc] initWithConnectionManager:testConnectionManager fileManager:testFileManager windowCapability:testTitleOnlyWindowCapability menuConfiguration:testMenuConfiguration currentMenu:testCurrentMenu updatedMenu:testNewMenu compatibilityModeEnabled:NO currentMenuUpdatedHandler:testCurrentMenuUpdatedBlock];
+ [testOp start];
+
+ NSPredicate *deleteCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass:%@", [SDLDeleteCommand class]];
+ NSArray *deletes = [[testConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:deleteCommandPredicate];
+
+ NSPredicate *addCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass:%@", [SDLAddCommand class]];
+ NSArray *adds = [[testConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:addCommandPredicate];
+
+ expect(deletes).to(haveCount(0));
+ expect(adds).to(haveCount(2));
+
+ [testConnectionManager respondToRequestWithResponse:addCommandSuccessResponse requestNumber:0 error:nil];
+ [testConnectionManager respondToRequestWithResponse:addCommandSuccessResponse requestNumber:1 error:nil];
+ [testConnectionManager respondToLastMultipleRequestsWithSuccess:YES];
+
+ expect(testOp.isFinished).to(beTrue());
+ expect(resultError).to(beNil());
+ expect(resultMenuCells).to(haveCount(2));
+ expect(resultMenuCells[0].uniqueTitle).to(equal(textOnlyCell.uniqueTitle));
+ expect(resultMenuCells[0].secondaryText).to(beNil());
+ expect(resultMenuCells[1].uniqueTitle).toNot(equal(textOnlyCell.uniqueTitle));
+ expect(resultMenuCells[1].secondaryText).toNot(beNil());
+ });
+ });
+ });
+
+ context(@"without cell uniqueness", ^{
+ beforeEach(^{
+ [SDLGlobals sharedGlobals].rpcVersion = [SDLVersion versionWithMajor:7 minor:0 patch:0];
+ });
+
+ context(@"when cells have the same title but are unique", ^{
+ beforeEach(^{
+ testCurrentMenu = @[];
+
+ SDLMenuCell *textOnlyCellDupe = [textOnlyCell copy];
+ textOnlyCellDupe.secondaryText = @"Secondary Text";
+
+ testNewMenu = @[textOnlyCell, textOnlyCellDupe];
+ });
+
+ it(@"should change the second cell's title", ^{
+ testOp = [[SDLMenuReplaceOperation alloc] initWithConnectionManager:testConnectionManager fileManager:testFileManager windowCapability:testWindowCapability menuConfiguration:testMenuConfiguration currentMenu:testCurrentMenu updatedMenu:testNewMenu compatibilityModeEnabled:NO currentMenuUpdatedHandler:testCurrentMenuUpdatedBlock];
+ [testOp start];
+
+ NSPredicate *deleteCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass:%@", [SDLDeleteCommand class]];
+ NSArray *deletes = [[testConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:deleteCommandPredicate];
+
+ NSPredicate *addCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass:%@", [SDLAddCommand class]];
+ NSArray *adds = [[testConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:addCommandPredicate];
+
+ expect(deletes).to(haveCount(0));
+ expect(adds).to(haveCount(2));
+
+ [testConnectionManager respondToRequestWithResponse:addCommandSuccessResponse requestNumber:0 error:nil];
+ [testConnectionManager respondToRequestWithResponse:addCommandSuccessResponse requestNumber:1 error:nil];
+ [testConnectionManager respondToLastMultipleRequestsWithSuccess:YES];
+
+ expect(testOp.isFinished).to(beTrue());
+ expect(resultError).to(beNil());
+ expect(resultMenuCells).to(haveCount(2));
+ expect(resultMenuCells[0].uniqueTitle).to(equal(textOnlyCell.uniqueTitle));
+ expect(resultMenuCells[0].secondaryText).to(beNil());
+ expect(resultMenuCells[1].uniqueTitle).toNot(equal(textOnlyCell.uniqueTitle));
+ expect(resultMenuCells[1].secondaryText).toNot(beNil());
+ });
+ });
+
+ context(@"when cells are unique but are identical when stripped", ^{
+ beforeEach(^{
+ testCurrentMenu = @[];
+
+ SDLMenuCell *textOnlyCellDupe = [textOnlyCell copy];
+ textOnlyCellDupe.secondaryText = @"Secondary Text";
+
+ testNewMenu = [[NSArray alloc] initWithArray:@[textOnlyCell, textOnlyCellDupe] copyItems:YES];
+ });
+
+ it(@"should change the second cell's title", ^{
+ testOp = [[SDLMenuReplaceOperation alloc] initWithConnectionManager:testConnectionManager fileManager:testFileManager windowCapability:testTitleOnlyWindowCapability menuConfiguration:testMenuConfiguration currentMenu:testCurrentMenu updatedMenu:testNewMenu compatibilityModeEnabled:NO currentMenuUpdatedHandler:testCurrentMenuUpdatedBlock];
+ [testOp start];
+
+ NSPredicate *deleteCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass:%@", [SDLDeleteCommand class]];
+ NSArray *deletes = [[testConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:deleteCommandPredicate];
+
+ NSPredicate *addCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass:%@", [SDLAddCommand class]];
+ NSArray *adds = [[testConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:addCommandPredicate];
+
+ expect(deletes).to(haveCount(0));
+ expect(adds).to(haveCount(2));
+
+ [testConnectionManager respondToRequestWithResponse:addCommandSuccessResponse requestNumber:0 error:nil];
+ [testConnectionManager respondToRequestWithResponse:addCommandSuccessResponse requestNumber:1 error:nil];
+ [testConnectionManager respondToLastMultipleRequestsWithSuccess:YES];
+
+ expect(testOp.isFinished).to(beTrue());
+ expect(resultError).to(beNil());
+ expect(resultMenuCells).to(haveCount(2));
+ expect(resultMenuCells[0].uniqueTitle).to(equal(textOnlyCell.uniqueTitle));
+ expect(resultMenuCells[0].secondaryText).to(beNil());
+ expect(resultMenuCells[1].uniqueTitle).toNot(equal(textOnlyCell.uniqueTitle));
+ expect(resultMenuCells[1].secondaryText).toNot(beNil());
+ });
+ });
+ });
+ });
+ });
+
+ context(@"updating a menu with dynamic updates", ^{
+ context(@"adding a text cell", ^{
+ beforeEach(^{
+ testCurrentMenu = @[textOnlyCell];
+ testNewMenu = @[textOnlyCell, textOnlyCell2];
+ });
+
+ it(@"should only send an add", ^{
+ testOp = [[SDLMenuReplaceOperation alloc] initWithConnectionManager:testConnectionManager fileManager:testFileManager windowCapability:testWindowCapability menuConfiguration:testMenuConfiguration currentMenu:testCurrentMenu updatedMenu:testNewMenu compatibilityModeEnabled:NO currentMenuUpdatedHandler:testCurrentMenuUpdatedBlock];
+ [testOp start];
+
+ NSPredicate *deleteCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass:%@", [SDLDeleteCommand class]];
+ NSArray *deletes = [[testConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:deleteCommandPredicate];
+
+ NSPredicate *addCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass:%@", [SDLAddCommand class]];
+ NSArray *adds = [[testConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:addCommandPredicate];
+
+ expect(deletes).to(haveCount(0));
+ expect(adds).to(haveCount(1));
+
+ [testConnectionManager respondToLastRequestWithResponse:addCommandSuccessResponse];
+ [testConnectionManager respondToLastMultipleRequestsWithSuccess:YES];
+
+ expect(testOp.isFinished).to(beTrue());
+ expect(resultError).to(beNil());
+ expect(resultMenuCells).to(haveCount(2));
+ });
+ });
+
+ context(@"rearranging cells with subcells", ^{
+ beforeEach(^{
+ testCurrentMenu = [[NSArray alloc] initWithArray:@[textOnlyCell, submenuCell, submenuImageCell] copyItems:YES];
+ [SDLMenuReplaceUtilities addIdsToMenuCells:testCurrentMenu parentId:ParentIdNotFound];
+
+ testNewMenu = [[NSArray alloc] initWithArray:@[submenuCell, submenuImageCell, textOnlyCell] copyItems:YES];
+
+ OCMStub([testFileManager uploadArtworks:[OCMArg any] progressHandler:([OCMArg invokeBlockWithArgs:textAndImageCell.icon.name, @1.0, [NSNull null], nil]) completionHandler:([OCMArg invokeBlockWithArgs: @[textAndImageCell.icon.name], [NSNull null], nil])]);
+ });
+
+ it(@"should send dynamic deletes first then dynamic adds case with 2 submenu cells", ^{
+ testOp = [[SDLMenuReplaceOperation alloc] initWithConnectionManager:testConnectionManager fileManager:testFileManager windowCapability:testWindowCapability menuConfiguration:testMenuConfiguration currentMenu:testCurrentMenu updatedMenu:testNewMenu compatibilityModeEnabled:NO currentMenuUpdatedHandler:testCurrentMenuUpdatedBlock];
+ [testOp start];
+
+ // Delete textOnlyCell
+ [testConnectionManager respondToLastRequestWithResponse:deleteCommandSuccessResponse];
+ [testConnectionManager respondToLastMultipleRequestsWithSuccess:YES];
+ expect(testOp.currentMenu).toNot(contain(textOnlyCell));
+
+ // Add textOnlyCell
+ [testConnectionManager respondToLastRequestWithResponse:addCommandSuccessResponse];
+ [testConnectionManager respondToLastMultipleRequestsWithSuccess:YES];
+
+ NSPredicate *deleteCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass:%@", [SDLDeleteCommand class]];
+ NSArray *deletes = [[testConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:deleteCommandPredicate];
+
+ NSPredicate *addCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass:%@", [SDLAddCommand class]];
+ NSArray *adds = [[testConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:addCommandPredicate];
+
+ NSPredicate *addSubmenuPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass: %@", [SDLAddSubMenu class]];
+ NSArray *submenu = [[testConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:addSubmenuPredicate];
+
+ expect(deletes).to(haveCount(1));
+ expect(adds).to(haveCount(1));
+ expect(submenu).to(haveCount(0));
+
+ expect(testOp.isFinished).to(beTrue());
+ expect(resultError).to(beNil());
+ expect(resultMenuCells).to(haveCount(3));
+ });
+ });
+
+ context(@"rearranging cells and their subcells", ^{
+ beforeEach(^{
+ testCurrentMenu = [[NSArray alloc] initWithArray:@[textOnlyCell, textAndImageCell, submenuCell] copyItems:YES];
+ [SDLMenuReplaceUtilities addIdsToMenuCells:testCurrentMenu parentId:ParentIdNotFound];
+
+ testNewMenu = [[NSArray alloc] initWithArray:@[submenuCellReversed, textAndImageCell, textOnlyCell] copyItems:YES];
+
+ OCMStub([testFileManager uploadArtworks:[OCMArg any] progressHandler:([OCMArg invokeBlockWithArgs:textAndImageCell.icon.name, @1.0, [NSNull null], nil]) completionHandler:([OCMArg invokeBlockWithArgs: @[textAndImageCell.icon.name], [NSNull null], nil])]);
+ });
+
+ it(@"should sent the correct deletions and additions", ^{
+ testOp = [[SDLMenuReplaceOperation alloc] initWithConnectionManager:testConnectionManager fileManager:testFileManager windowCapability:testWindowCapability menuConfiguration:testMenuConfiguration currentMenu:testCurrentMenu updatedMenu:testNewMenu compatibilityModeEnabled:NO currentMenuUpdatedHandler:testCurrentMenuUpdatedBlock];
+ [testOp start];
+
+ // Delete textOnlyCell and submenuCell
+ expect(testConnectionManager.receivedRequests).to(haveCount(2));
+ expect(testConnectionManager.receivedRequests[0]).to(beAnInstanceOf(SDLDeleteCommand.class));
+ expect(testConnectionManager.receivedRequests[1]).to(beAnInstanceOf(SDLDeleteSubMenu.class));
+
+ [testConnectionManager respondToRequestWithResponse:deleteCommandSuccessResponse requestNumber:0 error:nil];
+ [testConnectionManager respondToRequestWithResponse:deleteSubMenuSuccessResponse requestNumber:1 error:nil];
+ [testConnectionManager respondToLastMultipleRequestsWithSuccess:YES];
+ expect(testOp.currentMenu).to(haveCount(1));
+
+ // Main Menu Add Command / Add Submenu
+ expect(testConnectionManager.receivedRequests).to(haveCount(4));
+
+ [testConnectionManager respondToRequestWithResponse:addSubMenuSuccessResponse requestNumber:2 error:nil];
+ [testConnectionManager respondToRequestWithResponse:addCommandSuccessResponse requestNumber:3 error:nil];
+ [testConnectionManager respondToLastMultipleRequestsWithSuccess:YES];
+ expect(testOp.currentMenu).to(haveCount(3));
+
+ NSPredicate *deleteCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass:%@", [SDLDeleteCommand class]];
+ NSArray *deletes = [[testConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:deleteCommandPredicate];
+
+ NSPredicate *deleteSubCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass:%@", [SDLDeleteSubMenu class]];
+ NSArray *subDeletes = [[testConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:deleteSubCommandPredicate];
+
+ NSPredicate *addCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass:%@", [SDLAddCommand class]];
+ NSArray *adds = [[testConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:addCommandPredicate];
+
+ NSPredicate *addSubmenuPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass: %@", [SDLAddSubMenu class]];
+ NSArray *submenu = [[testConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:addSubmenuPredicate];
+
+ // Submenu add commands sent
+ expect(deletes).to(haveCount(1));
+ expect(subDeletes).to(haveCount(1));
+ expect(adds).to(haveCount(51));
+ expect(submenu).to(haveCount(1));
+
+ // Respond to all 50 submenu add commands
+ for (NSUInteger i = 0; i < 50; i++) {
+ [testConnectionManager respondToRequestWithResponse:addCommandSuccessResponse requestNumber:(i + 4) error:nil];
+ }
+ [testConnectionManager respondToLastMultipleRequestsWithSuccess:YES];
+
+ expect(testOp.isFinished).to(beTrue());
+ expect(resultError).to(beNil());
+ expect(resultMenuCells).to(haveCount(3));
+ });
+ });
+
+ context(@"removing a cell with subcells", ^{
+ beforeEach(^{
+ testCurrentMenu = [[NSArray alloc] initWithArray:@[textOnlyCell, textAndImageCell, submenuCell, submenuImageCell] copyItems:YES];
+ [SDLMenuReplaceUtilities addIdsToMenuCells:testCurrentMenu parentId:ParentIdNotFound];
+
+ testNewMenu = [[NSArray alloc] initWithArray:@[textOnlyCell, textAndImageCell, submenuCell] copyItems:YES];
+
+ OCMStub([testFileManager uploadArtworks:[OCMArg any] progressHandler:([OCMArg invokeBlockWithArgs:textAndImageCell.icon.name, @1.0, [NSNull null], nil]) completionHandler:([OCMArg invokeBlockWithArgs: @[textAndImageCell.icon.name], [NSNull null], nil])]);
+ });
+
+ it(@"should send one deletion", ^{
+ testOp = [[SDLMenuReplaceOperation alloc] initWithConnectionManager:testConnectionManager fileManager:testFileManager windowCapability:testWindowCapability menuConfiguration:testMenuConfiguration currentMenu:testCurrentMenu updatedMenu:testNewMenu compatibilityModeEnabled:NO currentMenuUpdatedHandler:testCurrentMenuUpdatedBlock];
+ [testOp start];
+
+ // Delete submenuImageCell
+ [testConnectionManager respondToLastRequestWithResponse:deleteSubMenuSuccessResponse];
+ [testConnectionManager respondToLastMultipleRequestsWithSuccess:YES];
+ expect(testOp.currentMenu).toNot(contain(submenuImageCell));
+
+ NSPredicate *deleteCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass:%@", [SDLDeleteCommand class]];
+ NSArray *deletes = [[testConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:deleteCommandPredicate];
+
+ NSPredicate *deleteSubCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass:%@", [SDLDeleteSubMenu class]];
+ NSArray *subDeletes = [[testConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:deleteSubCommandPredicate];
+
+ NSPredicate *addCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass:%@", [SDLAddCommand class]];
+ NSArray *adds = [[testConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:addCommandPredicate];
+
+ NSPredicate *addSubmenuPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass: %@", [SDLAddSubMenu class]];
+ NSArray *submenu = [[testConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:addSubmenuPredicate];
+
+ expect(deletes).to(haveCount(0));
+ expect(subDeletes).to(haveCount(1));
+ expect(adds).to(haveCount(0));
+ expect(submenu).to(haveCount(0));
+
+ expect(testOp.isFinished).to(beTrue());
+ expect(resultError).to(beNil());
+ expect(resultMenuCells).to(haveCount(3));
+ });
+ });
+
+ context(@"when cells remain the same", ^{
+ __block BOOL secondHandlerCalled = NO;
+
+ beforeEach(^{
+ secondHandlerCalled = NO;
+
+ testCurrentMenu = [[NSArray alloc] initWithArray:@[textOnlyCell, textOnlyCell2, textAndImageCell] copyItems:YES];
+ [SDLMenuReplaceUtilities addIdsToMenuCells:testCurrentMenu parentId:ParentIdNotFound];
+
+ textOnlyCell.handler = ^(SDLTriggerSource triggerSource) {
+ secondHandlerCalled = YES;
+ };
+ testNewMenu = [[NSArray alloc] initWithArray:@[textOnlyCell, textOnlyCell2, textAndImageCell] copyItems:YES];
+ });
+
+ it(@"should not send deletes or adds, but should transfer handlers", ^{
+ testOp = [[SDLMenuReplaceOperation alloc] initWithConnectionManager:testConnectionManager fileManager:testFileManager windowCapability:testWindowCapability menuConfiguration:testMenuConfiguration currentMenu:testCurrentMenu updatedMenu:testNewMenu compatibilityModeEnabled:NO currentMenuUpdatedHandler:testCurrentMenuUpdatedBlock];
+ [testOp start];
+
+ NSPredicate *deleteCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass:%@", [SDLDeleteCommand class]];
+ NSArray *deletes = [[testConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:deleteCommandPredicate];
+
+ NSPredicate *addCommandPredicate = [NSPredicate predicateWithFormat:@"self isMemberOfClass:%@", [SDLAddCommand class]];
+ NSArray *adds = [[testConnectionManager.receivedRequests copy] filteredArrayUsingPredicate:addCommandPredicate];
+
+ expect(deletes).to(haveCount(0));
+ expect(adds).to(haveCount(0));
+
+ expect(testOp.isFinished).to(beTrue());
+ expect(resultError).to(beNil());
+ expect(resultMenuCells).to(haveCount(3));
+
+ resultMenuCells[0].handler(SDLTriggerSourceMenu);
+ expect(secondHandlerCalled).to(beTrue());
+ });
+ });
+ });
+});
+
+QuickSpecEnd
diff --git a/SmartDeviceLinkTests/DevAPISpecs/SDLMenuReplaceUtilitiesSpec.m b/SmartDeviceLinkTests/DevAPISpecs/SDLMenuReplaceUtilitiesSpec.m
new file mode 100644
index 000000000..51f6bca6a
--- /dev/null
+++ b/SmartDeviceLinkTests/DevAPISpecs/SDLMenuReplaceUtilitiesSpec.m
@@ -0,0 +1,532 @@
+#import <Quick/Quick.h>
+#import <Nimble/Nimble.h>
+#import <OCMock/OCMock.h>
+
+#import "SDLMenuReplaceUtilities.h"
+
+#import "SDLFileManager.h"
+#import "SDLMenuCell.h"
+#import "SDLMenuReplaceUtilitiesSpecHelpers.h"
+#import "SDLMenuManagerPrivateConstants.h"
+#import "SDLWindowCapability.h"
+#import "TestConnectionManager.h"
+
+@interface SDLMenuCell()
+
+@property (assign, nonatomic) UInt32 parentCellId;
+@property (assign, nonatomic) UInt32 cellId;
+@property (copy, nonatomic, readwrite, nullable) NSArray<SDLMenuCell *> *subCells;
+
+@end
+
+@interface SDLMenuReplaceUtilities ()
+
+@property (class, assign, nonatomic) UInt32 nextMenuId;
+
+@end
+
+QuickSpecBegin(SDLMenuReplaceUtilitiesSpec)
+
+__block NSMutableArray<SDLMenuCell *> *testMenuCells = nil;
+__block SDLFileManager *mockFileManager = nil;
+__block SDLWindowCapability *testWindowCapability = nil;
+__block NSArray<SDLTextField *> *allSupportedTextFields = @[
+ [[SDLTextField alloc] initWithName:SDLTextFieldNameMenuCommandSecondaryText characterSet:SDLCharacterSetUtf8 width:100 rows:1],
+ [[SDLTextField alloc] initWithName:SDLTextFieldNameMenuCommandTertiaryText characterSet:SDLCharacterSetUtf8 width:100 rows:1],
+ [[SDLTextField alloc] initWithName:SDLTextFieldNameMenuSubMenuSecondaryText characterSet:SDLCharacterSetUtf8 width:100 rows:1],
+ [[SDLTextField alloc] initWithName:SDLTextFieldNameMenuSubMenuTertiaryText characterSet:SDLCharacterSetUtf8 width:100 rows:1]
+];
+__block NSArray<SDLImageField *> *allSupportedImageFields = @[
+ [[SDLImageField alloc] initWithName:SDLImageFieldNameCommandIcon imageTypeSupported:@[SDLImageTypeDynamic] imageResolution:nil],
+ [[SDLImageField alloc] initWithName:SDLImageFieldNameMenuCommandSecondaryImage imageTypeSupported:@[SDLImageTypeDynamic] imageResolution:nil],
+ [[SDLImageField alloc] initWithName:SDLImageFieldNameSubMenuIcon imageTypeSupported:@[SDLImageTypeDynamic] imageResolution:nil],
+ [[SDLImageField alloc] initWithName:SDLImageFieldNameMenuSubMenuSecondaryImage imageTypeSupported:@[SDLImageTypeDynamic] imageResolution:nil]
+];
+
+describe(@"adding ids", ^{
+ it(@"should properly add ids", ^{
+ SDLMenuReplaceUtilities.nextMenuId = 0;
+ testMenuCells = SDLMenuReplaceUtilitiesSpecHelpers.deepMenu;
+
+ [SDLMenuReplaceUtilities addIdsToMenuCells:testMenuCells parentId:ParentIdNotFound];
+
+ expect(testMenuCells[0].cellId).to(equal(1));
+ expect(testMenuCells[1].cellId).to(equal(6));
+ expect(testMenuCells[2].cellId).to(equal(7));
+
+ NSArray<SDLMenuCell *> *subCellList1 = testMenuCells[0].subCells;
+ expect(subCellList1[0].cellId).to(equal(2));
+ expect(subCellList1[0].parentCellId).to(equal(1));
+ expect(subCellList1[1].cellId).to(equal(5));
+ expect(subCellList1[1].parentCellId).to(equal(1));
+
+ NSArray<SDLMenuCell *> *subCell1SubCellList1 = subCellList1[0].subCells;
+ expect(subCell1SubCellList1[0].cellId).to(equal(3));
+ expect(subCell1SubCellList1[0].parentCellId).to(equal(2));
+ expect(subCell1SubCellList1[1].cellId).to(equal(4));
+ expect(subCell1SubCellList1[1].parentCellId).to(equal(2));
+
+ NSArray<SDLMenuCell *> *subCellList2 = testMenuCells[2].subCells;
+ expect(subCellList2[0].cellId).to(equal(8));
+ expect(subCellList2[0].parentCellId).to(equal(7));
+ expect(subCellList2[1].cellId).to(equal(9));
+ expect(subCellList2[1].parentCellId).to(equal(7));
+ });
+});
+
+describe(@"transferring cell ids", ^{
+ it(@"should properly transfer ids and set parent ids", ^{
+ testMenuCells = [[NSMutableArray alloc] initWithArray:SDLMenuReplaceUtilitiesSpecHelpers.deepMenu copyItems:YES];
+ [SDLMenuReplaceUtilities addIdsToMenuCells:testMenuCells parentId:ParentIdNotFound];
+
+ NSArray<SDLMenuCell *> *toCells = [[NSArray alloc] initWithArray:SDLMenuReplaceUtilitiesSpecHelpers.deepMenu copyItems:YES];
+ [SDLMenuReplaceUtilities transferCellIDsFromCells:testMenuCells toCells:toCells];
+
+ // Top-level cells should have same cell ids
+ for (NSUInteger i = 0; i < testMenuCells.count; i++) {
+ expect(toCells[i].cellId).to(equal(testMenuCells[i].cellId));
+ }
+
+ // Sub-cells should _not_ have the same cell ids
+ for (NSUInteger i = 0; i < testMenuCells[0].subCells.count; i++) {
+ expect(toCells[0].subCells[i].cellId).toNot(equal(testMenuCells[0].subCells[i].cellId));
+ }
+
+ // Sub-cells should have proper parent ids
+ for (NSUInteger i = 0; i < testMenuCells[0].subCells.count; i++) {
+ expect(toCells[0].subCells[i].parentCellId).to(equal(toCells[0].cellId));
+ }
+ });
+});
+
+describe(@"transferring cell handlers", ^{
+ __block BOOL cell1HandlerTriggered = NO;
+ __block BOOL cell2HandlerTriggered = NO;
+ beforeEach(^{
+ cell1HandlerTriggered = NO;
+ cell2HandlerTriggered = NO;
+ });
+
+ it(@"should properly transfer cell handlers", ^{
+ SDLMenuCell *cell1 = [[SDLMenuCell alloc] initWithTitle:@"Cell1" secondaryText:nil tertiaryText:nil icon:nil secondaryArtwork:nil voiceCommands:nil handler:^(SDLTriggerSource _Nonnull triggerSource) {
+ cell1HandlerTriggered = YES;
+ }];
+ SDLMenuCell *cell2 = [[SDLMenuCell alloc] initWithTitle:@"Cell1" secondaryText:nil tertiaryText:nil icon:nil secondaryArtwork:nil voiceCommands:nil handler:^(SDLTriggerSource _Nonnull triggerSource) {
+ cell2HandlerTriggered = YES;
+ }];
+
+ [SDLMenuReplaceUtilities transferCellHandlersFromCells:@[cell1] toCells:@[cell2]];
+ cell2.handler(SDLTriggerSourceMenu);
+
+ expect(cell1HandlerTriggered).to(beTrue());
+ expect(cell2HandlerTriggered).to(beFalse());
+ });
+});
+
+describe(@"finding all artworks from cells", ^{
+ beforeEach(^{
+ mockFileManager = OCMClassMock([SDLFileManager class]);
+ testWindowCapability = [[SDLWindowCapability alloc] init];
+ });
+
+ context(@"when all the files need to be uploaded", ^{
+ beforeEach(^{
+ OCMStub([mockFileManager fileNeedsUpload:[OCMArg any]]).andReturn(YES);
+ });
+
+ context(@"when the window capability doesn't support the primary image", ^{
+ beforeEach(^{
+ testWindowCapability.textFields = allSupportedTextFields;
+ });
+
+ it(@"should return an empty list of artworks to upload", ^{
+ NSArray<SDLArtwork *> *artworksToUpload = [SDLMenuReplaceUtilities findAllArtworksToBeUploadedFromCells:SDLMenuReplaceUtilitiesSpecHelpers.topLevelOnlyMenu fileManager:mockFileManager windowCapability:testWindowCapability];
+
+ expect(artworksToUpload).to(beEmpty());
+ });
+ });
+
+ context(@"when the window capability supports primary but not secondary image", ^{
+ beforeEach(^{
+ testWindowCapability.textFields = allSupportedTextFields;
+ testWindowCapability.imageFields = @[allSupportedImageFields[0], allSupportedImageFields[2]];
+ });
+
+ it(@"should only return primary images to upload", ^{
+ NSArray<SDLArtwork *> *artworksToUpload = [SDLMenuReplaceUtilities findAllArtworksToBeUploadedFromCells:SDLMenuReplaceUtilitiesSpecHelpers.topLevelOnlyMenu fileManager:mockFileManager windowCapability:testWindowCapability];
+
+ expect(artworksToUpload).to(haveCount(2));
+ });
+ });
+
+ context(@"when the window capability supports both images", ^{
+ beforeEach(^{
+ testWindowCapability.textFields = allSupportedTextFields;
+ testWindowCapability.imageFields = allSupportedImageFields;
+ });
+
+ context(@"with a shallow menu", ^{
+ it(@"should only return all images to upload", ^{
+ NSArray<SDLArtwork *> *artworksToUpload = [SDLMenuReplaceUtilities findAllArtworksToBeUploadedFromCells:SDLMenuReplaceUtilitiesSpecHelpers.topLevelOnlyMenu fileManager:mockFileManager windowCapability:testWindowCapability];
+
+ expect(artworksToUpload).to(haveCount(2));
+ });
+ });
+
+ context(@"with a deep menu", ^{
+ it(@"should only return all images to upload", ^{
+ NSArray<SDLArtwork *> *artworksToUpload = [SDLMenuReplaceUtilities findAllArtworksToBeUploadedFromCells:SDLMenuReplaceUtilitiesSpecHelpers.deepMenu fileManager:mockFileManager windowCapability:testWindowCapability];
+
+ expect(artworksToUpload).to(haveCount(4));
+ });
+ });
+ });
+ });
+
+ context(@"when no files need to be uploaded", ^{
+ beforeEach(^{
+ OCMStub([mockFileManager fileNeedsUpload:[OCMArg any]]).andReturn(NO);
+ });
+
+ context(@"when the window capability supports both images", ^{
+ beforeEach(^{
+ testWindowCapability.textFields = allSupportedTextFields;
+ testWindowCapability.imageFields = allSupportedImageFields;
+ });
+
+ it(@"should not return any images to upload", ^{
+ NSArray<SDLArtwork *> *artworksToUpload = [SDLMenuReplaceUtilities findAllArtworksToBeUploadedFromCells:SDLMenuReplaceUtilitiesSpecHelpers.topLevelOnlyMenu fileManager:mockFileManager windowCapability:testWindowCapability];
+
+ expect(artworksToUpload).to(beEmpty());
+ });
+ });
+ });
+});
+
+describe(@"retrieving a commandId", ^{
+ context(@"with an AddCommand", ^{
+ it(@"should return the command id", ^{
+ SDLAddCommand *rpc = [[SDLAddCommand alloc] init];
+ rpc.cmdID = @12345;
+ expect([SDLMenuReplaceUtilities commandIdForRPCRequest:rpc]).to(equal(12345));
+ });
+ });
+
+ context(@"with an AddSubMenu", ^{
+ it(@"should return the command id", ^{
+ SDLAddSubMenu *rpc = [[SDLAddSubMenu alloc] init];
+ rpc.menuID = @12345;
+ expect([SDLMenuReplaceUtilities commandIdForRPCRequest:rpc]).to(equal(12345));
+ });
+ });
+
+ context(@"with a DeleteCommand", ^{
+ it(@"should return the command id", ^{
+ SDLDeleteCommand *rpc = [[SDLDeleteCommand alloc] init];
+ rpc.cmdID = @12345;
+ expect([SDLMenuReplaceUtilities commandIdForRPCRequest:rpc]).to(equal(12345));
+ });
+ });
+
+ context(@"with a DeleteSubMenu", ^{
+ it(@"should return the command id", ^{
+ SDLDeleteSubMenu *rpc = [[SDLDeleteSubMenu alloc] init];
+ rpc.menuID = @12345;
+ expect([SDLMenuReplaceUtilities commandIdForRPCRequest:rpc]).to(equal(12345));
+ });
+ });
+
+ context(@"with an Alert", ^{
+ it(@"should return 0", ^{
+ SDLAlert *rpc = [[SDLAlert alloc] init];
+ expect([SDLMenuReplaceUtilities commandIdForRPCRequest:rpc]).to(equal(0));
+ });
+ });
+});
+
+describe(@"retrieving a position", ^{
+ context(@"with an AddCommand", ^{
+ it(@"should return the position", ^{
+ SDLAddCommand *rpc = [[SDLAddCommand alloc] init];
+ rpc.menuParams = [[SDLMenuParams alloc] init];
+ rpc.menuParams.position = @123;
+ expect(@([SDLMenuReplaceUtilities positionForRPCRequest:rpc])).to(equal(@123));
+ });
+ });
+
+ context(@"with an AddSubMenu", ^{
+ it(@"should return the command id", ^{
+ SDLAddSubMenu *rpc = [[SDLAddSubMenu alloc] init];
+ rpc.position = @123;
+ expect(@([SDLMenuReplaceUtilities positionForRPCRequest:rpc])).to(equal(@123));
+ });
+ });
+});
+
+describe(@"generating RPCs", ^{
+ __block SDLMenuLayout testMenuLayout = SDLMenuLayoutList;
+
+ beforeEach(^{
+ mockFileManager = OCMClassMock([SDLFileManager class]);
+ testWindowCapability = [[SDLWindowCapability alloc] init];
+ });
+
+ context(@"delete commands", ^{
+ context(@"shallow menu", ^{
+ beforeEach(^{
+ testMenuCells = SDLMenuReplaceUtilitiesSpecHelpers.topLevelOnlyMenu;
+ });
+
+ it(@"should generate the correct RPCs", ^{
+ NSArray<SDLRPCRequest *> *requests = [SDLMenuReplaceUtilities deleteCommandsForCells:testMenuCells];
+ expect(requests).to(haveCount(3));
+ expect(requests[0]).to(beAnInstanceOf(SDLDeleteCommand.class));
+ expect(requests[1]).to(beAnInstanceOf(SDLDeleteCommand.class));
+ expect(requests[2]).to(beAnInstanceOf(SDLDeleteCommand.class));
+ });
+ });
+
+ context(@"deep menu", ^{
+ beforeEach(^{
+ testMenuCells = SDLMenuReplaceUtilitiesSpecHelpers.deepMenu;
+ });
+
+ it(@"should generate the correct RPCs", ^{
+ NSArray<SDLRPCRequest *> *requests = [SDLMenuReplaceUtilities deleteCommandsForCells:testMenuCells];
+ expect(requests).to(haveCount(3));
+ expect(requests[0]).to(beAnInstanceOf(SDLDeleteSubMenu.class));
+ expect(requests[1]).to(beAnInstanceOf(SDLDeleteCommand.class));
+ expect(requests[2]).to(beAnInstanceOf(SDLDeleteSubMenu.class));
+ });
+ });
+ });
+
+ context(@"main menu commands", ^{
+ context(@"shallow menu", ^{
+ beforeEach(^{
+ testMenuCells = SDLMenuReplaceUtilitiesSpecHelpers.topLevelOnlyMenu;
+ });
+
+ it(@"should generate the correct RPCs", ^{
+ NSArray<SDLRPCRequest *> *requests = [SDLMenuReplaceUtilities mainMenuCommandsForCells:testMenuCells fileManager:mockFileManager usingPositionsFromFullMenu:testMenuCells windowCapability:testWindowCapability defaultSubmenuLayout:testMenuLayout];
+ expect(requests).to(haveCount(3));
+ expect(requests[0]).to(beAnInstanceOf(SDLAddCommand.class));
+ expect(requests[1]).to(beAnInstanceOf(SDLAddCommand.class));
+ expect(requests[2]).to(beAnInstanceOf(SDLAddCommand.class));
+ });
+ });
+
+ context(@"deep menu", ^{
+ beforeEach(^{
+ testMenuCells = SDLMenuReplaceUtilitiesSpecHelpers.deepMenu;
+ });
+
+ it(@"should generate the correct RPCs", ^{
+ NSArray<SDLRPCRequest *> *requests = [SDLMenuReplaceUtilities mainMenuCommandsForCells:testMenuCells fileManager:mockFileManager usingPositionsFromFullMenu:testMenuCells windowCapability:testWindowCapability defaultSubmenuLayout:testMenuLayout];
+ expect(requests).to(haveCount(3));
+ expect(requests[0]).to(beAnInstanceOf(SDLAddSubMenu.class));
+ expect(requests[1]).to(beAnInstanceOf(SDLAddCommand.class));
+ expect(requests[2]).to(beAnInstanceOf(SDLAddSubMenu.class));
+ });
+ });
+ });
+
+ context(@"sub menu commands", ^{
+ context(@"shallow menu", ^{
+ beforeEach(^{
+ testMenuCells = SDLMenuReplaceUtilitiesSpecHelpers.topLevelOnlyMenu;
+ });
+ });
+
+ context(@"deep menu", ^{
+ beforeEach(^{
+ testMenuCells = SDLMenuReplaceUtilitiesSpecHelpers.deepMenu;
+ });
+ });
+ });
+});
+
+// updating menu cells
+describe(@"updating menu cell lists", ^{
+ __block UInt32 testCommandId = 0;
+
+ describe(@"removing commands", ^{
+ context(@"from a shallow list", ^{
+ beforeEach(^{
+ testMenuCells = SDLMenuReplaceUtilitiesSpecHelpers.topLevelOnlyMenu;
+ [SDLMenuReplaceUtilities addIdsToMenuCells:testMenuCells parentId:ParentIdNotFound];
+ });
+
+ context(@"when the cell is in the menu", ^{
+ beforeEach(^{
+ testCommandId = testMenuCells[1].cellId;
+ });
+
+ it(@"should return the menu without the cell and return YES", ^{
+ NSMutableArray<SDLMenuCell *> *testMutableMenuCells = [testMenuCells mutableCopy];
+ BOOL foundItem = [SDLMenuReplaceUtilities removeCellFromList:testMutableMenuCells withCellId:testCommandId];
+
+ expect(foundItem).to(beTrue());
+ expect(testMutableMenuCells).to(haveCount(2));
+ expect(testMutableMenuCells[0]).to(equal(testMenuCells[0]));
+ expect(testMutableMenuCells[1]).to(equal(testMenuCells[2]));
+ });
+ });
+
+ context(@"when the cell is not in the menu", ^{
+ beforeEach(^{
+ testCommandId = 100;
+ });
+
+ it(@"should return the menu with all cells and return NO", ^{
+ NSMutableArray<SDLMenuCell *> *testMutableMenuCells = [testMenuCells mutableCopy];
+ BOOL foundItem = [SDLMenuReplaceUtilities removeCellFromList:testMutableMenuCells withCellId:testCommandId];
+
+ expect(foundItem).to(beFalse());
+ expect(testMutableMenuCells).to(haveCount(3));
+ });
+ });
+ });
+
+ context(@"from a deep list", ^{
+ beforeEach(^{
+ testMenuCells = SDLMenuReplaceUtilitiesSpecHelpers.deepMenu;
+ [SDLMenuReplaceUtilities addIdsToMenuCells:testMenuCells parentId:ParentIdNotFound];
+ });
+
+ context(@"when the cell is in the top menu", ^{
+ beforeEach(^{
+ testCommandId = testMenuCells[1].cellId;
+ });
+
+ it(@"should return the menu without the cell and return YES", ^{
+ NSMutableArray<SDLMenuCell *> *testMutableMenuCells = [testMenuCells mutableCopy];
+ BOOL foundItem = [SDLMenuReplaceUtilities removeCellFromList:testMutableMenuCells withCellId:testCommandId];
+
+ expect(foundItem).to(beTrue());
+ expect(testMutableMenuCells).to(haveCount(2));
+ expect(testMutableMenuCells[0]).to(equal(testMenuCells[0]));
+ expect(testMutableMenuCells[1]).to(equal(testMenuCells[2]));
+ });
+ });
+
+ context(@"when the cell is in the submenu", ^{
+ beforeEach(^{
+ testCommandId = testMenuCells[0].subCells[0].cellId;
+ });
+
+ it(@"should return the menu without the cell and return YES", ^{
+ NSMutableArray<SDLMenuCell *> *testMutableMenuCells = [testMenuCells mutableCopy];
+ BOOL foundItem = [SDLMenuReplaceUtilities removeCellFromList:testMutableMenuCells withCellId:testCommandId];
+
+ expect(foundItem).to(beTrue());
+ expect(testMutableMenuCells).to(haveCount(3));
+ expect(testMutableMenuCells[0].subCells).to(haveCount(1));
+ });
+ });
+
+ context(@"when the cell is not in the menu", ^{
+ beforeEach(^{
+ testCommandId = 100;
+ });
+
+ it(@"should return the menu with all cells and return NO", ^{
+ NSMutableArray<SDLMenuCell *> *testMutableMenuCells = [testMenuCells mutableCopy];
+ BOOL foundItem = [SDLMenuReplaceUtilities removeCellFromList:testMutableMenuCells withCellId:testCommandId];
+
+ expect(foundItem).to(beFalse());
+ expect(testMutableMenuCells).to(haveCount(3));
+ expect(testMutableMenuCells[0].subCells).to(haveCount(2));
+ expect(testMutableMenuCells[2].subCells).to(haveCount(2));
+ });
+ });
+ });
+ });
+
+ describe(@"add commands to the main list", ^{
+ __block NSMutableArray<SDLMenuCell *> *newCellList = nil;
+
+ context(@"from a shallow list", ^{
+ beforeEach(^{
+ testMenuCells = SDLMenuReplaceUtilitiesSpecHelpers.topLevelOnlyMenu;
+ [SDLMenuReplaceUtilities addIdsToMenuCells:testMenuCells parentId:ParentIdNotFound];
+
+ SDLMenuCell *newCell = [[SDLMenuCell alloc] initWithTitle:@"New Cell" secondaryText:nil tertiaryText:nil icon:nil secondaryArtwork:nil voiceCommands:nil handler:^(SDLTriggerSource _Nonnull triggerSource) {}];
+ newCell.cellId = 99;
+ newCellList = [@[newCell] mutableCopy];
+ });
+
+ describe(@"if the cell is not in the cell list", ^{
+ beforeEach(^{
+ newCellList = [[NSMutableArray alloc] init];
+ });
+
+ it(@"should return NO", ^{
+ BOOL didAddCell = [SDLMenuReplaceUtilities addCellWithCellId:99 position:0 fromNewMenuList:newCellList toMainMenuList:testMenuCells];
+
+ expect(didAddCell).to(beFalse());
+ });
+ });
+
+ context(@"at the beginning", ^{
+ it(@"should return YES and the cell should be included", ^{
+ BOOL didAddCell = [SDLMenuReplaceUtilities addCellWithCellId:newCellList[0].cellId position:0 fromNewMenuList:newCellList toMainMenuList:testMenuCells];
+
+ expect(didAddCell).to(beTrue());
+ expect(testMenuCells).to(haveCount(4));
+ expect(testMenuCells[0]).to(equal(newCellList[0]));
+ });
+ });
+
+ context(@"in the middle", ^{
+ it(@"should return YES and the cell should be included", ^{
+ BOOL didAddCell = [SDLMenuReplaceUtilities addCellWithCellId:newCellList[0].cellId position:1 fromNewMenuList:newCellList toMainMenuList:testMenuCells];
+
+ expect(didAddCell).to(beTrue());
+ expect(testMenuCells).to(haveCount(4));
+ expect(testMenuCells[1]).to(equal(newCellList[0]));
+ });
+ });
+
+ context(@"at the end", ^{
+ it(@"should return YES and the cell should be included", ^{
+ BOOL didAddCell = [SDLMenuReplaceUtilities addCellWithCellId:newCellList[0].cellId position:3 fromNewMenuList:newCellList toMainMenuList:testMenuCells];
+
+ expect(didAddCell).to(beTrue());
+ expect(testMenuCells).to(haveCount(4));
+ expect(testMenuCells[3]).to(equal(newCellList[0]));
+ });
+ });
+ });
+
+ context(@"from a deep list", ^{
+ __block SDLMenuCell *subCell = nil;
+ __block NSMutableArray<SDLMenuCell *> *newMenu = nil;
+
+ beforeEach(^{
+ testMenuCells = SDLMenuReplaceUtilitiesSpecHelpers.deepMenu.copy;
+ [SDLMenuReplaceUtilities addIdsToMenuCells:testMenuCells parentId:ParentIdNotFound];
+
+ newMenu = [[NSMutableArray alloc] initWithArray:testMenuCells copyItems:YES];
+ NSMutableArray<SDLMenuCell *> *subMenuToUpdate = newMenu[0].subCells.mutableCopy;
+
+ subCell = [[SDLMenuCell alloc] initWithTitle:@"New SubCell" secondaryText:nil tertiaryText:nil icon:nil secondaryArtwork:nil voiceCommands:nil handler:^(SDLTriggerSource _Nonnull triggerSource) {}];
+ subCell.cellId = 98;
+ subCell.parentCellId = newMenu[0].cellId;
+ [subMenuToUpdate insertObject:subCell atIndex:0];
+ newMenu[0].subCells = subMenuToUpdate.copy;
+ });
+
+ it(@"should properly add the subcell to the list", ^{
+ BOOL didAddCell = [SDLMenuReplaceUtilities addCellWithCellId:newMenu[0].subCells[0].cellId position:0 fromNewMenuList:newMenu toMainMenuList:testMenuCells];
+
+ expect(didAddCell).to(beTrue());
+ expect(testMenuCells).to(haveCount(3));
+ expect(testMenuCells[0].subCells).to(haveCount(3));
+ expect(testMenuCells[0].subCells[0]).to(equal(subCell));
+ });
+ });
+ });
+});
+
+QuickSpecEnd
diff --git a/SmartDeviceLinkTests/DevAPISpecs/SDLMenuReplaceUtilitiesSpecHelpers.h b/SmartDeviceLinkTests/DevAPISpecs/SDLMenuReplaceUtilitiesSpecHelpers.h
new file mode 100644
index 000000000..7106cb567
--- /dev/null
+++ b/SmartDeviceLinkTests/DevAPISpecs/SDLMenuReplaceUtilitiesSpecHelpers.h
@@ -0,0 +1,22 @@
+//
+// SDLMenuReplaceUtilitiesSpecHelpers.h
+// SmartDeviceLinkTests
+//
+// Created by Joel Fischer on 1/29/21.
+// Copyright © 2021 smartdevicelink. All rights reserved.
+//
+
+#import <Foundation/Foundation.h>
+
+@class SDLMenuCell;
+
+NS_ASSUME_NONNULL_BEGIN
+
+@interface SDLMenuReplaceUtilitiesSpecHelpers : NSObject
+
+@property (class, nonatomic, readonly) NSMutableArray<SDLMenuCell *> *topLevelOnlyMenu;
+@property (class, nonatomic, readonly) NSMutableArray<SDLMenuCell *> *deepMenu;
+
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/SmartDeviceLinkTests/DevAPISpecs/SDLMenuReplaceUtilitiesSpecHelpers.m b/SmartDeviceLinkTests/DevAPISpecs/SDLMenuReplaceUtilitiesSpecHelpers.m
new file mode 100644
index 000000000..c6069d315
--- /dev/null
+++ b/SmartDeviceLinkTests/DevAPISpecs/SDLMenuReplaceUtilitiesSpecHelpers.m
@@ -0,0 +1,70 @@
+//
+// SDLMenuReplaceUtilitiesSpecHelpers.m
+// SmartDeviceLinkTests
+//
+// Created by Joel Fischer on 1/29/21.
+// Copyright © 2021 smartdevicelink. All rights reserved.
+//
+
+#import "SDLMenuReplaceUtilitiesSpecHelpers.h"
+
+#import "SDLArtwork.h"
+#import "SDLMenuCell.h"
+
+@interface SDLMenuCell()
+
+@property (assign, nonatomic) UInt32 parentCellId;
+@property (assign, nonatomic) UInt32 cellId;
+
+@end
+
+@implementation SDLMenuReplaceUtilitiesSpecHelpers
+
++ (NSMutableArray<SDLMenuCell *> *)topLevelOnlyMenu {
+ NSData *cellArtData = [@"testart" dataUsingEncoding:NSUTF8StringEncoding];
+ NSData *cellArtData2 = [@"testart2" dataUsingEncoding:NSUTF8StringEncoding];
+ SDLArtwork *artwork1 = [[SDLArtwork alloc] initWithData:cellArtData name:@"Test Art 1" fileExtension:@"png" persistent:NO];
+ SDLArtwork *artwork2 = [[SDLArtwork alloc] initWithData:cellArtData2 name:@"Test Art 2" fileExtension:@"png" persistent:NO];
+
+ SDLMenuCell *cell1 = [[SDLMenuCell alloc] initWithTitle:@"Item 1" secondaryText:nil tertiaryText:nil icon:artwork1 secondaryArtwork:nil voiceCommands:nil handler:^(SDLTriggerSource _Nonnull triggerSource) {}];
+ cell1.cellId = 1;
+
+ SDLMenuCell *cell2 = [[SDLMenuCell alloc] initWithTitle:@"Item 2" secondaryText:nil tertiaryText:nil icon:artwork1 secondaryArtwork:nil voiceCommands:nil handler:^(SDLTriggerSource _Nonnull triggerSource) {}];
+ cell2.cellId = 2;
+
+ SDLMenuCell *cell3 = [[SDLMenuCell alloc] initWithTitle:@"Item 3" secondaryText:nil tertiaryText:nil icon:nil secondaryArtwork:artwork2 voiceCommands:nil handler:^(SDLTriggerSource _Nonnull triggerSource) {}];
+ cell3.cellId = 3;
+
+ return [@[cell1, cell2, cell3] mutableCopy];
+}
+
++ (NSMutableArray<SDLMenuCell *> *)deepMenu {
+ NSData *cellArtData = [@"testart" dataUsingEncoding:NSUTF8StringEncoding];
+ NSData *cellArtData2 = [@"testart2" dataUsingEncoding:NSUTF8StringEncoding];
+ NSData *cellArtData3 = [@"testart3" dataUsingEncoding:NSUTF8StringEncoding];
+ NSData *cellArtData4 = [@"testart4" dataUsingEncoding:NSUTF8StringEncoding];
+ SDLArtwork *artwork1 = [[SDLArtwork alloc] initWithData:cellArtData name:@"Test Art 1" fileExtension:@"png" persistent:NO];
+ SDLArtwork *artwork2 = [[SDLArtwork alloc] initWithData:cellArtData2 name:@"Test Art 2" fileExtension:@"png" persistent:NO];
+ SDLArtwork *artwork3 = [[SDLArtwork alloc] initWithData:cellArtData3 name:@"Test Art 3" fileExtension:@"png" persistent:NO];
+ SDLArtwork *artwork4 = [[SDLArtwork alloc] initWithData:cellArtData4 name:@"Test Art 4" fileExtension:@"png" persistent:NO];
+
+ SDLMenuCell *subList1SubList1Cell1 = [[SDLMenuCell alloc] initWithTitle:@"Item 1" secondaryText:@"SubItem 1" tertiaryText:@"Sub-SubItem 1" icon:nil secondaryArtwork:artwork3 voiceCommands:nil handler:^(SDLTriggerSource _Nonnull triggerSource) {}];
+ SDLMenuCell *subList1SubList1Cell2 = [[SDLMenuCell alloc] initWithTitle:@"Item 1" secondaryText:@"SubItem 1" tertiaryText:@"Sub-SubItem 2" icon:artwork1 secondaryArtwork:nil voiceCommands:nil handler:^(SDLTriggerSource _Nonnull triggerSource) {}];
+ NSArray<SDLMenuCell *> *subList1SubList1 = @[subList1SubList1Cell1, subList1SubList1Cell2];
+
+ SDLMenuCell *subList1Cell1 = [[SDLMenuCell alloc] initWithTitle:@"Item 1" secondaryText:@"SubItem 1" tertiaryText:nil icon:artwork4 secondaryArtwork:nil submenuLayout:nil subCells:subList1SubList1];
+ SDLMenuCell *subList1Cell2 = [[SDLMenuCell alloc] initWithTitle:@"Item 1" secondaryText:@"SubItem 2" tertiaryText:nil icon:artwork2 secondaryArtwork:nil voiceCommands:nil handler:^(SDLTriggerSource _Nonnull triggerSource) {}];
+ NSArray<SDLMenuCell *> *subList1 = @[subList1Cell1, subList1Cell2];
+
+ SDLMenuCell *subList2Cell1 = [[SDLMenuCell alloc] initWithTitle:@"Item 3" secondaryText:@"SubItem 1" tertiaryText:nil icon:artwork1 secondaryArtwork:artwork4 voiceCommands:nil handler:^(SDLTriggerSource _Nonnull triggerSource) {}];
+ SDLMenuCell *subList2Cell2 = [[SDLMenuCell alloc] initWithTitle:@"Item 3" secondaryText:@"SubItem 2" tertiaryText:nil icon:artwork1 secondaryArtwork:artwork2 voiceCommands:nil handler:^(SDLTriggerSource _Nonnull triggerSource) {}];
+ NSArray<SDLMenuCell *> *subList2 = @[subList2Cell1, subList2Cell2];
+
+ SDLMenuCell *topListCell1 = [[SDLMenuCell alloc] initWithTitle:@"Item 1" secondaryText:nil tertiaryText:nil icon:artwork1 secondaryArtwork:nil submenuLayout:nil subCells:subList1];
+ SDLMenuCell *topListCell2 = [[SDLMenuCell alloc] initWithTitle:@"Item 2" secondaryText:nil tertiaryText:nil icon:nil secondaryArtwork:nil voiceCommands:nil handler:^(SDLTriggerSource _Nonnull triggerSource) {}];
+ SDLMenuCell *topListCell3 = [[SDLMenuCell alloc] initWithTitle:@"Item 3" secondaryText:nil tertiaryText:nil icon:nil secondaryArtwork:nil submenuLayout:nil subCells:subList2];
+
+ return @[topListCell1, topListCell2, topListCell3].mutableCopy;
+}
+
+@end
diff --git a/SmartDeviceLinkTests/DevAPISpecs/SDLMenuShowOperationSpec.m b/SmartDeviceLinkTests/DevAPISpecs/SDLMenuShowOperationSpec.m
new file mode 100644
index 000000000..d79f2b90f
--- /dev/null
+++ b/SmartDeviceLinkTests/DevAPISpecs/SDLMenuShowOperationSpec.m
@@ -0,0 +1,167 @@
+//
+// SDLMenuShowOperationSpec.m
+// SmartDeviceLinkTests
+//
+// Created by Joel Fischer on 2/16/21.
+// Copyright © 2021 smartdevicelink. All rights reserved.
+//
+
+#import <Nimble/Nimble.h>
+#import <OCMock/OCMock.h>
+#import <Quick/Quick.h>
+
+#import <SmartDeviceLink/SmartDeviceLink.h>
+#import "SDLMenuShowOperation.h"
+#import "TestConnectionManager.h"
+
+@interface SDLMenuShowOperation ()
+
+@property (strong, nonatomic, nullable) SDLMenuCell *submenuCell;
+
+@end
+
+QuickSpecBegin(SDLMenuShowOperationSpec)
+
+describe(@"the show menu operation", ^{
+ __block SDLMenuShowOperation *testOp = nil;
+ __block TestConnectionManager *testConnectionManager = nil;
+ __block NSError *resultError = nil;
+ __block BOOL callbackCalled = NO;
+
+ beforeEach(^{
+ testConnectionManager = [[TestConnectionManager alloc] init];
+ testOp = [[SDLMenuShowOperation alloc] initWithConnectionManager:testConnectionManager toMenuCell:nil completionHandler:^(NSError * _Nullable error) {
+ resultError = error;
+ callbackCalled = YES;
+ }];
+ resultError = nil;
+ callbackCalled = NO;
+ });
+
+ // opening to the main menu
+ context(@"opening to the main menu", ^{
+ beforeEach(^{
+ [testOp start];
+ });
+
+ it(@"should send the RPC request", ^{
+ expect(testConnectionManager.receivedRequests).to(haveCount(1));
+ });
+
+ // when the response is not SUCCESS or WARNINGS
+ context(@"when the response is not SUCCESS or WARNINGS", ^{
+ beforeEach(^{
+ SDLShowAppMenuResponse *response = [[SDLShowAppMenuResponse alloc] init];
+ response.success = @NO;
+ response.resultCode = SDLResultRejected;
+
+ [testConnectionManager respondToLastRequestWithResponse:response];
+ });
+
+ it(@"should set the error and finish", ^{
+ expect(testOp.isFinished).to(beTrue());
+ expect(resultError).toNot(beNil());
+ expect(callbackCalled).to(beTrue());
+ });
+ });
+
+ // when the response is SUCCESS
+ context(@"when the response is SUCCESS", ^{
+ beforeEach(^{
+ SDLShowAppMenuResponse *response = [[SDLShowAppMenuResponse alloc] init];
+ response.success = @YES;
+ response.resultCode = SDLResultSuccess;
+
+ [testConnectionManager respondToLastRequestWithResponse:response];
+ });
+
+ it(@"should not set the error and finish", ^{
+ expect(testOp.isFinished).to(beTrue());
+ expect(resultError).to(beNil());
+ expect(callbackCalled).to(beTrue());
+ });
+ });
+
+ // when the response is WARNINGS
+ context(@"when the response is WARNINGS", ^{
+ beforeEach(^{
+ SDLShowAppMenuResponse *response = [[SDLShowAppMenuResponse alloc] init];
+ response.success = @YES;
+ response.resultCode = SDLResultWarnings;
+
+ [testConnectionManager respondToLastRequestWithResponse:response];
+ });
+
+ it(@"should not set the error and finish", ^{
+ expect(testOp.isFinished).to(beTrue());
+ expect(resultError).to(beNil());
+ expect(callbackCalled).to(beTrue());
+ });
+ });
+ });
+
+ // opening to an inner menu
+ context(@"opening to an inner menu", ^{
+ __block SDLMenuCell *openToCell = nil;
+ __block SDLMenuCell *subcell = nil;
+ beforeEach(^{
+ subcell = [[SDLMenuCell alloc] initWithTitle:@"Subcell" secondaryText:nil tertiaryText:nil icon:nil secondaryArtwork:nil voiceCommands:nil handler:^(SDLTriggerSource _Nonnull triggerSource) { }];
+ openToCell = [[SDLMenuCell alloc] initWithTitle:@"Test submenu" secondaryText:nil tertiaryText:nil icon:nil secondaryArtwork:nil submenuLayout:nil subCells:@[subcell]];
+ testOp.submenuCell = openToCell;
+ [testOp start];
+ });
+
+ // when the response is not SUCCESS or WARNINGS
+ context(@"when the response is not SUCCESS or WARNINGS", ^{
+ beforeEach(^{
+ SDLShowAppMenuResponse *response = [[SDLShowAppMenuResponse alloc] init];
+ response.success = @NO;
+ response.resultCode = SDLResultRejected;
+
+ [testConnectionManager respondToLastRequestWithResponse:response];
+ });
+
+ it(@"should set the error and finish", ^{
+ expect(resultError).toNot(beNil());
+ expect(callbackCalled).to(beTrue());
+ expect(testOp.isFinished).to(beTrue());
+ });
+ });
+
+ // when the response is SUCCESS
+ context(@"when the response is SUCCESS", ^{
+ beforeEach(^{
+ SDLShowAppMenuResponse *response = [[SDLShowAppMenuResponse alloc] init];
+ response.success = @YES;
+ response.resultCode = SDLResultSuccess;
+
+ [testConnectionManager respondToLastRequestWithResponse:response];
+ });
+
+ it(@"should not set the error and finish", ^{
+ expect(resultError).to(beNil());
+ expect(callbackCalled).to(beTrue());
+ expect(testOp.isFinished).to(beTrue());
+ });
+ });
+
+ // when the response is WARNINGS
+ context(@"when the response is WARNINGS", ^{
+ beforeEach(^{
+ SDLShowAppMenuResponse *response = [[SDLShowAppMenuResponse alloc] init];
+ response.success = @YES;
+ response.resultCode = SDLResultWarnings;
+
+ [testConnectionManager respondToLastRequestWithResponse:response];
+ });
+
+ it(@"should not set the error and finish", ^{
+ expect(resultError).to(beNil());
+ expect(callbackCalled).to(beTrue());
+ expect(testOp.isFinished).to(beTrue());
+ });
+ });
+ });
+});
+
+QuickSpecEnd