Files
2020-11-21 17:02:19 +04:00

1671 lines
65 KiB
Objective-C

// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
#import "MSACAnalytics+Validation.h"
#import "MSACAnalyticsCategory.h"
#import "MSACAnalyticsPrivate.h"
#import "MSACAnalyticsTransmissionTargetPrivate.h"
#import "MSACAppCenter.h"
#import "MSACAppCenterInternal.h"
#import "MSACAppCenterPrivate.h"
#import "MSACAppCenterUserDefaultsPrivate.h"
#import "MSACBooleanTypedProperty.h"
#import "MSACChannelGroupDefault.h"
#import "MSACChannelUnitConfiguration.h"
#import "MSACChannelUnitDefault.h"
#import "MSACConstants+Internal.h"
#import "MSACDateTimeTypedProperty.h"
#import "MSACDoubleTypedProperty.h"
#import "MSACEventLog.h"
#import "MSACEventPropertiesInternal.h"
#import "MSACLongTypedProperty.h"
#import "MSACMockUserDefaults.h"
#import "MSACPageLog.h"
#import "MSACSessionContextPrivate.h"
#import "MSACSessionTrackerPrivate.h"
#import "MSACStringTypedProperty.h"
#import "MSACTestFrameworks.h"
static NSString *const kMSACAnalyticsGroupId = @"Analytics";
static NSString *const kMSACTypeEvent = @"event";
static NSString *const kMSACTypePage = @"page";
static NSString *const kMSACTestAppSecret = @"TestAppSecret";
static NSString *const kMSACTestTransmissionToken = @"AnalyticsTestTransmissionToken";
static NSString *const kMSACTestTransmissionToken2 = @"AnalyticsTestTransmissionToken2";
static NSString *const kMSACAnalyticsServiceName = @"Analytics";
@class MSACMockAnalyticsDelegate;
@interface MSACAnalyticsTests : XCTestCase <MSACAnalyticsDelegate>
@property(nonatomic) MSACMockUserDefaults *settingsMock;
@property(nonatomic) id sessionContextMock;
@property(nonatomic) id channelGroupMock;
@property(nonatomic) id channelUnitMock;
@property(nonatomic) id channelUnitCriticalMock;
@end
@interface MSACServiceAbstract ()
- (BOOL)isEnabled;
- (void)setEnabled:(BOOL)enabled;
@end
@interface MSACAnalytics ()
- (BOOL)channelUnit:(id<MSACChannelUnitProtocol>)channelUnit shouldFilterLog:(id<MSACLog>)log;
@end
/*
* FIXME: Log manager mock is holding sessionTracker instance even after dealloc and this causes session tracker test failures. There is a
* PR in OCMock that seems a related issue. https://github.com/erikdoe/ocmock/pull/348 Stopping session tracker after applyEnabledState
* calls for hack to avoid failures.
*/
@implementation MSACAnalyticsTests
- (void)setUp {
[super setUp];
[MSACAppCenter resetSharedInstance];
// Mock NSUserDefaults.
self.settingsMock = [MSACMockUserDefaults new];
// Mock session context.
[MSACSessionContext resetSharedInstance];
self.sessionContextMock = OCMClassMock([MSACSessionContext class]);
OCMStub([self.sessionContextMock sharedInstance]).andReturn(self.sessionContextMock);
// Mock channel.
self.channelGroupMock = OCMClassMock([MSACChannelGroupDefault class]);
self.channelUnitMock = OCMProtocolMock(@protocol(MSACChannelUnitProtocol));
self.channelUnitCriticalMock = OCMProtocolMock(@protocol(MSACChannelUnitProtocol));
[MSACAnalytics sharedInstance].criticalChannelUnit = self.channelUnitCriticalMock;
OCMStub([self.channelGroupMock alloc]).andReturn(self.channelGroupMock);
OCMStub([self.channelGroupMock initWithHttpClient:OCMOCK_ANY installId:OCMOCK_ANY logUrl:OCMOCK_ANY]).andReturn(self.channelGroupMock);
OCMStub([self.channelGroupMock addChannelUnitWithConfiguration:hasProperty(@"groupId", endsWith(kMSACCriticalChannelSuffix))])
.andReturn(self.channelUnitCriticalMock);
OCMStub([self.channelGroupMock addChannelUnitWithConfiguration:hasProperty(@"groupId", equalTo(kMSACAnalyticsGroupId))])
.andReturn(self.channelUnitMock);
}
- (void)tearDown {
[MSACSessionContext resetSharedInstance];
[MSACAnalytics resetSharedInstance];
[self.settingsMock stopMocking];
[self.sessionContextMock stopMocking];
[super tearDown];
}
#pragma mark - Tests
- (void)testMigrateOnInit {
NSString *key = [NSString stringWithFormat:kMSACMockMigrationKey, @"Analytics"];
XCTAssertNotNil([self.settingsMock objectForKey:key]);
}
- (void)testValidateEventName {
const int maxEventNameLength = 256;
// If
NSString *validEventName = @"validEventName";
NSString *shortEventName = @"e";
NSString *eventName256 = [@"" stringByPaddingToLength:maxEventNameLength withString:@"eventName256" startingAtIndex:0];
NSString *nullableEventName = nil;
NSString *emptyEventName = @"";
NSString *tooLongEventName = [@"" stringByPaddingToLength:(maxEventNameLength + 1) withString:@"tooLongEventName" startingAtIndex:0];
// When
NSString *valid = [[MSACAnalytics sharedInstance] validateEventName:validEventName forLogType:kMSACTypeEvent];
NSString *validShortEventName = [[MSACAnalytics sharedInstance] validateEventName:shortEventName forLogType:kMSACTypeEvent];
NSString *validEventName256 = [[MSACAnalytics sharedInstance] validateEventName:eventName256 forLogType:kMSACTypeEvent];
NSString *validNullableEventName = [[MSACAnalytics sharedInstance] validateEventName:nullableEventName forLogType:kMSACTypeEvent];
NSString *validEmptyEventName = [[MSACAnalytics sharedInstance] validateEventName:emptyEventName forLogType:kMSACTypeEvent];
NSString *validTooLongEventName = [[MSACAnalytics sharedInstance] validateEventName:tooLongEventName forLogType:kMSACTypeEvent];
// Then
XCTAssertNotNil(valid);
XCTAssertNotNil(validShortEventName);
XCTAssertNotNil(validEventName256);
XCTAssertNil(validNullableEventName);
XCTAssertNil(validEmptyEventName);
XCTAssertNotNil(validTooLongEventName);
XCTAssertEqual([validTooLongEventName length], maxEventNameLength);
}
- (void)testApplyEnabledStateWorks {
[[MSACAnalytics sharedInstance] startWithChannelGroup:OCMProtocolMock(@protocol(MSACChannelGroupProtocol))
appSecret:kMSACTestAppSecret
transmissionTargetToken:nil
fromApplication:YES];
MSACServiceAbstract *service = [MSACAnalytics sharedInstance];
[service setEnabled:YES];
XCTAssertTrue([service isEnabled]);
[service setEnabled:NO];
XCTAssertFalse([service isEnabled]);
[service setEnabled:YES];
XCTAssertTrue([service isEnabled]);
// FIXME: logManager holds session tracker somehow and it causes other test failures. Stop it for hack.
[[MSACAnalytics sharedInstance].sessionTracker stop];
}
- (void)testSetTransmissionIntervalApplied {
// If
NSUInteger testInterval = 5;
// When
[MSACAnalytics setTransmissionInterval:testInterval];
[[MSACAnalytics sharedInstance] startWithChannelGroup:self.channelGroupMock
appSecret:kMSACTestAppSecret
transmissionTargetToken:nil
fromApplication:YES];
// Then
OCMVerify(
[self.channelGroupMock addChannelUnitWithConfiguration:allOf(hasProperty(@"flushInterval", equalToUnsignedInteger(testInterval)),
hasProperty(@"groupId", equalTo(kMSACAnalyticsGroupId)), nil)]);
// FIXME: logManager holds session tracker somehow and it causes other test failures. Stop it for hack.
[[MSACAnalytics sharedInstance].sessionTracker stop];
}
- (void)testSetTransmissionIntervalNotApplied {
// If
NSUInteger testInterval = 2;
// When
[MSACAnalytics setTransmissionInterval:testInterval];
[[MSACAnalytics sharedInstance] startWithChannelGroup:self.channelGroupMock
appSecret:kMSACTestAppSecret
transmissionTargetToken:nil
fromApplication:YES];
// Then
OCMVerify([self.channelGroupMock addChannelUnitWithConfiguration:allOf(hasProperty(@"flushInterval", equalToUnsignedInteger(3)),
hasProperty(@"groupId", equalTo(kMSACAnalyticsGroupId)), nil)]);
// FIXME: logManager holds session tracker somehow and it causes other test failures. Stop it for hack.
[[MSACAnalytics sharedInstance].sessionTracker stop];
}
- (void)testSetTransmissionIntervalNotAppliedIfHigherThanDay {
// If
NSUInteger testInterval = 25 * 60 * 60;
// When
[MSACAnalytics setTransmissionInterval:testInterval];
[[MSACAnalytics sharedInstance] startWithChannelGroup:self.channelGroupMock
appSecret:kMSACTestAppSecret
transmissionTargetToken:nil
fromApplication:YES];
// Then
OCMVerify([self.channelGroupMock addChannelUnitWithConfiguration:allOf(hasProperty(@"flushInterval", equalToUnsignedInteger(3)),
hasProperty(@"groupId", equalTo(kMSACAnalyticsGroupId)), nil)]);
// FIXME: logManager holds session tracker somehow and it causes other test failures. Stop it for hack.
[[MSACAnalytics sharedInstance].sessionTracker stop];
}
- (void)testSetTransmissionIntervalNotAppliedAfterStart {
// If
NSUInteger testInterval = 5;
id<MSACChannelGroupProtocol> channelGroupMock = OCMProtocolMock(@protocol(MSACChannelGroupProtocol));
// When
[[MSACAnalytics sharedInstance] startWithChannelGroup:channelGroupMock
appSecret:kMSACTestAppSecret
transmissionTargetToken:nil
fromApplication:YES];
// Make sure that interval is not set after service start.
[MSACAnalytics setTransmissionInterval:testInterval];
// Then
// FIXME: logManager holds session tracker somehow and it causes other test failures. Stop it for hack.
[[MSACAnalytics sharedInstance].sessionTracker stop];
XCTAssertNotEqual([MSACAnalytics sharedInstance].flushInterval, testInterval);
}
- (void)testDisablingAnalyticsClearsSessionHistory {
[[MSACAnalytics sharedInstance] startWithChannelGroup:OCMProtocolMock(@protocol(MSACChannelGroupProtocol))
appSecret:kMSACTestAppSecret
transmissionTargetToken:nil
fromApplication:YES];
MSACServiceAbstract *service = [MSACAnalytics sharedInstance];
[service setEnabled:NO];
XCTAssertFalse([service isEnabled]);
OCMVerify([self.sessionContextMock clearSessionHistoryAndKeepCurrentSession:NO]);
}
- (void)testTrackPageCalledWhenAutoPageTrackingEnabled {
// If
id analyticsMock = OCMPartialMock([MSACAnalytics sharedInstance]);
id analyticsCategoryMock = OCMClassMock([MSACAnalyticsCategory class]);
NSString *testPageName = @"TestPage";
OCMStub([analyticsCategoryMock missedPageViewName]).andReturn(testPageName);
[MSACAnalytics setAutoPageTrackingEnabled:YES];
MSACServiceAbstract *service = [MSACAnalytics sharedInstance];
[MSACAppCenter configureWithAppSecret:kMSACTestAppSecret];
// When
[[MSACAnalytics sharedInstance] startWithChannelGroup:OCMProtocolMock(@protocol(MSACChannelGroupProtocol))
appSecret:kMSACTestAppSecret
transmissionTargetToken:nil
fromApplication:YES];
// FIXME: logManager holds session tracker somehow and it causes other test failures. Stop it for hack.
[[MSACAnalytics sharedInstance].sessionTracker stop];
XCTestExpectation *expectation = [self expectationWithDescription:@"Wait for block in applyEnabledState to be dispatched"];
dispatch_async(dispatch_get_main_queue(), ^{
[expectation fulfill];
});
[self waitForExpectationsWithTimeout:1
handler:^(NSError *error) {
if (error) {
XCTFail(@"Expectation Failed with error: %@", error);
}
// Then
XCTAssertTrue([service isEnabled]);
OCMVerify([analyticsMock trackPage:testPageName withProperties:nil]);
}];
}
- (void)testSettingDelegateWorks {
id<MSACAnalyticsDelegate> delegateMock = OCMProtocolMock(@protocol(MSACAnalyticsDelegate));
[MSACAnalytics setDelegate:delegateMock];
XCTAssertNotNil([MSACAnalytics sharedInstance].delegate);
XCTAssertEqual([MSACAnalytics sharedInstance].delegate, delegateMock);
}
- (void)testAnalyticsDelegateWithoutImplementations {
// If
[MSACAnalytics setDelegate:self];
// When
MSACEventLog *eventLog = [MSACEventLog new];
[[MSACAnalytics sharedInstance] channel:self.channelUnitMock willSendLog:eventLog];
[[MSACAnalytics sharedInstance] channel:self.channelUnitMock didSucceedSendingLog:eventLog];
[[MSACAnalytics sharedInstance] channel:self.channelUnitMock didFailSendingLog:eventLog withError:nil];
// Then - no crashes
}
- (void)testAnalyticsDelegateMethodsAreCalled {
// If
id<MSACAnalyticsDelegate> delegateMock = OCMProtocolMock(@protocol(MSACAnalyticsDelegate));
[MSACAnalytics setDelegate:delegateMock];
// When
MSACEventLog *eventLog = [MSACEventLog new];
[[MSACAnalytics sharedInstance] channel:self.channelUnitMock willSendLog:eventLog];
[[MSACAnalytics sharedInstance] channel:self.channelUnitMock didSucceedSendingLog:eventLog];
[[MSACAnalytics sharedInstance] channel:self.channelUnitMock didFailSendingLog:eventLog withError:nil];
// Then
OCMVerify([delegateMock analytics:[MSACAnalytics sharedInstance] willSendEventLog:eventLog]);
OCMVerify([delegateMock analytics:[MSACAnalytics sharedInstance] didSucceedSendingEventLog:eventLog]);
OCMVerify([delegateMock analytics:[MSACAnalytics sharedInstance] didFailSendingEventLog:eventLog withError:nil]);
}
- (void)testAnalyticsLogsVerificationIsCalled {
// If
MSACEventLog *eventLog = [MSACEventLog new];
eventLog.name = @"test";
eventLog.properties = @{@"test" : @"test"};
MSACPageLog *pageLog = [MSACPageLog new];
MSACLogWithNameAndProperties *analyticsLog = [MSACLogWithNameAndProperties new];
id analyticsMock = OCMPartialMock([MSACAnalytics sharedInstance]);
OCMExpect([analyticsMock validateLog:eventLog]).andForwardToRealObject();
OCMExpect([analyticsMock validateEventName:@"test" forLogType:@"event"]).andForwardToRealObject();
OCMExpect([analyticsMock validateProperties:OCMOCK_ANY forLogName:@"test" andType:@"event"]).andForwardToRealObject();
OCMExpect([analyticsMock validateLog:pageLog]).andForwardToRealObject();
OCMExpect([analyticsMock validateEventName:OCMOCK_ANY forLogType:@"page"]).andForwardToRealObject();
OCMReject([analyticsMock validateProperties:OCMOCK_ANY forLogName:OCMOCK_ANY andType:@"page"]);
OCMReject([analyticsMock validateLog:analyticsLog]);
// When
[[MSACAnalytics sharedInstance] channelUnit:nil shouldFilterLog:eventLog];
[[MSACAnalytics sharedInstance] channelUnit:nil shouldFilterLog:pageLog];
[[MSACAnalytics sharedInstance] channelUnit:nil shouldFilterLog:analyticsLog];
// Then
OCMVerifyAll(analyticsMock);
}
- (void)testAnalyticsLogsVerificationIsCalledWithWrongClass {
// If
NSObject *notAnalyticsLog = [NSObject new];
// When
BOOL wrongClass = [MSACLogWithNameAndProperties isEqual:notAnalyticsLog];
BOOL wrongType = [MSACLogWithNameAndProperties isEqual:@"invalid equal test"];
// Then
XCTAssertFalse(wrongClass);
XCTAssertFalse(wrongType);
}
- (void)testTrackEventWithoutProperties {
// If
__block NSString *name;
__block NSString *type;
NSString *expectedName = @"gotACoffee";
OCMStub([self.channelUnitMock enqueueItem:[OCMArg isKindOfClass:[MSACLogWithProperties class]] flags:MSACFlagsDefault])
.andDo(^(NSInvocation *invocation) {
MSACEventLog *log;
[invocation getArgument:&log atIndex:2];
type = log.type;
name = log.name;
});
[MSACAppCenter configureWithAppSecret:kMSACTestAppSecret];
[[MSACAnalytics sharedInstance] startWithChannelGroup:self.channelGroupMock
appSecret:kMSACTestAppSecret
transmissionTargetToken:nil
fromApplication:YES];
// When
[MSACAnalytics trackEvent:expectedName];
// Then
assertThat(type, is(kMSACTypeEvent));
assertThat(name, is(expectedName));
}
- (void)testTrackEventWithPropertiesNilWhenAnalyticsDisabled {
// If
id analyticsMock = OCMPartialMock([MSACAnalytics sharedInstance]);
OCMStub([analyticsMock isEnabled]).andReturn(NO);
[MSACAppCenter configureWithAppSecret:kMSACTestAppSecret];
[[MSACAnalytics sharedInstance] startWithChannelGroup:self.channelGroupMock
appSecret:kMSACTestAppSecret
transmissionTargetToken:nil
fromApplication:YES];
// When
OCMReject([self.channelUnitMock enqueueItem:OCMOCK_ANY flags:MSACFlagsDefault]);
[[MSACAnalytics sharedInstance] trackEvent:@"Some event" withProperties:nil forTransmissionTarget:nil flags:MSACFlagsDefault];
// Then
OCMVerifyAll(self.channelUnitMock);
}
- (void)testTrackEventWithTypedPropertiesNilWhenAnalyticsDisabled {
// If
id analyticsMock = OCMPartialMock([MSACAnalytics sharedInstance]);
OCMStub([analyticsMock isEnabled]).andReturn(NO);
[MSACAppCenter configureWithAppSecret:kMSACTestAppSecret];
[[MSACAnalytics sharedInstance] startWithChannelGroup:self.channelGroupMock
appSecret:kMSACTestAppSecret
transmissionTargetToken:nil
fromApplication:YES];
// When
OCMReject([self.channelUnitMock enqueueItem:OCMOCK_ANY flags:MSACFlagsDefault]);
[[MSACAnalytics sharedInstance] trackEvent:@"Some event" withTypedProperties:nil forTransmissionTarget:nil flags:MSACFlagsDefault];
// Then
OCMVerifyAll(self.channelUnitMock);
}
- (void)testTrackEventWithPropertiesNilWhenTransmissionTargetDisabled {
// If
[MSACAppCenter configureWithAppSecret:kMSACTestAppSecret];
[[MSACAnalytics sharedInstance] startWithChannelGroup:self.channelGroupMock
appSecret:kMSACTestAppSecret
transmissionTargetToken:nil
fromApplication:YES];
// When
OCMReject([self.channelUnitMock enqueueItem:OCMOCK_ANY flags:MSACFlagsDefault]);
MSACAnalyticsTransmissionTarget *target = [MSACAnalytics transmissionTargetForToken:@"test"];
[target setEnabled:NO];
[[MSACAnalytics sharedInstance] trackEvent:@"Some event" withProperties:nil forTransmissionTarget:target flags:MSACFlagsDefault];
// Then
OCMVerifyAll(self.channelUnitMock);
// FIXME: logManager holds session tracker somehow and it causes other test failures. Stop it for hack.
[[MSACAnalytics sharedInstance].sessionTracker stop];
}
- (void)testTrackEventWithPropertiesWhenTransmissionTargetProvided {
// If
__block NSUInteger propertiesCount = 0;
[MSACAppCenter configureWithAppSecret:kMSACTestAppSecret];
[[MSACAnalytics sharedInstance] startWithChannelGroup:self.channelGroupMock
appSecret:kMSACTestAppSecret
transmissionTargetToken:nil
fromApplication:YES];
OCMStub([self.channelUnitMock enqueueItem:[OCMArg isKindOfClass:[MSACEventLog class]] flags:MSACFlagsDefault])
.andDo(^(NSInvocation *invocation) {
MSACEventLog *log;
[invocation getArgument:&log atIndex:2];
propertiesCount = log.typedProperties.properties.count;
});
// When
NSMutableDictionary *properties = [NSMutableDictionary new];
for (int i = 0; i < 100; i++) {
properties[[@"prop" stringByAppendingFormat:@"%d", i]] = [@"val" stringByAppendingFormat:@"%d", i];
}
MSACAnalyticsTransmissionTarget *target = [MSACAnalytics transmissionTargetForToken:@"test"];
[[MSACAnalytics sharedInstance] trackEvent:@"Some event" withProperties:properties forTransmissionTarget:target flags:MSACFlagsDefault];
// Then
XCTAssertEqual(properties.count, propertiesCount);
}
- (void)testTrackEventSetsTagWhenTransmissionTargetProvided {
// If
__block NSObject *tag;
[MSACAppCenter configureWithAppSecret:kMSACTestAppSecret];
[[MSACAnalytics sharedInstance] startWithChannelGroup:self.channelGroupMock
appSecret:kMSACTestAppSecret
transmissionTargetToken:nil
fromApplication:YES];
OCMStub([self.channelUnitMock enqueueItem:[OCMArg isKindOfClass:[MSACEventLog class]] flags:MSACFlagsDefault])
.andDo(^(NSInvocation *invocation) {
MSACEventLog *log;
[invocation getArgument:&log atIndex:2];
tag = log.tag;
});
// When
MSACAnalyticsTransmissionTarget *target = [MSACAnalytics transmissionTargetForToken:@"test"];
[[MSACAnalytics sharedInstance] trackEvent:@"Some event" withProperties:nil forTransmissionTarget:target flags:MSACFlagsDefault];
// Then
XCTAssertEqualObjects(tag, target);
}
- (void)testTrackEventDoesNotSetUserIdForAppCenter {
// If
__block MSACEventLog *log;
[MSACAppCenter setUserId:@"c:test"];
[MSACAppCenter configureWithAppSecret:kMSACTestAppSecret];
[[MSACAnalytics sharedInstance] startWithChannelGroup:self.channelGroupMock
appSecret:kMSACTestAppSecret
transmissionTargetToken:nil
fromApplication:YES];
OCMStub([self.channelUnitMock enqueueItem:[OCMArg isKindOfClass:[MSACEventLog class]] flags:MSACFlagsDefault])
.andDo(^(NSInvocation *invocation) {
[invocation getArgument:&log atIndex:2];
});
// When
[MSACAnalytics trackEvent:@"Some event"];
// Then
XCTAssertNotNil(log);
XCTAssertNil(log.userId);
}
- (void)testTrackEventWithTypedPropertiesNilWhenTransmissionTargetDisabled {
// If
[MSACAppCenter configureWithAppSecret:kMSACTestAppSecret];
[[MSACAnalytics sharedInstance] startWithChannelGroup:self.channelGroupMock
appSecret:kMSACTestAppSecret
transmissionTargetToken:nil
fromApplication:YES];
// When
OCMReject([self.channelUnitMock enqueueItem:OCMOCK_ANY flags:MSACFlagsDefault]);
MSACAnalyticsTransmissionTarget *target = [MSACAnalytics transmissionTargetForToken:@"test"];
[target setEnabled:NO];
[[MSACAnalytics sharedInstance] trackEvent:@"Some event" withTypedProperties:nil forTransmissionTarget:target flags:MSACFlagsDefault];
// Then
OCMVerifyAll(self.channelUnitMock);
// FIXME: logManager holds session tracker somehow and it causes other test failures. Stop it for hack.
[[MSACAnalytics sharedInstance].sessionTracker stop];
}
- (void)testTrackEventWithPropertiesNilAndInvalidName {
// If
NSString *invalidEventName = nil;
id analyticsMock = OCMPartialMock([MSACAnalytics sharedInstance]);
[MSACAppCenter configureWithAppSecret:kMSACTestAppSecret];
[[MSACAnalytics sharedInstance] startWithChannelGroup:self.channelGroupMock
appSecret:kMSACTestAppSecret
transmissionTargetToken:nil
fromApplication:YES];
// When
OCMExpect([self.channelUnitMock enqueueItem:OCMOCK_ANY flags:MSACFlagsDefault]);
// Will be validated in shouldFilterLog callback instead.
OCMReject([analyticsMock validateEventName:OCMOCK_ANY forLogType:OCMOCK_ANY]);
OCMReject([analyticsMock validateProperties:OCMOCK_ANY forLogName:OCMOCK_ANY andType:OCMOCK_ANY]);
[[MSACAnalytics sharedInstance] trackEvent:invalidEventName withProperties:nil forTransmissionTarget:nil flags:MSACFlagsDefault];
// Then
OCMVerifyAll(self.channelUnitMock);
OCMVerifyAll(analyticsMock);
}
- (void)testTrackEventWithTypedPropertiesNilAndInvalidName {
// If
NSString *invalidEventName = nil;
id analyticsMock = OCMPartialMock([MSACAnalytics sharedInstance]);
[MSACAppCenter configureWithAppSecret:kMSACTestAppSecret];
[[MSACAnalytics sharedInstance] startWithChannelGroup:self.channelGroupMock
appSecret:kMSACTestAppSecret
transmissionTargetToken:nil
fromApplication:YES];
// When
OCMExpect([self.channelUnitMock enqueueItem:OCMOCK_ANY flags:MSACFlagsDefault]);
// Will be validated in shouldFilterLog callback instead.
OCMReject([analyticsMock validateEventName:OCMOCK_ANY forLogType:OCMOCK_ANY]);
OCMReject([analyticsMock validateProperties:OCMOCK_ANY forLogName:OCMOCK_ANY andType:OCMOCK_ANY]);
[[MSACAnalytics sharedInstance] trackEvent:invalidEventName withTypedProperties:nil forTransmissionTarget:nil flags:MSACFlagsDefault];
// Then
OCMVerifyAll(self.channelUnitMock);
OCMVerifyAll(analyticsMock);
}
- (void)testTrackEventWithProperties {
// If
__block NSString *type;
__block NSString *name;
__block MSACEventProperties *eventProperties;
NSString *expectedName = @"gotACoffee";
NSDictionary *expectedProperties = @{@"milk" : @"yes", @"cookie" : @"of course"};
OCMStub([self.channelUnitMock enqueueItem:[OCMArg isKindOfClass:[MSACEventLog class]] flags:MSACFlagsDefault])
.andDo(^(NSInvocation *invocation) {
MSACEventLog *log;
[invocation getArgument:&log atIndex:2];
type = log.type;
name = log.name;
eventProperties = log.typedProperties;
});
[MSACAppCenter configureWithAppSecret:kMSACTestAppSecret];
[[MSACAnalytics sharedInstance] startWithChannelGroup:self.channelGroupMock
appSecret:kMSACTestAppSecret
transmissionTargetToken:nil
fromApplication:YES];
// When
[MSACAnalytics trackEvent:expectedName withProperties:expectedProperties];
// Then
assertThat(type, is(kMSACTypeEvent));
assertThat(name, is(expectedName));
for (MSACTypedProperty *typedProperty in [eventProperties.properties objectEnumerator]) {
assertThat(typedProperty, isA([MSACStringTypedProperty class]));
MSACStringTypedProperty *stringTypedProperty = (MSACStringTypedProperty *)typedProperty;
assertThat(stringTypedProperty.value, equalTo(expectedProperties[stringTypedProperty.name]));
}
XCTAssertEqual([expectedProperties count], [eventProperties.properties count]);
}
- (void)testTrackEventWithTypedProperties {
// If
__block NSString *type;
__block NSString *name;
__block MSACEventProperties *eventProperties;
MSACEventProperties *expectedProperties = [MSACEventProperties new];
[expectedProperties setString:@"string" forKey:@"stringKey"];
[expectedProperties setBool:YES forKey:@"boolKey"];
[expectedProperties setDate:[NSDate new] forKey:@"dateKey"];
[expectedProperties setInt64:123 forKey:@"longKey"];
[expectedProperties setDouble:1.23e2 forKey:@"doubleKey"];
NSString *expectedName = @"gotACoffee";
OCMStub([self.channelUnitMock enqueueItem:[OCMArg isKindOfClass:[MSACEventLog class]] flags:MSACFlagsDefault])
.andDo(^(NSInvocation *invocation) {
MSACEventLog *log;
[invocation getArgument:&log atIndex:2];
type = log.type;
name = log.name;
eventProperties = log.typedProperties;
});
[MSACAppCenter configureWithAppSecret:kMSACTestAppSecret];
[[MSACAnalytics sharedInstance] startWithChannelGroup:self.channelGroupMock
appSecret:kMSACTestAppSecret
transmissionTargetToken:nil
fromApplication:YES];
// When
[MSACAnalytics trackEvent:expectedName withTypedProperties:expectedProperties];
// Then
assertThat(type, is(kMSACTypeEvent));
assertThat(name, is(expectedName));
for (NSString *propertyKey in eventProperties.properties) {
MSACTypedProperty *typedProperty = eventProperties.properties[propertyKey];
XCTAssertEqual(typedProperty.name, propertyKey);
if ([typedProperty isKindOfClass:[MSACBooleanTypedProperty class]]) {
MSACBooleanTypedProperty *expectedProperty = (MSACBooleanTypedProperty *)expectedProperties.properties[propertyKey];
MSACBooleanTypedProperty *property = (MSACBooleanTypedProperty *)eventProperties.properties[propertyKey];
XCTAssertEqual(property.value, expectedProperty.value);
} else if ([typedProperty isKindOfClass:[MSACDoubleTypedProperty class]]) {
MSACDoubleTypedProperty *expectedProperty = (MSACDoubleTypedProperty *)expectedProperties.properties[propertyKey];
MSACDoubleTypedProperty *property = (MSACDoubleTypedProperty *)eventProperties.properties[propertyKey];
XCTAssertEqual(property.value, expectedProperty.value);
} else if ([typedProperty isKindOfClass:[MSACLongTypedProperty class]]) {
MSACLongTypedProperty *expectedProperty = (MSACLongTypedProperty *)expectedProperties.properties[propertyKey];
MSACLongTypedProperty *property = (MSACLongTypedProperty *)eventProperties.properties[propertyKey];
XCTAssertEqual(property.value, expectedProperty.value);
} else if ([typedProperty isKindOfClass:[MSACStringTypedProperty class]]) {
MSACStringTypedProperty *expectedProperty = (MSACStringTypedProperty *)expectedProperties.properties[propertyKey];
MSACStringTypedProperty *property = (MSACStringTypedProperty *)eventProperties.properties[propertyKey];
XCTAssertEqualObjects(property.value, expectedProperty.value);
} else if ([typedProperty isKindOfClass:[MSACDateTimeTypedProperty class]]) {
MSACDateTimeTypedProperty *expectedProperty = (MSACDateTimeTypedProperty *)expectedProperties.properties[propertyKey];
MSACDateTimeTypedProperty *property = (MSACDateTimeTypedProperty *)eventProperties.properties[propertyKey];
XCTAssertEqual(property.value, expectedProperty.value);
}
[expectedProperties.properties removeObjectForKey:propertyKey];
}
XCTAssertEqual([expectedProperties.properties count], 0);
}
- (void)testTrackEventWithPropertiesWithNormalPersistenceFlag {
// If
__block NSString *actualType;
__block NSString *actualName;
__block MSACFlags actualFlags;
NSString *expectedName = @"gotACoffee";
OCMStub([[self.channelUnitMock ignoringNonObjectArgs] enqueueItem:[OCMArg isKindOfClass:[MSACEventLog class]] flags:(MSACFlags)0])
.andDo(^(NSInvocation *invocation) {
MSACEventLog *log;
[invocation getArgument:&log atIndex:2];
actualType = log.type;
actualName = log.name;
MSACFlags flags;
[invocation getArgument:&flags atIndex:3];
actualFlags = flags;
});
[MSACAppCenter configureWithAppSecret:kMSACTestAppSecret];
[[MSACAnalytics sharedInstance] startWithChannelGroup:self.channelGroupMock
appSecret:kMSACTestAppSecret
transmissionTargetToken:nil
fromApplication:YES];
// When
[MSACAnalytics trackEvent:expectedName withProperties:nil flags:MSACFlagsNormal];
// Then
XCTAssertEqual(actualType, kMSACTypeEvent);
XCTAssertEqual(actualName, expectedName);
XCTAssertEqual(actualFlags, MSACFlagsNormal);
}
- (void)testTrackEventWithPropertiesWithCriticalPersistenceFlag {
// If
__block NSString *actualType;
__block NSString *actualName;
__block MSACFlags actualFlags;
NSString *expectedName = @"gotACoffee";
OCMReject([[self.channelUnitMock ignoringNonObjectArgs] enqueueItem:[OCMArg isKindOfClass:[MSACEventLog class]] flags:(MSACFlags)0]);
OCMStub([[self.channelUnitCriticalMock ignoringNonObjectArgs] enqueueItem:[OCMArg isKindOfClass:[MSACEventLog class]] flags:(MSACFlags)0])
.andDo(^(NSInvocation *invocation) {
MSACEventLog *log;
[invocation getArgument:&log atIndex:2];
actualType = log.type;
actualName = log.name;
MSACFlags flags;
[invocation getArgument:&flags atIndex:3];
actualFlags = flags;
});
[MSACAppCenter configureWithAppSecret:kMSACTestAppSecret];
[[MSACAnalytics sharedInstance] startWithChannelGroup:self.channelGroupMock
appSecret:kMSACTestAppSecret
transmissionTargetToken:nil
fromApplication:YES];
// When
[MSACAnalytics trackEvent:expectedName withProperties:nil flags:MSACFlagsCritical];
// Then
XCTAssertEqual(actualType, kMSACTypeEvent);
XCTAssertEqual(actualName, expectedName);
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
XCTAssertEqual(actualFlags, MSACFlagsPersistenceCritical);
OCMVerifyAll(self.channelUnitMock);
#pragma clang diagnostic pop
}
- (void)testTrackEventWithPropertiesWithInvalidFlag {
// If
__block NSString *actualType;
__block NSString *actualName;
__block MSACFlags actualFlags;
NSString *expectedName = @"gotACoffee";
OCMStub([[self.channelUnitMock ignoringNonObjectArgs] enqueueItem:[OCMArg isKindOfClass:[MSACEventLog class]] flags:(MSACFlags)0])
.andDo(^(NSInvocation *invocation) {
MSACEventLog *log;
[invocation getArgument:&log atIndex:2];
actualType = log.type;
actualName = log.name;
MSACFlags flags;
[invocation getArgument:&flags atIndex:3];
actualFlags = flags;
});
[MSACAppCenter configureWithAppSecret:kMSACTestAppSecret];
[[MSACAnalytics sharedInstance] startWithChannelGroup:self.channelGroupMock
appSecret:kMSACTestAppSecret
transmissionTargetToken:nil
fromApplication:YES];
// When
[MSACAnalytics trackEvent:expectedName withProperties:nil flags:42];
// Then
XCTAssertEqual(actualType, kMSACTypeEvent);
XCTAssertEqual(actualName, expectedName);
XCTAssertEqual(actualFlags, MSACFlagsNormal);
}
- (void)testPersistanceFlagsSeparateChannels {
// If
NSString *expectedCriticalEvent = @"Having a cup of coffee";
NSString *expectedEvent = @"Washing a cup after having a coffee";
[MSACAppCenter configureWithAppSecret:kMSACTestAppSecret];
[[MSACAnalytics sharedInstance] startWithChannelGroup:self.channelGroupMock
appSecret:kMSACTestAppSecret
transmissionTargetToken:nil
fromApplication:YES];
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
OCMExpect([self.channelUnitCriticalMock enqueueItem:OCMOCK_ANY flags:MSACFlagsPersistenceCritical]);
OCMExpect([self.channelUnitMock enqueueItem:OCMOCK_ANY flags:MSACFlagsPersistenceNormal]);
// When
[[MSACAnalytics sharedInstance] trackEvent:expectedCriticalEvent
withTypedProperties:nil
forTransmissionTarget:nil
flags:MSACFlagsPersistenceCritical];
[[MSACAnalytics sharedInstance] trackEvent:expectedEvent
withTypedProperties:nil
forTransmissionTarget:nil
flags:MSACFlagsPersistenceNormal];
#pragma clang diagnostic pop
// Then
OCMVerifyAll(self.channelUnitCriticalMock);
OCMVerifyAll(self.channelUnitMock);
}
- (void)testTrackEventWithTypedPropertiesWithNormalPersistenceFlag {
// If
__block NSString *actualType;
__block NSString *actualName;
__block MSACFlags actualFlags;
NSString *expectedName = @"gotACoffee";
OCMStub([[self.channelUnitMock ignoringNonObjectArgs] enqueueItem:[OCMArg isKindOfClass:[MSACEventLog class]] flags:(MSACFlags)0])
.andDo(^(NSInvocation *invocation) {
MSACEventLog *log;
[invocation getArgument:&log atIndex:2];
actualType = log.type;
actualName = log.name;
MSACFlags flags;
[invocation getArgument:&flags atIndex:3];
actualFlags = flags;
});
[MSACAppCenter configureWithAppSecret:kMSACTestAppSecret];
[[MSACAnalytics sharedInstance] startWithChannelGroup:self.channelGroupMock
appSecret:kMSACTestAppSecret
transmissionTargetToken:nil
fromApplication:YES];
// When
[MSACAnalytics trackEvent:expectedName withTypedProperties:nil flags:MSACFlagsNormal];
// Then
XCTAssertEqual(actualType, kMSACTypeEvent);
XCTAssertEqual(actualName, expectedName);
XCTAssertEqual(actualFlags, MSACFlagsNormal);
}
- (void)testTrackEventWithTypedPropertiesWithCriticalPersistenceFlag {
// If
__block NSString *actualType;
__block NSString *actualName;
__block MSACFlags actualFlags;
NSString *expectedName = @"gotACoffee";
OCMStub([[self.channelUnitCriticalMock ignoringNonObjectArgs] enqueueItem:[OCMArg isKindOfClass:[MSACEventLog class]] flags:(MSACFlags)0])
.andDo(^(NSInvocation *invocation) {
MSACEventLog *log;
[invocation getArgument:&log atIndex:2];
actualType = log.type;
actualName = log.name;
MSACFlags flags;
[invocation getArgument:&flags atIndex:3];
actualFlags = flags;
});
OCMReject([[self.channelUnitMock ignoringNonObjectArgs] enqueueItem:[OCMArg isKindOfClass:[MSACEventLog class]] flags:(MSACFlags)0]);
[MSACAppCenter configureWithAppSecret:kMSACTestAppSecret];
[[MSACAnalytics sharedInstance] startWithChannelGroup:self.channelGroupMock
appSecret:kMSACTestAppSecret
transmissionTargetToken:nil
fromApplication:YES];
// When
[MSACAnalytics trackEvent:expectedName withTypedProperties:nil flags:MSACFlagsCritical];
// Then
XCTAssertEqual(actualType, kMSACTypeEvent);
XCTAssertEqual(actualName, expectedName);
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
XCTAssertEqual(actualFlags, MSACFlagsPersistenceCritical);
#pragma clang diagnostic pop
OCMVerifyAll(self.channelUnitMock);
}
- (void)testTrackEventWithTypedPropertiesWithInvalidFlag {
// If
__block NSString *actualType;
__block NSString *actualName;
__block MSACFlags actualFlags;
NSString *expectedName = @"gotACoffee";
OCMStub([[self.channelUnitMock ignoringNonObjectArgs] enqueueItem:[OCMArg isKindOfClass:[MSACEventLog class]] flags:(MSACFlags)0])
.andDo(^(NSInvocation *invocation) {
MSACEventLog *log;
[invocation getArgument:&log atIndex:2];
actualType = log.type;
actualName = log.name;
MSACFlags flags;
[invocation getArgument:&flags atIndex:3];
actualFlags = flags;
});
[MSACAppCenter configureWithAppSecret:kMSACTestAppSecret];
[[MSACAnalytics sharedInstance] startWithChannelGroup:self.channelGroupMock
appSecret:kMSACTestAppSecret
transmissionTargetToken:nil
fromApplication:YES];
// When
[MSACAnalytics trackEvent:expectedName withTypedProperties:nil flags:42];
// Then
XCTAssertEqual(actualType, kMSACTypeEvent);
XCTAssertEqual(actualName, expectedName);
XCTAssertEqual(actualFlags, MSACFlagsNormal);
}
- (void)testTrackPageWithoutProperties {
// If
__block NSString *name;
__block NSString *type;
NSString *expectedName = @"HomeSweetHome";
OCMStub([self.channelUnitMock enqueueItem:[OCMArg isKindOfClass:[MSACLogWithProperties class]] flags:MSACFlagsDefault])
.andDo(^(NSInvocation *invocation) {
MSACEventLog *log;
[invocation getArgument:&log atIndex:2];
type = log.type;
name = log.name;
});
[MSACAppCenter configureWithAppSecret:kMSACTestAppSecret];
[[MSACAnalytics sharedInstance] startWithChannelGroup:self.channelGroupMock
appSecret:kMSACTestAppSecret
transmissionTargetToken:nil
fromApplication:YES];
// When
[MSACAnalytics trackPage:expectedName];
// Then
assertThat(type, is(kMSACTypePage));
assertThat(name, is(expectedName));
}
- (void)testTrackPageWithProperties {
// If
__block NSString *type;
__block NSString *name;
__block NSDictionary<NSString *, NSString *> *properties;
NSString *expectedName = @"HomeSweetHome";
NSDictionary *expectedProperties = @{@"Sofa" : @"yes", @"TV" : @"of course"};
OCMStub([self.channelUnitMock enqueueItem:[OCMArg isKindOfClass:[MSACLogWithProperties class]] flags:MSACFlagsDefault])
.andDo(^(NSInvocation *invocation) {
MSACEventLog *log;
[invocation getArgument:&log atIndex:2];
type = log.type;
name = log.name;
properties = log.properties;
});
[MSACAppCenter configureWithAppSecret:kMSACTestAppSecret];
[[MSACAnalytics sharedInstance] startWithChannelGroup:self.channelGroupMock
appSecret:kMSACTestAppSecret
transmissionTargetToken:nil
fromApplication:YES];
// When
[MSACAnalytics trackPage:expectedName withProperties:expectedProperties];
// Then
assertThat(type, is(kMSACTypePage));
assertThat(name, is(expectedName));
assertThat(properties, is(expectedProperties));
}
- (void)testTrackPageWhenAnalyticsDisabled {
// If
id analyticsMock = OCMPartialMock([MSACAnalytics sharedInstance]);
OCMStub([analyticsMock isEnabled]).andReturn(NO);
[MSACAppCenter configureWithAppSecret:kMSACTestAppSecret];
[[MSACAnalytics sharedInstance] startWithChannelGroup:self.channelGroupMock
appSecret:kMSACTestAppSecret
transmissionTargetToken:nil
fromApplication:YES];
// When
OCMReject([self.channelUnitMock enqueueItem:OCMOCK_ANY flags:MSACFlagsDefault]);
[[MSACAnalytics sharedInstance] trackPage:@"Some page" withProperties:nil];
// Then
OCMVerifyAll(self.channelUnitMock);
}
- (void)testTrackPageWithInvalidName {
// If
NSString *invalidPageName = nil;
id analyticsMock = OCMPartialMock([MSACAnalytics sharedInstance]);
[MSACAppCenter configureWithAppSecret:kMSACTestAppSecret];
[[MSACAnalytics sharedInstance] startWithChannelGroup:self.channelGroupMock
appSecret:kMSACTestAppSecret
transmissionTargetToken:nil
fromApplication:YES];
// When
OCMExpect([self.channelUnitMock enqueueItem:OCMOCK_ANY flags:MSACFlagsDefault]);
// Will be validated in shouldFilterLog callback instead.
OCMReject([analyticsMock validateEventName:OCMOCK_ANY forLogType:OCMOCK_ANY]);
OCMReject([analyticsMock validateProperties:OCMOCK_ANY forLogName:OCMOCK_ANY andType:OCMOCK_ANY]);
[[MSACAnalytics sharedInstance] trackPage:invalidPageName withProperties:nil];
// Then
OCMVerifyAll(self.channelUnitMock);
OCMVerifyAll(analyticsMock);
}
- (void)testAutoPageTracking {
// For now auto page tracking is disabled by default
XCTAssertFalse([MSACAnalytics isAutoPageTrackingEnabled]);
// When
[MSACAnalytics setAutoPageTrackingEnabled:YES];
// Then
XCTAssertTrue([MSACAnalytics isAutoPageTrackingEnabled]);
// When
[MSACAnalytics setAutoPageTrackingEnabled:NO];
// Then
XCTAssertFalse([MSACAnalytics isAutoPageTrackingEnabled]);
}
- (void)testInitializationPriorityCorrect {
XCTAssertTrue([[MSACAnalytics sharedInstance] initializationPriority] == MSACInitializationPriorityDefault);
}
- (void)testServiceNameIsCorrect {
XCTAssertEqual([MSACAnalytics serviceName], kMSACAnalyticsServiceName);
}
#if TARGET_OS_IOS
// TODO: Modify for testing each platform when page tracking will be supported on each platform.
- (void)testViewWillAppearSwizzlingWithAnalyticsAvailable {
// If
id analyticsMock = OCMPartialMock([MSACAnalytics sharedInstance]);
OCMStub([analyticsMock isAutoPageTrackingEnabled]).andReturn(YES);
OCMStub([analyticsMock isAvailable]).andReturn(YES);
[MSACAppCenter configureWithAppSecret:kMSACTestAppSecret];
[[MSACAnalytics sharedInstance] startWithChannelGroup:OCMProtocolMock(@protocol(MSACChannelGroupProtocol))
appSecret:kMSACTestAppSecret
transmissionTargetToken:nil
fromApplication:YES];
// When
#if TARGET_OS_OSX
NSViewController *viewController = [[NSViewController alloc] init];
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wunguarded-availability"
if ([viewController respondsToSelector:@selector(viewWillAppear)]) {
[viewController viewWillAppear];
}
#pragma clang diagnostic pop
#else
UIViewController *viewController = [[UIViewController alloc] init];
[viewController viewWillAppear:NO];
#endif
// Then
OCMVerify([analyticsMock isAutoPageTrackingEnabled]);
XCTAssertNil([MSACAnalyticsCategory missedPageViewName]);
}
- (void)testViewWillAppearSwizzlingWithAnalyticsNotAvailable {
// If
id analyticsMock = OCMPartialMock([MSACAnalytics sharedInstance]);
OCMStub([analyticsMock isAutoPageTrackingEnabled]).andReturn(YES);
OCMStub([analyticsMock isAvailable]).andReturn(NO);
[MSACAppCenter configureWithAppSecret:kMSACTestAppSecret];
[[MSACAnalytics sharedInstance] startWithChannelGroup:OCMProtocolMock(@protocol(MSACChannelGroupProtocol))
appSecret:kMSACTestAppSecret
transmissionTargetToken:nil
fromApplication:YES];
// When
#if TARGET_OS_OSX
NSViewController *viewController = [[NSViewController alloc] init];
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wunguarded-availability"
if ([viewController respondsToSelector:@selector(viewWillAppear)]) {
[viewController viewWillAppear];
}
#pragma clang diagnostic pop
#else
UIViewController *viewController = [[UIViewController alloc] init];
[viewController viewWillAppear:NO];
#endif
// Then
OCMVerify([analyticsMock isAutoPageTrackingEnabled]);
XCTAssertNotNil([MSACAnalyticsCategory missedPageViewName]);
}
- (void)testViewWillAppearSwizzlingWithShouldTrackPageDisabled {
// If
id analyticsMock = OCMPartialMock([MSACAnalytics sharedInstance]);
[MSACAppCenter configureWithAppSecret:kMSACTestAppSecret];
[[MSACAnalytics sharedInstance] startWithChannelGroup:OCMProtocolMock(@protocol(MSACChannelGroupProtocol))
appSecret:kMSACTestAppSecret
transmissionTargetToken:nil
fromApplication:YES];
// When
OCMExpect([analyticsMock isAutoPageTrackingEnabled]).andReturn(YES);
OCMReject([analyticsMock isAvailable]);
#if TARGET_OS_OSX
NSPageController *containerController = [[NSPageController alloc] init];
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wunguarded-availability"
if ([containerController respondsToSelector:@selector(viewWillAppear)]) {
[containerController viewWillAppear];
}
#pragma clang diagnostic pop
#else
UIPageViewController *containerController = [[UIPageViewController alloc] init];
[containerController viewWillAppear:NO];
#endif
// Then
OCMVerifyAll(analyticsMock);
}
#endif
- (void)testStartWithTransmissionTargetAndAppSecretUsesTransmissionTarget {
// If
[MSACAppCenter configureWithAppSecret:kMSACTestAppSecret];
__block MSACEventLog *log;
__block int invocations = 0;
OCMStub([self.channelUnitMock enqueueItem:[OCMArg isKindOfClass:[MSACLogWithProperties class]] flags:MSACFlagsDefault])
.andDo(^(NSInvocation *invocation) {
++invocations;
[invocation getArgument:&log atIndex:2];
});
[[MSACAnalytics sharedInstance] startWithChannelGroup:self.channelGroupMock
appSecret:kMSACTestAppSecret
transmissionTargetToken:kMSACTestTransmissionToken
fromApplication:YES];
// When
[MSACAnalytics trackEvent:@"eventName"];
// Then
OCMVerify([self.channelUnitMock enqueueItem:log flags:MSACFlagsDefault]);
XCTAssertTrue([[log transmissionTargetTokens] containsObject:kMSACTestTransmissionToken]);
XCTAssertEqual([[log transmissionTargetTokens] count], (unsigned long)1);
XCTAssertEqual(invocations, 1);
}
- (void)testStartWithTransmissionTargetWithoutAppSecretUsesTransmissionTarget {
// If
[MSACAppCenter configureWithAppSecret:kMSACTestAppSecret];
__block MSACEventLog *log;
__block int invocations = 0;
OCMStub([self.channelUnitMock enqueueItem:[OCMArg isKindOfClass:[MSACLogWithProperties class]] flags:MSACFlagsDefault])
.andDo(^(NSInvocation *invocation) {
++invocations;
[invocation getArgument:&log atIndex:2];
});
[[MSACAnalytics sharedInstance] startWithChannelGroup:self.channelGroupMock
appSecret:nil
transmissionTargetToken:kMSACTestTransmissionToken
fromApplication:YES];
// When
[MSACAnalytics trackEvent:@"eventName"];
// Then
OCMVerify([self.channelUnitMock enqueueItem:log flags:MSACFlagsDefault]);
XCTAssertTrue([[log transmissionTargetTokens] containsObject:kMSACTestTransmissionToken]);
XCTAssertEqual([[log transmissionTargetTokens] count], (unsigned long)1);
XCTAssertEqual(invocations, 1);
}
- (void)testGetTransmissionTargetCreatesTransmissionTargetOnce {
// When
MSACAnalyticsTransmissionTarget *transmissionTarget1 = [MSACAnalytics transmissionTargetForToken:kMSACTestTransmissionToken];
MSACAnalyticsTransmissionTarget *transmissionTarget2 = [MSACAnalytics transmissionTargetForToken:kMSACTestTransmissionToken];
// Then
XCTAssertNotNil(transmissionTarget1);
XCTAssertEqual(transmissionTarget1, transmissionTarget2);
}
- (void)testGetTransmissionTargetNeverReturnsDefault {
// If
[[MSACAnalytics sharedInstance] startWithChannelGroup:OCMProtocolMock(@protocol(MSACChannelGroupProtocol))
appSecret:nil
transmissionTargetToken:kMSACTestTransmissionToken
fromApplication:NO];
// When
MSACAnalyticsTransmissionTarget *transmissionTarget = [MSACAnalytics transmissionTargetForToken:kMSACTestTransmissionToken];
// Then
XCTAssertNotNil([MSACAnalytics sharedInstance].defaultTransmissionTarget);
XCTAssertNotNil(transmissionTarget);
XCTAssertNotEqual([MSACAnalytics sharedInstance].defaultTransmissionTarget, transmissionTarget);
}
- (void)testDefaultTransmissionTargetMirrorAnalyticsEnableState {
// If
MSACAnalytics *service = [MSACAnalytics sharedInstance];
[MSACAppCenter configureWithAppSecret:kMSACTestAppSecret];
// When
[[MSACAnalytics sharedInstance] startWithChannelGroup:self.channelGroupMock
appSecret:kMSACTestAppSecret
transmissionTargetToken:kMSACTestTransmissionToken
fromApplication:YES];
// Then
XCTAssertNotNil([MSACAnalytics sharedInstance].defaultTransmissionTarget);
XCTAssertTrue([service isEnabled]);
XCTAssertTrue([service.defaultTransmissionTarget isEnabled]);
// When
[service setEnabled:NO];
// Then
XCTAssertFalse([service isEnabled]);
XCTAssertFalse([service.defaultTransmissionTarget isEnabled]);
// When
[service setEnabled:YES];
// Then
XCTAssertTrue([service isEnabled]);
XCTAssertTrue([service.defaultTransmissionTarget isEnabled]);
}
- (void)testEnableStatePropagateToTransmissionTargets {
// If
[MSACAppCenter configureWithAppSecret:kMSACTestAppSecret];
[[MSACAnalytics sharedInstance] startWithChannelGroup:OCMProtocolMock(@protocol(MSACChannelGroupProtocol))
appSecret:kMSACTestAppSecret
transmissionTargetToken:nil
fromApplication:NO];
MSACServiceAbstract *analytics = [MSACAnalytics sharedInstance];
[analytics setEnabled:NO];
// When
// Analytics is disabled, targets must match Analytics enabled state.
MSACAnalyticsTransmissionTarget *transmissionTarget = [MSACAnalytics transmissionTargetForToken:kMSACTestTransmissionToken];
MSACAnalyticsTransmissionTarget *transmissionTarget2 = [MSACAnalytics transmissionTargetForToken:kMSACTestTransmissionToken2];
// Then
XCTAssertFalse([transmissionTarget isEnabled]);
XCTAssertFalse([transmissionTarget2 isEnabled]);
// When
// Trying re-enabling will fail since Analytics is still disabled.
[transmissionTarget setEnabled:YES];
// Then
XCTAssertFalse([transmissionTarget isEnabled]);
XCTAssertFalse([transmissionTarget2 isEnabled]);
// When
// Enabling Analytics will enable all targets.
[analytics setEnabled:YES];
// Then
XCTAssertTrue([transmissionTarget isEnabled]);
XCTAssertTrue([transmissionTarget2 isEnabled]);
// Disabling Analytics will disable all targets.
[analytics setEnabled:NO];
// Then
XCTAssertFalse([transmissionTarget isEnabled]);
XCTAssertFalse([transmissionTarget2 isEnabled]);
}
- (void)testAppSecretNotRequired {
XCTAssertFalse([[MSACAnalytics sharedInstance] isAppSecretRequired]);
}
- (void)testSessionTrackerStarted {
// When
[MSACAppCenter startFromLibraryWithServices:@ [[MSACAnalytics class]]];
// Then
XCTAssertFalse([MSACAnalytics sharedInstance].sessionTracker.started);
// When
[MSACAppCenter start:MSAC_UUID_STRING withServices:@ [[MSACAnalytics class]]];
// Then
XCTAssertTrue([MSACAnalytics sharedInstance].sessionTracker.started);
}
- (void)testSessionTrackerStartedWithToken {
// When
[MSACAppCenter startFromLibraryWithServices:@ [[MSACAnalytics class]]];
// Then
XCTAssertNil([MSACAnalytics sharedInstance].defaultTransmissionTarget);
// When
[[MSACAnalytics sharedInstance] updateConfigurationWithAppSecret:kMSACTestAppSecret transmissionTargetToken:kMSACTestTransmissionToken];
// Then
XCTAssertNotNil([MSACAnalytics sharedInstance].defaultTransmissionTarget);
}
- (void)testAutoPageTrackingWhenStartedFromLibrary {
// If
id analyticsMock = OCMPartialMock([MSACAnalytics sharedInstance]);
id analyticsCategoryMock = OCMClassMock([MSACAnalyticsCategory class]);
NSString *testPageName = @"TestPage";
OCMStub([analyticsCategoryMock missedPageViewName]).andReturn(testPageName);
[MSACAnalytics setAutoPageTrackingEnabled:YES];
MSACServiceAbstract *service = [MSACAnalytics sharedInstance];
// When
[[MSACAnalytics sharedInstance] startWithChannelGroup:OCMProtocolMock(@protocol(MSACChannelGroupProtocol))
appSecret:kMSACTestAppSecret
transmissionTargetToken:nil
fromApplication:NO];
// Then
XCTestExpectation *expectation = [self expectationWithDescription:@"Wait for block in applyEnabledState to be dispatched"];
dispatch_async(dispatch_get_main_queue(), ^{
[expectation fulfill];
});
[self waitForExpectationsWithTimeout:1
handler:^(NSError *error) {
if (error) {
XCTFail(@"Expectation Failed with error: %@", error);
}
// Then
XCTAssertTrue([service isEnabled]);
OCMReject([analyticsMock trackPage:testPageName withProperties:nil]);
}];
}
#pragma mark - Property validation tests
- (void)testRemoveInvalidPropertiesWithEmptyValue {
// If
NSDictionary *emptyValueProperties = @{@"aValidKey" : @""};
// When
NSDictionary *result = [[MSACAnalytics sharedInstance] removeInvalidProperties:emptyValueProperties];
// Then
XCTAssertTrue(result.count == 1);
XCTAssertEqualObjects(result, emptyValueProperties);
}
- (void)testRemoveInvalidPropertiesWithEmptyKey {
// If
NSDictionary *emptyKeyProperties = @{@"" : @"aValidValue"};
// When
NSDictionary *result = [[MSACAnalytics sharedInstance] removeInvalidProperties:emptyKeyProperties];
// Then
XCTAssertTrue(result.count == 1);
}
- (void)testremoveInvalidPropertiesWithNonStringKey {
// If
NSDictionary *numberAsKeyProperties = @{@(42) : @"aValidValue"};
// When
NSDictionary *result = [[MSACAnalytics sharedInstance] removeInvalidProperties:numberAsKeyProperties];
// Then
XCTAssertTrue(result.count == 0);
}
- (void)testValidateLogDataWithNonStringValue {
// If
NSDictionary *numberAsValueProperties = @{@"aValidKey" : @(42)};
// When
NSDictionary *result = [[MSACAnalytics sharedInstance] removeInvalidProperties:numberAsValueProperties];
// Then
XCTAssertTrue(result.count == 0);
}
- (void)testValidateLogDataWithCorrectNestedProperties {
// If
NSDictionary *correctlyNestedProperties = @{@"aValidKey1" : @"aValidValue1", @"aValidKey2.aValidKey2" : @"aValidValue3"};
// When
NSDictionary *result = [[MSACAnalytics sharedInstance] removeInvalidProperties:correctlyNestedProperties];
// Then
XCTAssertTrue(result.count == 2);
XCTAssertEqualObjects(result, correctlyNestedProperties);
}
- (void)testValidateLogDataWithIncorrectNestedProperties {
// If
NSDictionary *incorrectNestedProperties = @{
@"aValidKey1" : @"aValidValue1",
@"aValidKey2" : @1,
};
// When
NSDictionary *result = [[MSACAnalytics sharedInstance] removeInvalidProperties:incorrectNestedProperties];
// Then
XCTAssertTrue(result.count == 1);
XCTAssertNil(result[@"aValidKey2"]);
XCTAssertNotNil(result[@"aValidKey1"]);
XCTAssertEqualObjects(result[@"aValidKey1"], @"aValidValue1");
XCTAssertNotEqualObjects(result, incorrectNestedProperties);
}
- (void)testDictionaryContainsInvalidPropertiesKey {
// If
NSDictionary *incorrectNestedProperties = @{@1 : @"aValidValue1", @"aValidKey2" : @"aValidValue2"};
// When
NSDictionary *result = [[MSACAnalytics sharedInstance] removeInvalidProperties:incorrectNestedProperties];
// Then
XCTAssertTrue(result.count == 1);
XCTAssertNotNil(result[@"aValidKey2"]);
}
- (void)testDictionaryContainsValidNestedProperties {
NSDictionary *properties = @{@"aValidKey2" : @"aValidValue1", @"aValidKey1.avalidKey2" : @"aValidValue1"};
// When
NSDictionary *result = [[MSACAnalytics sharedInstance] removeInvalidProperties:properties];
// Then
XCTAssertEqualObjects(result, properties);
}
- (void)testPropertyNameIsTruncatedInACopyWhenValidatingForAppCenter {
// If
MSACEventProperties *properties = [MSACEventProperties new];
NSString *longKey = [@"" stringByPaddingToLength:kMSACMaxPropertyKeyLength + 2 withString:@"hi" startingAtIndex:0];
NSString *truncatedKey = [longKey substringToIndex:kMSACMaxPropertyKeyLength - 1];
[properties setString:@"test" forKey:longKey];
MSACStringTypedProperty *originalProperty = (MSACStringTypedProperty *)properties.properties[longKey];
// When
MSACEventProperties *validProperties = [[MSACAnalytics sharedInstance] validateAppCenterEventProperties:properties];
// Then
MSACStringTypedProperty *validProperty = (MSACStringTypedProperty *)validProperties.properties[truncatedKey];
XCTAssertNotNil(validProperty);
XCTAssertEqualObjects(validProperty.name, truncatedKey);
XCTAssertNotEqual(originalProperty, validProperty);
XCTAssertEqualObjects(originalProperty.name, longKey);
}
- (void)testPropertyValueIsTruncatedInACopyWhenValidatingForAppCenter {
// If
MSACEventProperties *properties = [MSACEventProperties new];
NSString *key = @"key";
NSString *longValue = [@"" stringByPaddingToLength:kMSACMaxPropertyValueLength + 2 withString:@"hi" startingAtIndex:0];
NSString *truncatedValue = [longValue substringToIndex:kMSACMaxPropertyValueLength - 1];
[properties setString:longValue forKey:key];
MSACStringTypedProperty *originalProperty = (MSACStringTypedProperty *)properties.properties[key];
// When
MSACEventProperties *validProperties = [[MSACAnalytics sharedInstance] validateAppCenterEventProperties:properties];
// Then
MSACStringTypedProperty *validProperty = (MSACStringTypedProperty *)validProperties.properties[key];
XCTAssertEqualObjects(validProperty.value, truncatedValue);
XCTAssertNotEqual(originalProperty, validProperty);
XCTAssertEqualObjects(originalProperty.value, longValue);
}
- (void)testAppCenterCopyHas20PropertiesWhenSelfHasMoreThan20 {
// If
MSACEventProperties *properties = [MSACEventProperties new];
// When
for (int i = 0; i < kMSACMaxPropertiesPerLog + 5; i++) {
[properties setBool:YES forKey:[@(i) stringValue]];
}
MSACEventProperties *validProperties = [[MSACAnalytics sharedInstance] validateAppCenterEventProperties:properties];
// Then
XCTAssertEqual([validProperties.properties count], kMSACMaxPropertiesPerLog);
}
- (void)testPause {
// If
id appCenterMock = OCMClassMock([MSACAppCenter class]);
OCMStub([appCenterMock sharedInstance]).andReturn(appCenterMock);
OCMStub([appCenterMock isSdkConfigured]).andReturn(YES);
[[MSACAnalytics sharedInstance] startWithChannelGroup:self.channelGroupMock
appSecret:kMSACTestAppSecret
transmissionTargetToken:nil
fromApplication:YES];
// When
[MSACAnalytics pause];
// Then
OCMVerify([self.channelUnitMock pauseWithIdentifyingObject:[MSACAnalytics sharedInstance]]);
[appCenterMock stopMocking];
}
- (void)testResume {
// If
id appCenterMock = OCMClassMock([MSACAppCenter class]);
OCMStub([appCenterMock sharedInstance]).andReturn(appCenterMock);
OCMStub([appCenterMock isSdkConfigured]).andReturn(YES);
[[MSACAnalytics sharedInstance] startWithChannelGroup:self.channelGroupMock
appSecret:kMSACTestAppSecret
transmissionTargetToken:nil
fromApplication:YES];
// When
[MSACAnalytics resume];
// Then
OCMVerify([self.channelUnitMock resumeWithIdentifyingObject:[MSACAnalytics sharedInstance]]);
[appCenterMock stopMocking];
}
- (void)testEnablingAnalyticsResumesIt {
// If
id appCenterMock = OCMClassMock([MSACAppCenter class]);
OCMStub([appCenterMock sharedInstance]).andReturn(appCenterMock);
OCMStub([appCenterMock isSdkConfigured]).andReturn(YES);
OCMStub(ClassMethod([appCenterMock isEnabled])).andReturn(YES);
[[MSACAnalytics sharedInstance] startWithChannelGroup:self.channelGroupMock
appSecret:kMSACTestAppSecret
transmissionTargetToken:nil
fromApplication:YES];
[MSACAnalytics setEnabled:NO];
// Reset ChannelUnitMock since it's already called at startup and we want to
// verify at enabling time.
[MSACAnalytics sharedInstance].channelUnit = OCMProtocolMock(@protocol(MSACChannelUnitProtocol));
// When
[MSACAnalytics setEnabled:YES];
// Then
OCMVerify([[MSACAnalytics sharedInstance].channelUnit resumeWithIdentifyingObject:[MSACAnalytics sharedInstance]]);
[appCenterMock stopMocking];
}
- (void)testPauseTransmissionTargetInOneCollectorChannelUnitWhenPausedWithTargetKey {
// If
id appCenterMock = OCMClassMock([MSACAppCenter class]);
OCMStub([appCenterMock sharedInstance]).andReturn(appCenterMock);
OCMStub([appCenterMock isSdkConfigured]).andReturn(YES);
OCMStub(ClassMethod([appCenterMock isEnabled])).andReturn(YES);
id<MSACChannelGroupProtocol> channelGroupMock = OCMProtocolMock(@protocol(MSACChannelGroupProtocol));
id<MSACChannelUnitProtocol> oneCollectorChannelUnitMock = OCMProtocolMock(@protocol(MSACChannelUnitProtocol));
OCMStub([channelGroupMock channelUnitForGroupId:@"Analytics/one"]).andReturn(oneCollectorChannelUnitMock);
[[MSACAnalytics sharedInstance] startWithChannelGroup:channelGroupMock appSecret:nil transmissionTargetToken:nil fromApplication:YES];
// When
[MSACAnalytics pauseTransmissionTargetForToken:kMSACTestTransmissionToken];
// Then
OCMVerify([oneCollectorChannelUnitMock pauseSendingLogsWithToken:kMSACTestTransmissionToken]);
[appCenterMock stopMocking];
}
- (void)testResumeTransmissionTargetInOneCollectorChannelUnitWhenResumedWithTargetKey {
// If
id appCenterMock = OCMClassMock([MSACAppCenter class]);
OCMStub([appCenterMock sharedInstance]).andReturn(appCenterMock);
OCMStub([appCenterMock isSdkConfigured]).andReturn(YES);
OCMStub(ClassMethod([appCenterMock isEnabled])).andReturn(YES);
id<MSACChannelGroupProtocol> channelGroupMock = OCMProtocolMock(@protocol(MSACChannelGroupProtocol));
id<MSACChannelUnitProtocol> oneCollectorChannelUnitMock = OCMProtocolMock(@protocol(MSACChannelUnitProtocol));
OCMStub([channelGroupMock channelUnitForGroupId:@"Analytics/one"]).andReturn(oneCollectorChannelUnitMock);
[[MSACAnalytics sharedInstance] startWithChannelGroup:channelGroupMock appSecret:nil transmissionTargetToken:nil fromApplication:YES];
// When
[MSACAnalytics resumeTransmissionTargetForToken:kMSACTestTransmissionToken];
// Then
OCMVerify([oneCollectorChannelUnitMock resumeSendingLogsWithToken:kMSACTestTransmissionToken]);
[appCenterMock stopMocking];
}
#if TARGET_OS_IOS
// TODO: Modify for testing each platform when page tracking will be supported on each platform.
- (void)testViewWillAppearSwizzling {
// If
id analyticsMock = OCMPartialMock([MSACAnalytics sharedInstance]);
UIViewController *viewController = [[UIViewController alloc] init];
// When
[MSACAnalyticsCategory activateCategory];
[viewController viewWillAppear:NO];
// Then
OCMVerify([analyticsMock isAutoPageTrackingEnabled]);
// Clear
[analyticsMock stopMocking];
}
#endif
@end