diff options
Diffstat (limited to 'SmartDeviceLinkTests')
9 files changed, 1062 insertions, 24 deletions
diff --git a/SmartDeviceLinkTests/DevAPISpecs/SDLMenuManagerSpec.m b/SmartDeviceLinkTests/DevAPISpecs/SDLMenuManagerSpec.m index c663205e2..1ee9d5afd 100644 --- a/SmartDeviceLinkTests/DevAPISpecs/SDLMenuManagerSpec.m +++ b/SmartDeviceLinkTests/DevAPISpecs/SDLMenuManagerSpec.m @@ -6,6 +6,7 @@ #import "SDLGlobals.h" #import "SDLMenuManager.h" +#import "SDLMenuShowOperation.h" #import "SDLMenuReplaceOperation.h" #import "TestConnectionManager.h" @@ -17,6 +18,12 @@ @end +@interface SDLMenuShowOperation() + +@property (strong, nonatomic, nullable) SDLMenuCell *submenuCell; + +@end + @interface SDLMenuManager() @property (weak, nonatomic) id<SDLConnectionManagerType> connectionManager; @@ -269,6 +276,22 @@ describe(@"menu manager", ^{ expect(canSendRPC).to(equal(YES)); }); + // should queue an open menu operation for a copied submenu cell + it(@"should queue an open menu operation for a copied submenu cell and match the original cell id", ^ { + submenuCell.cellId = 1; + testManager.menuCells = @[submenuCell]; + + SDLMenuCell *copiedCell = [[SDLMenuCell alloc] initWithTitle:@"Test 3" secondaryText:nil tertiaryText:nil icon:nil secondaryArtwork:nil submenuLayout:nil subCells:@[textOnlyCell]]; + + BOOL canSendRPC = [testManager openMenu:copiedCell]; + SDLMenuShowOperation *showOperation = (SDLMenuShowOperation *)testManager.transactionQueue.operations[1]; + + expect(showOperation.submenuCell.cellId).to(equal(submenuCell.cellId)); + expect(showOperation.submenuCell.cellId).toNot(equal(copiedCell.cellId)); + expect(testManager.transactionQueue.operationCount).to(equal(2)); + expect(canSendRPC).to(equal(YES)); + }); + it(@"should cancel the first task if a second is queued", ^{ testManager.menuCells = @[submenuCell]; [testManager openMenu:nil]; diff --git a/SmartDeviceLinkTests/DevAPISpecs/SDLTextAndGraphicManagerSpec.m b/SmartDeviceLinkTests/DevAPISpecs/SDLTextAndGraphicManagerSpec.m index ea230248c..29e9a775f 100644 --- a/SmartDeviceLinkTests/DevAPISpecs/SDLTextAndGraphicManagerSpec.m +++ b/SmartDeviceLinkTests/DevAPISpecs/SDLTextAndGraphicManagerSpec.m @@ -140,19 +140,23 @@ describe(@"text and graphic manager", ^{ }); }); - // when previous updates have bene cancelled - context(@"when previous updates have bene cancelled", ^{ + // without batching + context(@"without batching", ^{ beforeEach(^{ - testManager.textField1 = @"Hello"; - - // This should cancel the first operation - testManager.textField2 = @"Goodbye"; + testManager.batchUpdates = NO; + testManager.textField1 = @"test1"; + testManager.textField2 = @"test2"; + testManager.textField3 = @"test3"; + testManager.textField4 = @"test4"; }); - it(@"should properly queue the new update", ^{ + it(@"should create individual operations and not be cancelled", ^{ expect(testManager.transactionQueue.isSuspended).to(beTrue()); - expect(testManager.transactionQueue.operationCount).to(equal(2)); - expect(testManager.transactionQueue.operations[0].cancelled).to(beTrue()); + expect(testManager.transactionQueue.operationCount).to(equal(4)); + expect(testManager.transactionQueue.operations[0].cancelled).to(beFalse()); + expect(testManager.transactionQueue.operations[1].cancelled).to(beFalse()); + expect(testManager.transactionQueue.operations[2].cancelled).to(beFalse()); + expect(testManager.transactionQueue.operations[3].cancelled).to(beFalse()); }); }); @@ -450,6 +454,7 @@ describe(@"text and graphic manager", ^{ describe(@"when the operation updates the current screen data", ^{ __block SDLTextAndGraphicUpdateOperation *testOperation = nil; __block SDLTextAndGraphicUpdateOperation *testOperation2 = nil; + __block SDLTextAndGraphicUpdateOperation *testOperation3 = nil; beforeEach(^{ testManager.textField1 = @"test"; @@ -475,11 +480,26 @@ describe(@"text and graphic manager", ^{ beforeEach(^{ testManager.currentScreenData = [[SDLTextAndGraphicState alloc] init]; testManager.currentScreenData.textField1 = @"Test1"; - testOperation.currentDataUpdatedHandler(nil, [NSError errorWithDomain:@"any" code:1 userInfo:nil]); + + // Create a "bad data" text field 1, then set it in the manager, which should create an operation (op 2) + SDLTextAndGraphicState *errorState = [[SDLTextAndGraphicState alloc] init]; + errorState.textField1 = @"Bad Data"; + testManager.textField1 = errorState.textField1; + + // Create a "good data text field 4, which should create a second operation (op 3) + testManager.textField4 = @"Good Data"; + testOperation3 = testManager.transactionQueue.operations[3]; + + // Simulate a failure of the first operation + NSDictionary *userInfo = @{ + SDLTextAndGraphicFailedScreenStateErrorKey: errorState + }; + testOperation.currentDataUpdatedHandler(nil, [NSError errorWithDomain:@"any" code:1 userInfo:userInfo]); }); - it(@"should reset the manager's data", ^{ + it(@"should reset the manager's data and update other operations updated state", ^{ expect(testManager.textField1).to(equal(testManager.currentScreenData.textField1)); + expect(testOperation3.updatedState.textField1).to(equal(testManager.currentScreenData.textField1)); }); }); }); diff --git a/SmartDeviceLinkTests/DevAPISpecs/SDLTextAndGraphicUpdateOperationSpec.m b/SmartDeviceLinkTests/DevAPISpecs/SDLTextAndGraphicUpdateOperationSpec.m index b03252c25..a3f780ab0 100644 --- a/SmartDeviceLinkTests/DevAPISpecs/SDLTextAndGraphicUpdateOperationSpec.m +++ b/SmartDeviceLinkTests/DevAPISpecs/SDLTextAndGraphicUpdateOperationSpec.m @@ -797,6 +797,116 @@ describe(@"the text and graphic operation", ^{ }); }); + // updating with error state + describe(@"updating with error state", ^{ + beforeEach(^{ + updatedState = [[SDLTextAndGraphicState alloc] init]; + updatedState.textField1 = field1String; + updatedState.textField2 = field2String; + updatedState.textField3 = field3String; + updatedState.textField4 = field4String; + updatedState.mediaTrackTextField = mediaTrackString; + updatedState.title = titleString; + updatedState.primaryGraphic = testArtwork; + updatedState.secondaryGraphic = testArtwork2; + updatedState.alignment = SDLTextAlignmentLeft; + updatedState.textField1Type = SDLMetadataTypeMediaTitle; + updatedState.textField2Type = SDLMetadataTypeMediaArtist; + updatedState.textField3Type = SDLMetadataTypeMediaAlbum; + updatedState.textField4Type = SDLMetadataTypeMediaYear; + + emptyCurrentData = [[SDLTextAndGraphicState alloc] init]; + + testOp = [[SDLTextAndGraphicUpdateOperation alloc] initWithConnectionManager:testConnectionManager fileManager:mockFileManager currentCapabilities:windowCapability currentScreenData:emptyCurrentData newState:updatedState currentScreenDataUpdatedHandler:^(SDLTextAndGraphicState * _Nullable newScreenData, NSError * _Nullable error) {} updateCompletionHandler:nil]; + [testOp start]; + }); + + it(@"should reset to current screen data for equivalent properties in updated state and error state", ^{ + // Create an error state that matches the updated state, which should reset the updated state + SDLTextAndGraphicState *errorState = [updatedState copy]; + errorState.primaryGraphic = testArtwork; + errorState.secondaryGraphic = testArtwork2; + + [testOp updateTargetStateWithErrorState:errorState]; + + expect(updatedState.textField1).to(beNil()); + expect(updatedState.textField2).to(beNil()); + expect(updatedState.textField3).to(beNil()); + expect(updatedState.textField4).to(beNil()); + expect(updatedState.mediaTrackTextField).to(beNil()); + expect(updatedState.title).to(beNil()); + expect(updatedState.primaryGraphic).to(beNil()); + expect(updatedState.secondaryGraphic).to(beNil()); + expect(updatedState.textField1Type).to(beNil()); + expect(updatedState.textField2Type).to(beNil()); + expect(updatedState.textField3Type).to(beNil()); + expect(updatedState.textField4Type).to(beNil()); + }); + + it(@"should not reset to current screen data for non equivalent properties in updated state and error state", ^{ + // Save an original of the updatedState for confirming no changes later + SDLTextAndGraphicState *originalState = [updatedState copy]; + originalState.primaryGraphic = testArtwork; + originalState.secondaryGraphic = testArtwork2; + + // Create an error state that does not match the updated state, which should not reset the updated state + SDLTextAndGraphicState *errorState = [[SDLTextAndGraphicState alloc] init]; + errorState.textField1 = @"Error Text"; + errorState.textField2 = @"Error Text"; + errorState.textField3 = @"Error Text"; + errorState.textField4 = @"Error Text"; + errorState.mediaTrackTextField = @"Error Text"; + errorState.title = @"Error Text"; + errorState.primaryGraphic = testArtwork2; + errorState.secondaryGraphic = testArtwork; + errorState.alignment = SDLTextAlignmentRight; + errorState.textField1Type = SDLMetadataTypeMediaYear; + errorState.textField2Type = SDLMetadataTypeMediaAlbum; + errorState.textField3Type = SDLMetadataTypeMediaArtist; + errorState.textField4Type = SDLMetadataTypeMediaTitle; + + [testOp updateTargetStateWithErrorState:errorState]; + + expect(updatedState.textField1).to(equal(originalState.textField1)); + expect(updatedState.textField2).to(equal(originalState.textField2)); + expect(updatedState.textField3).to(equal(originalState.textField3)); + expect(updatedState.textField4).to(equal(originalState.textField4)); + expect(updatedState.mediaTrackTextField).to(equal(originalState.mediaTrackTextField)); + expect(updatedState.title).to(equal(originalState.title)); + expect(updatedState.primaryGraphic).to(equal(originalState.primaryGraphic)); + expect(updatedState.secondaryGraphic).to(equal(originalState.secondaryGraphic)); + expect(updatedState.textField1Type).to(equal(originalState.textField1Type)); + expect(updatedState.textField2Type).to(equal(originalState.textField2Type)); + expect(updatedState.textField3Type).to(equal(originalState.textField3Type)); + expect(updatedState.textField4Type).to(equal(originalState.textField4Type)); + }); + + it(@"should not reset to current screen data for nil error state", ^{ + // Save an original of the updatedState for confirming no changes later + SDLTextAndGraphicState *originalState = [updatedState copy]; + originalState.primaryGraphic = testArtwork; + originalState.secondaryGraphic = testArtwork2; + + // Create an empty error state + SDLTextAndGraphicState *errorState = [[SDLTextAndGraphicState alloc] init]; + + [testOp updateTargetStateWithErrorState:errorState]; + + expect(updatedState.textField1).to(equal(originalState.textField1)); + expect(updatedState.textField2).to(equal(originalState.textField2)); + expect(updatedState.textField3).to(equal(originalState.textField3)); + expect(updatedState.textField4).to(equal(originalState.textField4)); + expect(updatedState.mediaTrackTextField).to(equal(originalState.mediaTrackTextField)); + expect(updatedState.title).to(equal(originalState.title)); + expect(updatedState.primaryGraphic).to(equal(originalState.primaryGraphic)); + expect(updatedState.secondaryGraphic).to(equal(originalState.secondaryGraphic)); + expect(updatedState.textField1Type).to(equal(originalState.textField1Type)); + expect(updatedState.textField2Type).to(equal(originalState.textField2Type)); + expect(updatedState.textField3Type).to(equal(originalState.textField3Type)); + expect(updatedState.textField4Type).to(equal(originalState.textField4Type)); + }); + }); + // updating image fields describe(@"updating image fields", ^{ beforeEach(^{ @@ -933,6 +1043,8 @@ describe(@"the text and graphic operation", ^{ // Then it should return a failure and finish expect(receivedState).to(beNil()); expect(receivedError).toNot(beNil()); + expect(receivedError.userInfo[NSUnderlyingErrorKey]).toNot(beNil()); + expect(receivedError.userInfo[SDLTextAndGraphicFailedScreenStateErrorKey]).to(equal(updatedState)); expect(completionError).toNot(beNil()); expect(testOp.isFinished).to(beTrue()); @@ -1295,6 +1407,8 @@ describe(@"the text and graphic operation", ^{ [testConnectionManager respondToLastRequestWithResponse:failShowResponse]; expect(receivedState).to(beNil()); expect(receivedError).toNot(beNil()); + expect(receivedError.userInfo[NSUnderlyingErrorKey]).toNot(beNil()); + expect(receivedError.userInfo[SDLTextAndGraphicFailedScreenStateErrorKey]).to(equal(updatedState)); expect(testOp.isFinished).to(beTrue()); }); diff --git a/SmartDeviceLinkTests/ProtocolSpecs/HeaderSpecs/SDLProtocolHeaderSpec.m b/SmartDeviceLinkTests/ProtocolSpecs/HeaderSpecs/SDLProtocolHeaderSpec.m index a578ec561..9dac0203f 100644 --- a/SmartDeviceLinkTests/ProtocolSpecs/HeaderSpecs/SDLProtocolHeaderSpec.m +++ b/SmartDeviceLinkTests/ProtocolSpecs/HeaderSpecs/SDLProtocolHeaderSpec.m @@ -15,6 +15,18 @@ QuickSpecBegin(SDLProtocolHeaderSpec) +__block SDLProtocolHeader *testHeader; + +beforeSuite(^ { + testHeader = [[SDLProtocolHeader alloc] init]; + testHeader.encrypted = YES; + testHeader.frameType = SDLFrameTypeControl; + testHeader.serviceType = SDLServiceTypeRPC; + testHeader.frameData = SDLFrameInfoStartService; + testHeader.sessionID = 0x53; + testHeader.bytesInPayload = 0x1234; +}); + describe(@"HeaderForVersion Tests", ^ { it(@"Should return the correct header", ^ { expect([SDLProtocolHeader headerForVersion:1]).to(beAKindOf(SDLV1ProtocolHeader.class)); @@ -38,4 +50,30 @@ describe(@"DetermineVersion Tests", ^ { }); }); +describe(@"hashing tests", ^ { + it(@"should return equivalent hash values", ^ { + SDLProtocolHeader *equalHeader = [[SDLProtocolHeader alloc] init]; + equalHeader.encrypted = YES; + equalHeader.frameType = SDLFrameTypeControl; + equalHeader.serviceType = SDLServiceTypeRPC; + equalHeader.frameData = SDLFrameInfoStartService; + equalHeader.sessionID = 0x53; + equalHeader.bytesInPayload = 0x1234; + + expect([testHeader hash]).to(equal([equalHeader hash])); + }); + + it(@"should return unequivalent hash values", ^ { + SDLProtocolHeader *unequalHeader = [[SDLProtocolHeader alloc] init]; + unequalHeader.encrypted = NO; + unequalHeader.frameType = SDLFrameTypeFirst; + unequalHeader.serviceType = SDLServiceTypeVideo; + unequalHeader.frameData = SDLFrameInfoStartService; + unequalHeader.sessionID = 0x54; + unequalHeader.bytesInPayload = 0x1234; + + expect([testHeader hash]).toNot(equal([unequalHeader hash])); + }); +}); + QuickSpecEnd diff --git a/SmartDeviceLinkTests/ProtocolSpecs/HeaderSpecs/SDLV1ProtocolHeaderSpec.m b/SmartDeviceLinkTests/ProtocolSpecs/HeaderSpecs/SDLV1ProtocolHeaderSpec.m index a7c6c1660..f2218f7db 100644 --- a/SmartDeviceLinkTests/ProtocolSpecs/HeaderSpecs/SDLV1ProtocolHeaderSpec.m +++ b/SmartDeviceLinkTests/ProtocolSpecs/HeaderSpecs/SDLV1ProtocolHeaderSpec.m @@ -86,4 +86,21 @@ describe(@"RPCPayloadWithData Test", ^ { }); }); +describe(@"equality tests", ^ { + it (@"should be equal to copy of header", ^ { + // Create exact copy of test header + SDLV1ProtocolHeader *equalHeader = [testHeader copy]; + + expect([testHeader isEqual:equalHeader]).to(beTrue()); + }); + + it (@"should not be equal to a different header", ^ { + // create a slighty different version of test header + SDLV1ProtocolHeader *unequalHeader = [testHeader copy]; + unequalHeader.encrypted = NO; + + expect(([testHeader isEqual:unequalHeader])).to(beFalse()); + }); +}); + QuickSpecEnd diff --git a/SmartDeviceLinkTests/ProtocolSpecs/HeaderSpecs/SDLV2ProtocolHeaderSpec.m b/SmartDeviceLinkTests/ProtocolSpecs/HeaderSpecs/SDLV2ProtocolHeaderSpec.m index 1ade6e426..ec06d2b4d 100644 --- a/SmartDeviceLinkTests/ProtocolSpecs/HeaderSpecs/SDLV2ProtocolHeaderSpec.m +++ b/SmartDeviceLinkTests/ProtocolSpecs/HeaderSpecs/SDLV2ProtocolHeaderSpec.m @@ -90,4 +90,21 @@ describe(@"RPCPayloadWithData Test", ^ { }); }); +describe(@"equality tests", ^ { + it (@"should be equal to copy of header", ^ { + // Create exact copy of test header + SDLV2ProtocolHeader *equalHeader = [testHeader copy]; + + expect([testHeader isEqual:equalHeader]).to(beTrue()); + }); + + it (@"should not be equal to a different header", ^ { + // Create a slighty different version of test header + SDLV2ProtocolHeader *unequalHeader = [testHeader copy]; + unequalHeader.messageID = 0x6DAB424E; + + expect(([testHeader isEqual:unequalHeader])).to(beFalse()); + }); +}); + QuickSpecEnd diff --git a/SmartDeviceLinkTests/ProtocolSpecs/SDLProtocolReceivedMessageProcessorSpec.m b/SmartDeviceLinkTests/ProtocolSpecs/SDLProtocolReceivedMessageProcessorSpec.m new file mode 100644 index 000000000..7065e89a8 --- /dev/null +++ b/SmartDeviceLinkTests/ProtocolSpecs/SDLProtocolReceivedMessageProcessorSpec.m @@ -0,0 +1,769 @@ +// +// SDLProtocolReceivedMessageProcessorSpec.m +// SmartDeviceLinkTests +// +// Created by George Miller on 8/9/22. +// Copyright © 2022 smartdevicelink. All rights reserved. +// + +#import <Foundation/Foundation.h> + +#import <Quick/Quick.h> +#import <Nimble/Nimble.h> + +#import "SDLProtocol.h" +#import "SDLProtocolReceivedMessageProcessor.h" +#import "SDLV2ProtocolHeader.h" +#import "SDLV2ProtocolMessage.h" + +typedef NS_ENUM(NSInteger, ProcessorState) { + START_STATE = 0x0, + SERVICE_TYPE_STATE = 0x01, + CONTROL_FRAME_INFO_STATE = 0x02, + SESSION_ID_STATE = 0x03, + DATA_SIZE_1_STATE = 0x04, + DATA_SIZE_2_STATE = 0x05, + DATA_SIZE_3_STATE = 0x06, + DATA_SIZE_4_STATE = 0x07, + MESSAGE_1_STATE = 0x08, + MESSAGE_2_STATE = 0x09, + MESSAGE_3_STATE = 0x0A, + MESSAGE_4_STATE = 0x0B, + DATA_PUMP_STATE = 0x0C, + ERROR_STATE = -1, +}; + +@interface SDLProtocolReceivedMessageProcessor () +// State management +@property (assign, nonatomic) ProcessorState state; + +// Message assembly state +@property (strong, nonatomic) SDLProtocolHeader *header; +@property (strong, nonatomic) NSMutableData *headerBuffer; +@property (strong, nonatomic) NSMutableData *payloadBuffer; + +@property (assign, nonatomic) UInt8 version; +@property (assign, nonatomic) BOOL encrypted; +@property (assign, nonatomic) SDLFrameType frameType; +@property (assign, nonatomic) UInt32 dataLength; +@property (assign, nonatomic) UInt32 dataBytesRemaining; +@property (assign, nonatomic) SDLServiceType serviceType; +@end + +QuickSpecBegin(SDLProtocolReceivedMessageProcessorSpec) + +describe(@"The received message processor", ^{ + __block SDLProtocolReceivedMessageProcessor *testProcessor = nil; + __block NSMutableData *testBuffer; + __block NSMutableData *testHeaderBuffer; + __block SDLProtocolHeader *messageReadyHeader = nil; + __block SDLProtocolHeader *expectedMessageReadyHeader = nil; + __block NSData *messageReadyPayload = nil; + __block NSData *expectedPayloadBuffer = nil; + + beforeEach(^{ + testProcessor = [[SDLProtocolReceivedMessageProcessor alloc] init]; + testBuffer = [NSMutableData data]; + testHeaderBuffer = [NSMutableData data]; + messageReadyHeader = nil; + expectedMessageReadyHeader = nil; + messageReadyPayload = nil; + }); + + it(@"test processor should be initialized correctly", ^{ + expect(@(testProcessor.state)).to(equal(START_STATE)); + expect(@(testProcessor.version)).to(equal(0)); + expect(@(testProcessor.encrypted)).to(equal(NO)); + expect(@(testProcessor.frameType)).to(equal(SDLFrameTypeControl)); + expect(@(testProcessor.dataLength)).to(equal(0)); + expect(@(testProcessor.dataBytesRemaining)).to(equal(0)); + expect(@(testProcessor.serviceType)).to(equal(SDLServiceTypeControl)); + }); + + describe(@"when in START_STATE", ^{ + it(@"should transition to next state when receiving version 1", ^{ + Byte testByte = 0x11; + [testBuffer appendBytes:&testByte length:1]; + + [testProcessor processReceiveBuffer:testBuffer withMessageReadyBlock:^(SDLProtocolHeader *header, NSData *payload) { + messageReadyHeader = header; + messageReadyPayload = payload; + + expect(messageReadyHeader).to(beNil()); + expect(messageReadyPayload).to(beNil()); + }]; + expect(testProcessor.version).to(equal(1)); + expect(@(testProcessor.state)).to(equal(SERVICE_TYPE_STATE)); + }); + + it(@"should transition to next state when receiving version 2", ^{ + Byte testByte = 0x21; + [testBuffer appendBytes:&testByte length:1]; + + [testProcessor processReceiveBuffer:testBuffer withMessageReadyBlock:^(SDLProtocolHeader *header, NSData *payload) { + messageReadyHeader = header; + messageReadyPayload = payload; + + expect(messageReadyHeader).to(beNil()); + expect(messageReadyPayload).to(beNil()); + }]; + expect(testProcessor.version).to(equal(2)); + expect(@(testProcessor.state)).to(equal(SERVICE_TYPE_STATE)); + }); + + it(@"should transition to next state when receiving version 3", ^{ + Byte testByte = 0x31; + [testBuffer appendBytes:&testByte length:1]; + + [testProcessor processReceiveBuffer:testBuffer withMessageReadyBlock:^(SDLProtocolHeader *header, NSData *payload) { + messageReadyHeader = header; + messageReadyPayload = payload; + + expect(messageReadyHeader).to(beNil()); + expect(messageReadyPayload).to(beNil()); + }]; + expect(testProcessor.version).to(equal(3)); + expect(@(testProcessor.state)).to(equal(SERVICE_TYPE_STATE)); + }); + + it(@"should transition to next state when receiving version 4", ^{ + Byte testByte = 0x41; + [testBuffer appendBytes:&testByte length:1]; + + [testProcessor processReceiveBuffer:testBuffer withMessageReadyBlock:^(SDLProtocolHeader *header, NSData *payload) { + messageReadyHeader = header; + messageReadyPayload = payload; + + expect(messageReadyHeader).to(beNil()); + expect(messageReadyPayload).to(beNil()); + }]; + expect(testProcessor.version).to(equal(4)); + expect(@(testProcessor.state)).to(equal(SERVICE_TYPE_STATE)); + }); + + it(@"should transition to next state when receiving version 5", ^{ + Byte testByte = 0x51; + [testBuffer appendBytes:&testByte length:1]; + + [testProcessor processReceiveBuffer:testBuffer withMessageReadyBlock:^(SDLProtocolHeader *header, NSData *payload) { + messageReadyHeader = header; + messageReadyPayload = payload; + + expect(messageReadyHeader).to(beNil()); + expect(messageReadyPayload).to(beNil()); + }]; + expect(testProcessor.version).to(equal(5)); + expect(@(testProcessor.state)).to(equal(SERVICE_TYPE_STATE)); + }); + + it(@"should transition to next state when receiving a byte with a frameType of SDLFrameTypeControl", ^{ + Byte testByte = 0x10; + [testBuffer appendBytes:&testByte length:1]; + + [testProcessor processReceiveBuffer:testBuffer withMessageReadyBlock:^(SDLProtocolHeader *header, NSData *payload) { + messageReadyHeader = header; + messageReadyPayload = payload; + + expect(messageReadyHeader).to(beNil()); + expect(messageReadyPayload).to(beNil()); + }]; + expect(@(testProcessor.state)).to(equal(SERVICE_TYPE_STATE)); + }); + + it(@"should transition to next state when receiving a byte with a frameType of SDLFrameTypeSingle", ^{ + Byte testByte = 0x11; + [testBuffer appendBytes:&testByte length:1]; + + [testProcessor processReceiveBuffer:testBuffer withMessageReadyBlock:^(SDLProtocolHeader *header, NSData *payload) { + messageReadyHeader = header; + messageReadyPayload = payload; + + expect(messageReadyHeader).to(beNil()); + expect(messageReadyPayload).to(beNil()); + }]; + expect(@(testProcessor.state)).to(equal(SERVICE_TYPE_STATE)); + expect(@(testProcessor.frameType)).to(equal(SDLFrameTypeSingle)); + }); + + it(@"should transition to next state when receiving a byte with a frameType of SDLFrameTypeFirst", ^{ + Byte testByte = 0x12; + [testBuffer appendBytes:&testByte length:1]; + + [testProcessor processReceiveBuffer:testBuffer withMessageReadyBlock:^(SDLProtocolHeader *header, NSData *payload) { + messageReadyHeader = header; + messageReadyPayload = payload; + + expect(messageReadyHeader).to(beNil()); + expect(messageReadyPayload).to(beNil()); + }]; + expect(@(testProcessor.state)).to(equal(SERVICE_TYPE_STATE)); + expect(@(testProcessor.frameType)).to(equal(SDLFrameTypeFirst)); + }); + + it(@"should transition to next state when receiving a byte with a frameType of SDLFrameTypeConsecutive", ^{ + Byte testByte = 0x13; + [testBuffer appendBytes:&testByte length:1]; + + [testProcessor processReceiveBuffer:testBuffer withMessageReadyBlock:^(SDLProtocolHeader *header, NSData *payload) { + messageReadyHeader = header; + messageReadyPayload = payload; + + expect(messageReadyHeader).to(beNil()); + expect(messageReadyPayload).to(beNil()); + }]; + expect(@(testProcessor.state)).to(equal(SERVICE_TYPE_STATE)); + expect(@(testProcessor.frameType)).to(equal(SDLFrameTypeConsecutive)); + }); + + it(@"should reset state when receiving a byte with a bad version 0", ^{ + Byte testByte = 0x01; + [testBuffer appendBytes:&testByte length:1]; + + [testProcessor processReceiveBuffer:testBuffer withMessageReadyBlock:^(SDLProtocolHeader *header, NSData *payload) { + messageReadyHeader = header; + messageReadyPayload = payload; + + expect(messageReadyHeader).to(beNil()); + expect(messageReadyPayload).to(beNil()); + }]; + expect(@(testProcessor.state)).to(equal(START_STATE)); + expect(testProcessor.version).to(equal(0)); + expect(testProcessor.headerBuffer).to(equal([NSMutableData data])); + expect(testProcessor.payloadBuffer).to(equal([NSMutableData data])); + }); + + it(@"should reset state when receiving a byte with a bad version 6", ^{ + Byte testByte = 0x61; + [testBuffer appendBytes:&testByte length:1]; + + [testProcessor processReceiveBuffer:testBuffer withMessageReadyBlock:^(SDLProtocolHeader *header, NSData *payload) { + messageReadyHeader = header; + messageReadyPayload = payload; + + expect(messageReadyHeader).to(beNil()); + expect(messageReadyPayload).to(beNil()); + }]; + expect(@(testProcessor.state)).to(equal(START_STATE)); + expect(testProcessor.version).to(equal(0)); + expect(testProcessor.headerBuffer).to(equal([NSMutableData data])); + expect(testProcessor.payloadBuffer).to(equal([NSMutableData data])); + }); + + it(@"should reset state when receiving a byte with an invalid frameType of 6", ^{ + Byte testByte = 0x46; //0100 0 110 + [testBuffer appendBytes:&testByte length:1]; + + [testProcessor processReceiveBuffer:testBuffer withMessageReadyBlock:^(SDLProtocolHeader *header, NSData *payload) { + messageReadyHeader = header; + messageReadyPayload = payload; + + expect(messageReadyHeader).to(beNil()); + expect(messageReadyPayload).to(beNil()); + }]; + expect(@(testProcessor.state)).to(equal(START_STATE)); + expect(@(testProcessor.frameType)).to(equal(0)); + expect(testProcessor.version).to(equal(0)); + }); + }); + + // transitions to CONTROL_FRAME_INFO_STATE when in SERVICE_TYPE_STATE + describe(@"when in SERVICE_TYPE_STATE", ^{ + + beforeEach(^{ + testProcessor.state = SERVICE_TYPE_STATE; + }); + + it(@"should transition to next state when receiving a SDLServiceTypeControl byte", ^{ + Byte testByte = SDLServiceTypeControl; + [testBuffer appendBytes:&testByte length:1]; + + [testProcessor processReceiveBuffer:testBuffer withMessageReadyBlock:^(SDLProtocolHeader *header, NSData *payload) { + messageReadyHeader = header; + messageReadyPayload = payload; + + expect(messageReadyHeader).to(beNil()); + expect(messageReadyPayload).to(beNil()); + }]; + expect(@(testProcessor.state)).to(equal(CONTROL_FRAME_INFO_STATE)); + expect(@(testProcessor.serviceType)).to(equal(SDLServiceTypeControl)); + }); + + it(@"should transition to next state when receiving a SDLServiceTypeRPC byte", ^{ + Byte testByte = SDLServiceTypeRPC; + [testBuffer appendBytes:&testByte length:1]; + + [testProcessor processReceiveBuffer:testBuffer withMessageReadyBlock:^(SDLProtocolHeader *header, NSData *payload) { + messageReadyHeader = header; + messageReadyPayload = payload; + + expect(messageReadyHeader).to(beNil()); + expect(messageReadyPayload).to(beNil()); + }]; + expect(@(testProcessor.state)).to(equal(CONTROL_FRAME_INFO_STATE)); + expect(@(testProcessor.serviceType)).to(equal(SDLServiceTypeRPC)); + }); + + it(@"should transition to next state when receiving a SDLServiceTypeAudio byte", ^{ + Byte testByte = SDLServiceTypeAudio; + [testBuffer appendBytes:&testByte length:1]; + + [testProcessor processReceiveBuffer:testBuffer withMessageReadyBlock:^(SDLProtocolHeader *header, NSData *payload) { + messageReadyHeader = header; + messageReadyPayload = payload; + + expect(messageReadyHeader).to(beNil()); + expect(messageReadyPayload).to(beNil()); + }]; + expect(@(testProcessor.state)).to(equal(CONTROL_FRAME_INFO_STATE)); + expect(@(testProcessor.serviceType)).to(equal(SDLServiceTypeAudio)); + }); + + it(@"should transition to next state when receiving a SDLServiceTypeVideo byte", ^{ + Byte testByte = SDLServiceTypeVideo; + [testBuffer appendBytes:&testByte length:1]; + + [testProcessor processReceiveBuffer:testBuffer withMessageReadyBlock:^(SDLProtocolHeader *header, NSData *payload) { + messageReadyHeader = header; + messageReadyPayload = payload; + + expect(messageReadyHeader).to(beNil()); + expect(messageReadyPayload).to(beNil()); + }]; + expect(@(testProcessor.state)).to(equal(CONTROL_FRAME_INFO_STATE)); + expect(@(testProcessor.serviceType)).to(equal(SDLServiceTypeVideo)); + }); + + it(@"should transition to next state when receiving a SDLServiceTypeBulkData byte", ^{ + Byte testByte = SDLServiceTypeBulkData; + [testBuffer appendBytes:&testByte length:1]; + + [testProcessor processReceiveBuffer:testBuffer withMessageReadyBlock:^(SDLProtocolHeader *header, NSData *payload) { + messageReadyHeader = header; + messageReadyPayload = payload; + + expect(messageReadyHeader).to(beNil()); + expect(messageReadyPayload).to(beNil()); + }]; + expect(@(testProcessor.state)).to(equal(CONTROL_FRAME_INFO_STATE)); + expect(@(testProcessor.serviceType)).to(equal(SDLServiceTypeBulkData)); + }); + + it(@"should reset state when receiving an invalid byte", ^{ + Byte testByte = 0xFF; + [testBuffer appendBytes:&testByte length:1]; + + [testProcessor processReceiveBuffer:testBuffer withMessageReadyBlock:^(SDLProtocolHeader *header, NSData *payload) { + messageReadyHeader = header; + messageReadyPayload = payload; + + expect(messageReadyHeader).to(beNil()); + expect(messageReadyPayload).to(beNil()); + }]; + expect(@(testProcessor.state)).to(equal(START_STATE)); + expect(@(testProcessor.serviceType)).to(equal(SDLServiceTypeControl)); + expect(testProcessor.headerBuffer).to(equal([NSMutableData data])); + expect(testProcessor.payloadBuffer).to(equal([NSMutableData data])); + }); + }); + + describe(@"when in CONTROL_FRAME_INFO_STATE", ^{ + + beforeEach(^{ + testProcessor.state = CONTROL_FRAME_INFO_STATE; + }); + + it(@"should transition to next state when receiving a valid byte", ^{ + Byte testByte = 0x00; + testProcessor.frameType = SDLFrameTypeFirst; + [testBuffer appendBytes:&testByte length:1]; + + [testProcessor processReceiveBuffer:testBuffer withMessageReadyBlock:^(SDLProtocolHeader *header, NSData *payload) { + messageReadyHeader = header; + messageReadyPayload = payload; + + expect(messageReadyHeader).to(beNil()); + expect(messageReadyPayload).to(beNil()); + }]; + expect(@(testProcessor.state)).to(equal(SESSION_ID_STATE)); + expect(@(testProcessor.frameType)).to(equal(SDLFrameTypeFirst)); + }); + + it(@"should reset state when receiving a byte where controlFrameInfo is not 0 and frameType is SDLFrameTypeFirst", ^{ + Byte testByte = 0x01; + testProcessor.frameType = SDLFrameTypeFirst; + [testBuffer appendBytes:&testByte length:1]; + + [testProcessor processReceiveBuffer:testBuffer withMessageReadyBlock:^(SDLProtocolHeader *header, NSData *payload) { + messageReadyHeader = header; + messageReadyPayload = payload; + + expect(messageReadyHeader).to(beNil()); + expect(messageReadyPayload).to(beNil()); + }]; + expect(@(testProcessor.state)).to(equal(START_STATE)); + expect(@(testProcessor.frameType)).to(equal(SDLFrameTypeControl)); + expect(testProcessor.headerBuffer).to(equal([NSMutableData data])); + expect(testProcessor.payloadBuffer).to(equal([NSMutableData data])); + }); + + it(@"should resets state when receiving a byte where controlFrameInfo is not 0 and frameType is SDLFrameTypeSingle", ^{ + Byte testByte = 0x01; + testProcessor.frameType = SDLFrameTypeSingle; + [testBuffer appendBytes:&testByte length:1]; + + [testProcessor processReceiveBuffer:testBuffer withMessageReadyBlock:^(SDLProtocolHeader *header, NSData *payload) { + messageReadyHeader = header; + messageReadyPayload = payload; + + expect(messageReadyHeader).to(beNil()); + expect(messageReadyPayload).to(beNil()); + }]; + expect(@(testProcessor.state)).to(equal(START_STATE)); + expect(@(testProcessor.frameType)).to(equal(SDLFrameTypeControl)); + expect(testProcessor.headerBuffer).to(equal([NSMutableData data])); + expect(testProcessor.payloadBuffer).to(equal([NSMutableData data])); + }); + }); + + it(@"should transition to DATA_SIZE_1_STATE when in SESSION_ID_STATE and receiving a byte", ^{ + testProcessor.state = SESSION_ID_STATE; + Byte testByte = 0x00; + [testBuffer appendBytes:&testByte length:1]; + + [testProcessor processReceiveBuffer:testBuffer withMessageReadyBlock:^(SDLProtocolHeader *header, NSData *payload) { + messageReadyHeader = header; + messageReadyPayload = payload; + + expect(messageReadyHeader).to(beNil()); + expect(messageReadyPayload).to(beNil()); + }]; + expect(@(testProcessor.state)).to(equal(DATA_SIZE_1_STATE)); + expect(@(testProcessor.dataLength)).to(equal(0)); + }); + + it(@"should transition to DATA_SIZE_2_STATE when in DATA_SIZE_1_STATE and receiving a byte", ^{ + testProcessor.state = DATA_SIZE_1_STATE; + Byte testByte = 0x02; + [testBuffer appendBytes:&testByte length:1]; + + [testProcessor processReceiveBuffer:testBuffer withMessageReadyBlock:^(SDLProtocolHeader *header, NSData *payload) { + messageReadyHeader = header; + messageReadyPayload = payload; + + expect(messageReadyHeader).to(beNil()); + expect(messageReadyPayload).to(beNil()); + }]; + expect(@(testProcessor.state)).to(equal(DATA_SIZE_2_STATE)); + expect(@(testProcessor.dataLength)).to(equal((UInt32)(testByte & 0xFF) << 24)); + }); + + it(@"should transitions to DATA_SIZE_3_STATE when in DATA_SIZE_2_STATE and receiving a byte", ^{ + testProcessor.state = DATA_SIZE_2_STATE; + Byte testByte = 0x02; + [testBuffer appendBytes:&testByte length:1]; + + [testProcessor processReceiveBuffer:testBuffer withMessageReadyBlock:^(SDLProtocolHeader *header, NSData *payload) { + messageReadyHeader = header; + messageReadyPayload = payload; + + expect(messageReadyHeader).to(beNil()); + expect(messageReadyPayload).to(beNil()); + }]; + expect(@(testProcessor.state)).to(equal(DATA_SIZE_3_STATE)); + expect(@(testProcessor.dataLength)).to(equal((UInt32)(testByte & 0xFF) << 16)); + }); + + it(@"should transition to DATA_SIZE_4_STATE when in DATA_SIZE_3_STATE and receiving a byte", ^{ + testProcessor.state = DATA_SIZE_3_STATE; + Byte testByte = 0x02; + [testBuffer appendBytes:&testByte length:1]; + + [testProcessor processReceiveBuffer:testBuffer withMessageReadyBlock:^(SDLProtocolHeader *header, NSData *payload) { + messageReadyHeader = header; + messageReadyPayload = payload; + + expect(messageReadyHeader).to(beNil()); + expect(messageReadyPayload).to(beNil()); + }]; + expect(@(testProcessor.state)).to(equal(DATA_SIZE_4_STATE)); + expect(@(testProcessor.dataLength)).to(equal((UInt32)(testByte & 0xFF) << 8)); + }); + + describe(@"when in DATA_SIZE_4_STATE and the version is 1", ^{ + beforeEach(^{ + testProcessor.state = DATA_SIZE_4_STATE; + testProcessor.version = 1; + + messageReadyHeader = nil; + messageReadyPayload = nil; + + //need a valid headerbuffer. + Byte firstByte = ((testProcessor.version & 0x0f) << 4) + (0 << 3) + (1 & 0x07); //version 2 with no encryption, frametype 1 + UInt32 dataLength = 3; + const Byte testBytes[8] = {firstByte, 0x00, 0x00, 0x00, (dataLength >> 24) & 0xff, (dataLength >> 16) & 0xff, (dataLength >> 8) & 0xff, (dataLength) & 0xff }; + [testHeaderBuffer appendBytes:&testBytes length:8]; + UInt32 messageID = 0; + Byte messageIDBytes[4] = {(messageID >> 24) & 0xff, (messageID >> 16) & 0xff, (messageID >> 8) & 0xff, (messageID) & 0xff}; + [testHeaderBuffer appendBytes:&messageIDBytes length:4]; + + testProcessor.headerBuffer = testHeaderBuffer; + expectedMessageReadyHeader= [SDLProtocolHeader headerForVersion:testProcessor.version]; + [expectedMessageReadyHeader parse:testHeaderBuffer]; + + expectedPayloadBuffer = [NSData data]; + }); + + it(@"should reset state when receiving a byte and determines the data length is 0", ^{ + Byte testByte = 0x00; + [testBuffer appendBytes:&testByte length:1]; + messageReadyHeader = [SDLProtocolHeader headerForVersion:1]; + + [testProcessor processReceiveBuffer:testBuffer withMessageReadyBlock:^(SDLProtocolHeader *header, NSData *payload) { + messageReadyHeader = header; + messageReadyPayload = payload; + + expect(messageReadyHeader).to(equal(expectedMessageReadyHeader)); + expect(messageReadyPayload).to(equal(expectedPayloadBuffer)); + }]; + expect(@(testProcessor.dataLength)).to(equal(0)); + expect(@(testProcessor.state)).to(equal(START_STATE)); + expect(@(testProcessor.version)).to(equal(0)); + expect(testProcessor.headerBuffer).to(equal([NSMutableData data])); + expect(testProcessor.payloadBuffer).to(equal([NSMutableData data])); + }); + + it(@"should transition to DATA_PUMP_STATE when receiving a byte and determines the data length is greater than 0", ^{ + Byte testByte = 0x01; + [testBuffer appendBytes:&testByte length:1]; + + [testProcessor processReceiveBuffer:testBuffer withMessageReadyBlock:^(SDLProtocolHeader *header, NSData *payload) { + messageReadyHeader = header; + messageReadyPayload = payload; + + expect(messageReadyHeader).to(beNil()); + expect(messageReadyPayload).to(beNil()); + }]; + expect(@(testProcessor.state)).to(equal(DATA_PUMP_STATE)); + expect(@(testProcessor.dataLength)).to(equal(1)); + expect(@(testProcessor.version)).to(equal(1)); + }); + + it(@"should transition to START_STATE when receiving a byte and determines the data length is greater than maxMtuSize", ^{ + testProcessor.serviceType = SDLServiceTypeControl; + testProcessor.dataLength = 200000; + + Byte testByte = 0x00; + [testBuffer appendBytes:&testByte length:1]; + + [testProcessor processReceiveBuffer:testBuffer withMessageReadyBlock:^(SDLProtocolHeader *header, NSData *payload) { + messageReadyHeader = header; + messageReadyPayload = payload; + + expect(messageReadyHeader).to(beNil()); + expect(messageReadyPayload).to(beNil()); + }]; + expect(@(testProcessor.state)).to(equal(START_STATE)); + expect(@(testProcessor.serviceType)).to(equal(0)); + expect(@(testProcessor.dataLength)).to(equal(0)); + expect(@(testProcessor.version)).to(equal(0)); + }); + }); + + describe(@"when in DATA_SIZE_4_STATE and the version is greater than 1", ^{ + beforeEach(^{ + testProcessor.state = DATA_SIZE_4_STATE; + messageReadyHeader = nil; + messageReadyPayload = nil; + + testProcessor.version = 1; + //need a valid headerbuffer. + Byte firstByte = ((testProcessor.version & 0x0f) << 4) + (0 << 3) + (1 & 0x07); //version 2 with no encryption, frametype 1 + UInt32 dataLength = 3; + const Byte testBytes[8] = {firstByte, 0x00, 0x00, 0x00, (dataLength >> 24) & 0xff, (dataLength >> 16) & 0xff, (dataLength >> 8) & 0xff, (dataLength) & 0xff }; + [testHeaderBuffer appendBytes:&testBytes length:8]; + UInt32 messageID = 0; + Byte messageIDBytes[4] = {(messageID >> 24) & 0xff, (messageID >> 16) & 0xff, (messageID >> 8) & 0xff, (messageID) & 0xff}; + [testHeaderBuffer appendBytes:&messageIDBytes length:4]; + + testProcessor.headerBuffer = testHeaderBuffer; + expectedMessageReadyHeader= [SDLProtocolHeader headerForVersion:testProcessor.version]; + [expectedMessageReadyHeader parse:testHeaderBuffer]; + + expectedPayloadBuffer = [NSData data]; + }); + + it(@"should transition to MESSAGE_1_STATE when it receives a byte", ^{ + testProcessor.version = 2; + testProcessor.dataLength = 0; + + Byte testByte = 0x00; + [testBuffer appendBytes:&testByte length:1]; + + [testProcessor processReceiveBuffer:testBuffer withMessageReadyBlock:^(SDLProtocolHeader *header, NSData *payload) { + messageReadyHeader = header; + messageReadyPayload = payload; + + expect(messageReadyHeader).to(beNil()); + expect(messageReadyPayload).to(beNil()); + }]; + expect(@(testProcessor.state)).to(equal(MESSAGE_1_STATE)); + expect(testProcessor.version).to(equal(2)); + }); + }); + + it(@"should transition to MESSAGE_2_STATE when in MESSAGE_1_STATE, it receives a byte", ^{ + testProcessor.state = MESSAGE_1_STATE; + Byte testByte = 0x00; + [testBuffer appendBytes:&testByte length:1]; + + [testProcessor processReceiveBuffer:testBuffer withMessageReadyBlock:^(SDLProtocolHeader *header, NSData *payload) { + messageReadyHeader = header; + messageReadyPayload = payload; + + expect(messageReadyHeader).to(beNil()); + expect(messageReadyPayload).to(beNil()); + }]; + expect(@(testProcessor.state)).to(equal(MESSAGE_2_STATE)); + }); + + it(@"should transition to MESSAGE_3_STATE when in MESSAGE_2_STATE and it receives a byte", ^{ + testProcessor.state = MESSAGE_2_STATE; + Byte testByte = 0x00; + [testBuffer appendBytes:&testByte length:1]; + + [testProcessor processReceiveBuffer:testBuffer withMessageReadyBlock:^(SDLProtocolHeader *header, NSData *payload) { + messageReadyHeader = header; + messageReadyPayload = payload; + + expect(messageReadyHeader).to(beNil()); + expect(messageReadyPayload).to(beNil()); + }]; + expect(@(testProcessor.state)).to(equal(MESSAGE_3_STATE)); + }); + + it(@"should transition to MESSAGE_4_STATE when in MESSAGE_3_STATE and it receives a byte", ^{ + testProcessor.state = MESSAGE_3_STATE; + Byte testByte = 0x00; + [testBuffer appendBytes:&testByte length:1]; + + [testProcessor processReceiveBuffer:testBuffer withMessageReadyBlock:^(SDLProtocolHeader *header, NSData *payload) { + messageReadyHeader = header; + messageReadyPayload = payload; + + expect(messageReadyHeader).to(beNil()); + expect(messageReadyPayload).to(beNil()); + }]; + expect(@(testProcessor.state)).to(equal(MESSAGE_4_STATE)); + }); + + describe(@"when in MESSAGE_4_STATE and version is greater than 1", ^{ + beforeEach(^{ + testProcessor.state = MESSAGE_4_STATE; + testProcessor.version = 2; + //need a valid headerbuffer. + Byte firstByte = ((testProcessor.version & 0x0f) << 4) + (0 << 3) + (1 & 0x07); //version 2 with no encryption, frametype 1 + UInt32 dataLength = 3; + const Byte testBytes[8] = {firstByte, 0x00, 0x00, 0x00, (dataLength >> 24) & 0xff, (dataLength >> 16) & 0xff, (dataLength >> 8) & 0xff, (dataLength) & 0xff }; + [testHeaderBuffer appendBytes:&testBytes length:8]; + UInt32 messageID = 0; + Byte messageIDBytes[4] = {(messageID >> 24) & 0xff, (messageID >> 16) & 0xff, (messageID >> 8) & 0xff, (messageID) & 0xff}; + [testHeaderBuffer appendBytes:&messageIDBytes length:4]; + + testProcessor.headerBuffer = testHeaderBuffer; + expectedMessageReadyHeader= [SDLProtocolHeader headerForVersion:testProcessor.version]; + [expectedMessageReadyHeader parse:testHeaderBuffer]; + + expectedPayloadBuffer = [NSData data]; + + messageReadyHeader = nil; + messageReadyPayload = nil; + + Byte testByte = 0x00; + [testBuffer appendBytes:&testByte length:1]; + }); + + it(@"should reset state when data length is 0 and receiving a byte", ^{ + testProcessor.dataLength = 0; + [testProcessor processReceiveBuffer:testBuffer withMessageReadyBlock:^(SDLProtocolHeader *header, NSData *payload) { + messageReadyHeader = header; + messageReadyPayload = payload; + + expect(messageReadyHeader).to(equal(expectedMessageReadyHeader)); + expect(messageReadyPayload).to(equal(expectedPayloadBuffer)); + }]; + expect(testProcessor.version).to(equal(0)); + expect(testProcessor.headerBuffer).to(equal([NSMutableData data])); + expect(testProcessor.payloadBuffer).to(equal([NSMutableData data])); + expect(@(testProcessor.state)).to(equal(START_STATE)); + }); + + it(@"should transition to DATA_PUMP_STATE when datalength is greater than 0 and receiving a byte", ^{ + testProcessor.dataLength = 1; + + [testProcessor processReceiveBuffer:testBuffer withMessageReadyBlock:^(SDLProtocolHeader *header, NSData *payload) { + messageReadyHeader = header; + messageReadyPayload = payload; + + expect(messageReadyHeader).to(beNil()); + expect(messageReadyPayload).to(beNil()); + }]; + expect(@(testProcessor.state)).to(equal(DATA_PUMP_STATE)); + expect(testProcessor.version).to(equal(2)); + }); + }); + + describe(@"in DATA_PUMP_STATE", ^{ + beforeEach(^{ + testProcessor.state = DATA_PUMP_STATE; + testProcessor.version = 3; + //need a valid header buffer. + Byte firstByte = ((testProcessor.version & 0x0f) << 4) + (0 << 3) + (1 & 0x07); //version 2 with no encryption, frametype 1 + UInt32 dataLength = 3; + const Byte testBytes[8] = {firstByte, 0x00, 0x00, 0x00, (dataLength >> 24) & 0xff, (dataLength >> 16) & 0xff, (dataLength >> 8) & 0xff, (dataLength) & 0xff }; + [testHeaderBuffer appendBytes:&testBytes length:8]; + UInt32 messageID = 0; + Byte messageIDBytes[4] = {(messageID >> 24) & 0xff, (messageID >> 16) & 0xff, (messageID >> 8) & 0xff, (messageID) & 0xff}; + [testHeaderBuffer appendBytes:&messageIDBytes length:4]; + + testProcessor.headerBuffer = testHeaderBuffer; + expectedMessageReadyHeader= [SDLProtocolHeader headerForVersion:testProcessor.version]; + [expectedMessageReadyHeader parse:testHeaderBuffer]; + + Byte testByte = 0xBA; + [testBuffer appendBytes:&testByte length:1]; + }); + + it(@"should stay in current state when dataBytesRemaining is greater than 1 and receiving a byte", ^{ + testProcessor.dataBytesRemaining = 2; + + [testProcessor processReceiveBuffer:testBuffer withMessageReadyBlock:^(SDLProtocolHeader *header, NSData *payload) { + messageReadyHeader = header; + messageReadyPayload = payload; + + expect(messageReadyHeader).to(beNil()); + expect(messageReadyPayload).to(beNil()); + }]; + expect(@(testProcessor.state)).to(equal(DATA_PUMP_STATE)); + expect(testProcessor.dataBytesRemaining).to(equal(1)); + expect(testProcessor.version).to(equal(3)); + }); + + it(@"should transition to START_STATE when dataBytesRemaining is 1 and receiving a byte", ^{ + testProcessor.dataBytesRemaining = 1; + + [testProcessor processReceiveBuffer:testBuffer withMessageReadyBlock:^(SDLProtocolHeader *header, NSData *payload) { + messageReadyHeader = header; + messageReadyPayload = payload; + + expect(messageReadyHeader).to(equal(expectedMessageReadyHeader)); + expect(messageReadyPayload).to(equal(testBuffer)); + }]; + expect(testProcessor.dataBytesRemaining).to(equal(0)); + expect(@(testProcessor.state)).to(equal(START_STATE)); + expect(testProcessor.version).to(equal(0)); + }); + }); +}); + + +QuickSpecEnd diff --git a/SmartDeviceLinkTests/SDLPresentAlertOperationSpec.m b/SmartDeviceLinkTests/SDLPresentAlertOperationSpec.m index 78dda1e82..4173b923d 100644 --- a/SmartDeviceLinkTests/SDLPresentAlertOperationSpec.m +++ b/SmartDeviceLinkTests/SDLPresentAlertOperationSpec.m @@ -42,6 +42,7 @@ @property (strong, nonatomic, readwrite) SDLAlertView *alertView; @property (assign, nonatomic) UInt16 cancelId; @property (copy, nonatomic, nullable) NSError *internalError; +@property (assign, nonatomic) BOOL alertIconUploaded; - (nullable NSError *)sdl_isValidAlertViewData:(SDLAlertView *)alertView; - (SDLAlert *)alertRPC; @@ -365,16 +366,16 @@ describe(@"SDLPresentAlertOperation", ^{ testPresentAlertOperation = [[SDLPresentAlertOperation alloc] initWithConnectionManager:mockConnectionManager fileManager:mockFileManager systemCapabilityManager:mockSystemCapabilityManager currentWindowCapability:mockCurrentWindowCapability alertView:testAlertView cancelID:testCancelID]; }); - it(@"should set the image if icons are supported on the module", ^{ - OCMStub([mockCurrentWindowCapability hasImageFieldOfName:SDLImageFieldNameAlertIcon]).andReturn(YES); + it(@"should not set the image if it fails to upload to the module", ^{ SDLAlert *testAlert = testPresentAlertOperation.alertRPC; - expect(testAlert.alertIcon.value).to(equal(testAlertView.icon.name)); + expect(testAlert.alertIcon.value).to(beNil()); }); - it(@"should not set the image if icons are not supported on the module", ^{ - OCMStub([mockCurrentWindowCapability hasImageFieldOfName:SDLImageFieldNameAlertIcon]).andReturn(NO); + it(@"should set the image if it is uploaded to the module", ^{ + testPresentAlertOperation.alertIconUploaded = YES; + SDLAlert *testAlert = testPresentAlertOperation.alertRPC; - expect(testAlert.alertIcon).to(beNil()); + expect(testAlert.alertIcon.value).to(equal(testAlertView.icon.name)); }); }); }); @@ -635,6 +636,25 @@ describe(@"SDLPresentAlertOperation", ^{ OCMVerifyAll(strictMockSystemCapabilityManager); OCMVerifyAll(strictMockCurrentWindowCapability); }); + + it(@"should not upload the image if the alert icon is a static icon", ^{ + SDLAlertView *alertView = [[SDLAlertView alloc] initWithText:@"Test" secondaryText:nil tertiaryText:nil timeout:nil showWaitIndicator:nil audioIndication:nil buttons:nil icon:[SDLArtwork artworkWithStaticIcon:SDLStaticIconNameKey]]; + testPresentAlertOperation = [[SDLPresentAlertOperation alloc] initWithConnectionManager:mockConnectionManager fileManager:strictMockFileManager systemCapabilityManager:strictMockSystemCapabilityManager currentWindowCapability:strictMockCurrentWindowCapability alertView:alertView cancelID:testCancelID]; + + OCMStub([strictMockCurrentWindowCapability hasImageFieldOfName:SDLImageFieldNameAlertIcon]).andReturn(YES); + OCMStub([strictMockFileManager hasUploadedFile:[OCMArg any]]).andReturn(NO); + OCMStub([strictMockFileManager fileNeedsUpload:[OCMArg any]]).andReturn(NO); + + OCMReject([strictMockFileManager uploadArtworks:[OCMArg any] progressHandler:[OCMArg any] completionHandler:[OCMArg any]]); + + [testPresentAlertOperation start]; + + OCMVerifyAll(strictMockFileManager); + OCMVerifyAll(strictMockSystemCapabilityManager); + OCMVerifyAll(strictMockCurrentWindowCapability); + expect(testPresentAlertOperation.alertIconUploaded).to(beTrue()); + expect(testPresentAlertOperation.alertRPC.alertIcon).toNot(beNil()); + }); }); }); @@ -781,6 +801,7 @@ describe(@"SDLPresentAlertOperation", ^{ testAlertViewWithExtraSoftButtons = [[SDLAlertView alloc] initWithText:@"text" secondaryText:@"secondaryText" tertiaryText:@"tertiaryText" timeout:@(4) showWaitIndicator:@(YES) audioIndication:testAlertAudioData buttons:@[testAlertSoftButton1, testAlertSoftButton2, testAlertSoftButton3, testAlertSoftButton4, testAlertSoftButton5, testAlertSoftButton6] icon:testAlertIcon]; testPresentAlertOperation = [[SDLPresentAlertOperation alloc] initWithConnectionManager:mockConnectionManager fileManager:mockFileManager systemCapabilityManager:mockSystemCapabilityManager currentWindowCapability:mockCurrentWindowCapability alertView:testAlertViewWithExtraSoftButtons cancelID:testCancelID]; + testPresentAlertOperation.alertIconUploaded = YES; testPresentAlertOperation.completionBlock = ^{ hasCalledOperationCompletionHandler = YES; @@ -829,7 +850,7 @@ describe(@"SDLPresentAlertOperation", ^{ testSoftButtonCapabilities.imageSupported = @YES; OCMStub([mockCurrentWindowCapability softButtonCapabilities]).andReturn(@[testSoftButtonCapabilities]); OCMStub([mockFileManager fileNeedsUpload:[OCMArg any]]).andReturn(YES); - OCMStub([mockFileManager uploadArtworks:[OCMArg any] progressHandler:[OCMArg invokeBlock] completionHandler:([OCMArg invokeBlockWithArgs: @[testAlertView.icon.name], [NSNull null], nil])]); + OCMStub([mockFileManager uploadArtworks:[OCMArg any] progressHandler:[OCMArg invokeBlock] completionHandler:([OCMArg invokeBlockWithArgs: @[], [NSNull null], nil])]); OCMStub([mockFileManager uploadFiles:[OCMArg any] progressHandler:[OCMArg invokeBlock] completionHandler:[OCMArg invokeBlock]]); SDLVersion *supportedVersion = [SDLVersion versionWithMajor:6 minor:3 patch:0]; @@ -883,8 +904,8 @@ describe(@"SDLPresentAlertOperation", ^{ SDLSoftButtonCapabilities *testSoftButtonCapabilities = [[SDLSoftButtonCapabilities alloc] init]; testSoftButtonCapabilities.imageSupported = @YES; OCMStub([mockCurrentWindowCapability softButtonCapabilities]).andReturn(@[testSoftButtonCapabilities]); - OCMStub([mockFileManager fileNeedsUpload:[OCMArg any]]).andReturn(NO); - OCMStub([mockFileManager uploadArtworks:[OCMArg any] progressHandler:[OCMArg invokeBlock] completionHandler:[OCMArg invokeBlock]]); + OCMStub([mockFileManager fileNeedsUpload:[OCMArg any]]).andReturn(YES); + OCMStub([mockFileManager uploadArtworks:[OCMArg any] progressHandler:[OCMArg invokeBlock] completionHandler:([OCMArg invokeBlockWithArgs: @[testAlertView.icon.name], [NSNull null], nil])]); OCMStub([mockFileManager uploadFiles:[OCMArg any] progressHandler:[OCMArg invokeBlock] completionHandler:[OCMArg invokeBlock]]); SDLVersion *supportedVersion = [SDLVersion versionWithMajor:6 minor:3 patch:0]; diff --git a/SmartDeviceLinkTests/SDLSystemCapabilityManagerSpec.m b/SmartDeviceLinkTests/SDLSystemCapabilityManagerSpec.m index 613030611..5343c4397 100644 --- a/SmartDeviceLinkTests/SDLSystemCapabilityManagerSpec.m +++ b/SmartDeviceLinkTests/SDLSystemCapabilityManagerSpec.m @@ -95,9 +95,16 @@ describe(@"a system capability manager", ^{ imageField.imageTypeSupported = @[SDLFileTypePNG]; imageField.imageResolution = [[SDLImageResolution alloc] initWithWidth:42 height:4711]; testDisplayCapabilities.imageFields = @[imageField]; - testDisplayCapabilities.mediaClockFormats = @[]; + testDisplayCapabilities.mediaClockFormats = @[SDLMediaClockFormatClock1, SDLMediaClockFormatClock2]; testDisplayCapabilities.templatesAvailable = @[@"DEFAULT", @"MEDIA"]; testDisplayCapabilities.numCustomPresetsAvailable = @(8); + SDLScreenParams *screenParams = [[SDLScreenParams alloc] init]; + [screenParams setResolution:[[SDLImageResolution alloc] initWithWidth:675 height:960]]; + [screenParams setTouchEventAvailable:[[SDLTouchEventCapabilities alloc] init]]; + [screenParams.touchEventAvailable setPressAvailable:@YES]; + [screenParams.touchEventAvailable setMultiTouchAvailable:@YES]; + [screenParams.touchEventAvailable setDoublePressAvailable:@YES]; + testDisplayCapabilities.screenParams = screenParams; #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wdeprecated-declarations" @@ -702,18 +709,28 @@ describe(@"a system capability manager", ^{ expect(testSystemCapabilityManager.defaultMainWindowCapability.textFields).to(haveCount(38)); expect(testSystemCapabilityManager.defaultMainWindowCapability.imageFields).to(haveCount(18)); }); + + it(@"should set the default window id if window id is missing", ^{ + // Set window id to nil. windowID can be potentially nil in real applications + testSystemCapabilityManager.displays[0].windowCapabilities[0].windowID = nil; + + expect(testSystemCapabilityManager.defaultMainWindowCapability.windowID).to(equal(SDLPredefinedWindowsDefaultWindow)); + expect([testSystemCapabilityManager windowCapabilityWithWindowID:0].windowID).to(equal(SDLPredefinedWindowsDefaultWindow)); + }); }); }); // when updating display capabilities with OnSystemCapabilityUpdated describe(@"when updating display capabilities with OnSystemCapabilityUpdated", ^{ it(@"should properly update display capability including conversion two times", ^{ + // Set to display capabilities that have screenParams and mediaClockFormats set + testSystemCapabilityManager.displayCapabilities = testDisplayCapabilities; // two times because capabilities are just saved in first run but merged/updated in subsequent runs for (int i = 0; i < 2; i++) { testDisplayCapabilities.displayName = [NSString stringWithFormat:@"Display %i", i]; testDisplayCapabilities.graphicSupported = i == 0 ? @(NO) : @(YES); testDisplayCapabilities.templatesAvailable = @[[NSString stringWithFormat:@"Template %i", i]]; - + SDLWindowTypeCapabilities *windowTypeCapabilities = [[SDLWindowTypeCapabilities alloc] initWithType:SDLWindowTypeMain maximumNumberOfWindows:1]; SDLDisplayCapability *displayCapability = [[SDLDisplayCapability alloc] initWithDisplayName:testDisplayCapabilities.displayName]; displayCapability.windowTypeSupported = @[windowTypeCapabilities]; @@ -728,16 +745,18 @@ describe(@"a system capability manager", ^{ defaultWindowCapability.imageTypeSupported = testDisplayCapabilities.graphicSupported.boolValue ? @[SDLImageTypeStatic, SDLImageTypeDynamic] : @[SDLImageTypeStatic]; displayCapability.windowCapabilities = @[defaultWindowCapability]; NSArray<SDLDisplayCapability *> *newDisplayCapabilityList = testDisplayCapabilityList = @[displayCapability]; - + SDLSystemCapability *newCapability = [[SDLSystemCapability alloc] initWithDisplayCapabilities:newDisplayCapabilityList]; SDLOnSystemCapabilityUpdated *testUpdateNotification = [[SDLOnSystemCapabilityUpdated alloc] initWithSystemCapability:newCapability]; SDLRPCNotificationNotification *notification = [[SDLRPCNotificationNotification alloc] initWithName:SDLDidReceiveSystemCapabilityUpdatedNotification object:nil rpcNotification:testUpdateNotification]; [[NSNotificationCenter defaultCenter] postNotification:notification]; - + expect(testSystemCapabilityManager.displays).to(equal(testDisplayCapabilityList)); #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wdeprecated" expect(testSystemCapabilityManager.displayCapabilities).to(equal(testDisplayCapabilities)); + expect(testSystemCapabilityManager.displayCapabilities.screenParams).to(equal(testDisplayCapabilities.screenParams)); + expect(testSystemCapabilityManager.displayCapabilities.mediaClockFormats).to(equal(testDisplayCapabilities.mediaClockFormats)); expect(testSystemCapabilityManager.buttonCapabilities).to(equal(testButtonCapabilities)); expect(testSystemCapabilityManager.softButtonCapabilities).to(equal(testSoftButtonCapabilities)); expect(testSystemCapabilityManager.presetBankCapabilities).to(beNil()); |