2019-08-05 19:30:54 -07:00
|
|
|
// Copyright 2019 Google
|
|
|
|
|
//
|
|
|
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
|
// you may not use this file except in compliance with the License.
|
|
|
|
|
// You may obtain a copy of the License at
|
|
|
|
|
//
|
|
|
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
|
//
|
|
|
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
|
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
|
// See the License for the specific language governing permissions and
|
|
|
|
|
// limitations under the License.
|
|
|
|
|
|
2021-05-11 11:39:55 -07:00
|
|
|
#import <OCMock/OCMock.h>
|
2019-08-05 19:30:54 -07:00
|
|
|
#import <XCTest/XCTest.h>
|
|
|
|
|
|
|
|
|
|
#import "FirebaseABTesting/Sources/ABTConditionalUserPropertyController.h"
|
|
|
|
|
#import "FirebaseABTesting/Sources/ABTConstants.h"
|
2020-07-06 15:18:02 -07:00
|
|
|
#import "FirebaseABTesting/Sources/Private/ABTExperimentPayload.h"
|
2020-07-31 07:21:21 -07:00
|
|
|
#import "FirebaseABTesting/Sources/Public/FirebaseABTesting/FIRExperimentController.h"
|
|
|
|
|
#import "FirebaseABTesting/Sources/Public/FirebaseABTesting/FIRLifecycleEvents.h"
|
2019-08-05 19:30:54 -07:00
|
|
|
#import "FirebaseABTesting/Tests/Unit/ABTFakeFIRAConditionalUserPropertyController.h"
|
|
|
|
|
#import "FirebaseABTesting/Tests/Unit/ABTTestUniversalConstants.h"
|
2020-07-06 13:06:39 -04:00
|
|
|
#import "FirebaseABTesting/Tests/Unit/Utilities/ABTTestUtilities.h"
|
2022-03-22 13:36:33 -04:00
|
|
|
#import "FirebaseCore/Extension/FirebaseCoreInternal.h"
|
2019-08-05 19:30:54 -07:00
|
|
|
|
|
|
|
|
@interface ABTConditionalUserPropertyController (ExposedForTest)
|
|
|
|
|
- (NSInteger)maxNumberOfExperimentsOfOrigin:(NSString *)origin;
|
|
|
|
|
- (void)maxNumberOfExperimentsOfOrigin:(NSString *)origin
|
|
|
|
|
completionHandler:(void (^)(int32_t))completionHandler;
|
|
|
|
|
- (id)createExperimentFromOrigin:(NSString *)origin
|
|
|
|
|
payload:(ABTExperimentPayload *)payload
|
|
|
|
|
events:(FIRLifecycleEvents *)events;
|
2020-07-06 13:06:39 -04:00
|
|
|
- (ABTExperimentPayloadExperimentOverflowPolicy)
|
2019-08-05 19:30:54 -07:00
|
|
|
overflowPolicyWithPayload:(ABTExperimentPayload *)payload
|
2020-07-06 13:06:39 -04:00
|
|
|
originalPolicy:(ABTExperimentPayloadExperimentOverflowPolicy)originalPolicy;
|
2019-08-05 19:30:54 -07:00
|
|
|
/// Surface internal initializer to avoid singleton usage during tests.
|
|
|
|
|
- (instancetype)initWithAnalytics:(nullable id<FIRAnalyticsInterop>)analytics;
|
|
|
|
|
@end
|
|
|
|
|
|
|
|
|
|
typedef void (^FakeAnalyticsLogEventWithOriginNameParametersHandler)(
|
|
|
|
|
NSString *origin, NSString *name, NSDictionary<NSString *, id> *parameters);
|
|
|
|
|
|
|
|
|
|
@interface ABTConditionalUserPropertyControllerTest : XCTestCase {
|
|
|
|
|
ABTConditionalUserPropertyController *_ABTCUPController;
|
|
|
|
|
ABTFakeFIRAConditionalUserPropertyController *_fakeController;
|
|
|
|
|
id _mockCUPController;
|
|
|
|
|
}
|
|
|
|
|
@end
|
|
|
|
|
|
2020-07-06 13:06:39 -04:00
|
|
|
@interface ABTExperimentPayload (Testing)
|
|
|
|
|
@property(nonatomic, readwrite) ABTExperimentPayloadExperimentOverflowPolicy overflowPolicy;
|
|
|
|
|
@end
|
|
|
|
|
|
2019-08-05 19:30:54 -07:00
|
|
|
@implementation ABTConditionalUserPropertyControllerTest
|
|
|
|
|
- (void)setUp {
|
|
|
|
|
[super setUp];
|
|
|
|
|
|
|
|
|
|
_fakeController = [ABTFakeFIRAConditionalUserPropertyController sharedInstance];
|
|
|
|
|
_ABTCUPController = [[ABTConditionalUserPropertyController alloc]
|
|
|
|
|
initWithAnalytics:[[FakeAnalytics alloc] initWithFakeController:_fakeController]];
|
|
|
|
|
_mockCUPController = OCMPartialMock(_ABTCUPController);
|
|
|
|
|
OCMStub([_mockCUPController maxNumberOfExperimentsOfOrigin:[OCMArg any]]).andReturn(3);
|
|
|
|
|
|
|
|
|
|
// Must initialize FIRApp before calling set experiment as Firebase Analytics internal event
|
|
|
|
|
// logging requires it.
|
2024-10-02 07:49:28 -07:00
|
|
|
FIROptions *options =
|
|
|
|
|
[[FIROptions alloc] initWithGoogleAppID:@"1:123456789012:ios:1234567890123456"
|
|
|
|
|
GCMSenderID:@"123456789012"];
|
2019-08-05 19:30:54 -07:00
|
|
|
[FIRApp configureWithOptions:options];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (void)tearDown {
|
|
|
|
|
[_fakeController resetExperiments];
|
|
|
|
|
[_mockCUPController stopMocking];
|
|
|
|
|
[FIRApp resetApps];
|
|
|
|
|
[super tearDown];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#pragma mark - test proxy methods on Firebase Analytics
|
|
|
|
|
- (void)testSetExperiment {
|
2020-07-06 13:06:39 -04:00
|
|
|
id payload = OCMClassMock([ABTExperimentPayload class]);
|
|
|
|
|
OCMStub([payload experimentId]).andReturn(@"exp_0");
|
|
|
|
|
OCMStub([payload variantId]).andReturn(@"v1");
|
2019-08-05 19:30:54 -07:00
|
|
|
FIRLifecycleEvents *events = [[FIRLifecycleEvents alloc] init];
|
|
|
|
|
[_ABTCUPController
|
|
|
|
|
setExperimentWithOrigin:gABTTestOrigin
|
|
|
|
|
payload:payload
|
|
|
|
|
events:events
|
2020-07-06 13:06:39 -04:00
|
|
|
policy:ABTExperimentPayloadExperimentOverflowPolicyIgnoreNewest];
|
2019-08-05 19:30:54 -07:00
|
|
|
|
|
|
|
|
NSArray *experiments = [_ABTCUPController experimentsWithOrigin:gABTTestOrigin];
|
|
|
|
|
XCTAssertEqual(experiments.count, 1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (void)testSetExperimentWhenOverflow {
|
2020-07-06 13:06:39 -04:00
|
|
|
id payload1 = OCMClassMock([ABTExperimentPayload class]);
|
|
|
|
|
OCMStub([payload1 experimentId]).andReturn(@"exp_1");
|
|
|
|
|
OCMStub([payload1 variantId]).andReturn(@"v1");
|
2019-08-05 19:30:54 -07:00
|
|
|
FIRLifecycleEvents *events = [[FIRLifecycleEvents alloc] init];
|
|
|
|
|
[_ABTCUPController
|
|
|
|
|
setExperimentWithOrigin:gABTTestOrigin
|
2020-07-06 13:06:39 -04:00
|
|
|
payload:payload1
|
2019-08-05 19:30:54 -07:00
|
|
|
events:events
|
2020-07-06 13:06:39 -04:00
|
|
|
policy:ABTExperimentPayloadExperimentOverflowPolicyIgnoreNewest];
|
2019-08-05 19:30:54 -07:00
|
|
|
|
|
|
|
|
NSArray *experiments = [_ABTCUPController experimentsWithOrigin:gABTTestOrigin];
|
|
|
|
|
XCTAssertEqual(experiments.count, 1);
|
|
|
|
|
|
2020-07-06 13:06:39 -04:00
|
|
|
id payload2 = OCMClassMock([ABTExperimentPayload class]);
|
|
|
|
|
OCMStub([payload2 experimentId]).andReturn(@"exp_2");
|
|
|
|
|
OCMStub([payload2 variantId]).andReturn(@"v1");
|
2019-08-05 19:30:54 -07:00
|
|
|
[_ABTCUPController
|
|
|
|
|
setExperimentWithOrigin:gABTTestOrigin
|
2020-07-06 13:06:39 -04:00
|
|
|
payload:payload2
|
2019-08-05 19:30:54 -07:00
|
|
|
events:events
|
2020-07-06 13:06:39 -04:00
|
|
|
policy:ABTExperimentPayloadExperimentOverflowPolicyIgnoreNewest];
|
2019-08-05 19:30:54 -07:00
|
|
|
|
|
|
|
|
experiments = [_ABTCUPController experimentsWithOrigin:gABTTestOrigin];
|
|
|
|
|
XCTAssertEqual(experiments.count, 2);
|
|
|
|
|
|
2020-07-06 13:06:39 -04:00
|
|
|
id payload3 = OCMClassMock([ABTExperimentPayload class]);
|
|
|
|
|
OCMStub([payload3 experimentId]).andReturn(@"exp_3");
|
|
|
|
|
OCMStub([payload3 variantId]).andReturn(@"v1");
|
2019-08-05 19:30:54 -07:00
|
|
|
[_ABTCUPController
|
|
|
|
|
setExperimentWithOrigin:gABTTestOrigin
|
2020-07-06 13:06:39 -04:00
|
|
|
payload:payload3
|
2019-08-05 19:30:54 -07:00
|
|
|
events:events
|
2020-07-06 13:06:39 -04:00
|
|
|
policy:ABTExperimentPayloadExperimentOverflowPolicyIgnoreNewest];
|
2019-08-05 19:30:54 -07:00
|
|
|
|
|
|
|
|
experiments = [_ABTCUPController experimentsWithOrigin:gABTTestOrigin];
|
|
|
|
|
XCTAssertEqual(experiments.count, 3);
|
|
|
|
|
|
|
|
|
|
// Now it's overflowed, try setting a new experiment exp_4.
|
2020-07-06 13:06:39 -04:00
|
|
|
id payload4 = OCMClassMock([ABTExperimentPayload class]);
|
|
|
|
|
OCMStub([payload4 experimentId]).andReturn(@"exp_4");
|
|
|
|
|
OCMStub([payload4 variantId]).andReturn(@"v1");
|
2019-08-05 19:30:54 -07:00
|
|
|
// Try setting a new experiment with ignore newest policy.
|
|
|
|
|
[_ABTCUPController
|
|
|
|
|
setExperimentWithOrigin:gABTTestOrigin
|
2020-07-06 13:06:39 -04:00
|
|
|
payload:payload4
|
2019-08-05 19:30:54 -07:00
|
|
|
events:events
|
2020-07-06 13:06:39 -04:00
|
|
|
policy:ABTExperimentPayloadExperimentOverflowPolicyIgnoreNewest];
|
2019-08-05 19:30:54 -07:00
|
|
|
|
|
|
|
|
experiments = [_ABTCUPController experimentsWithOrigin:gABTTestOrigin];
|
|
|
|
|
XCTAssertEqual(experiments.count, 3);
|
|
|
|
|
|
|
|
|
|
XCTAssertTrue([self isExperimentID:@"exp_1" variantID:@"v1" inExperiments:experiments]);
|
|
|
|
|
XCTAssertTrue([self isExperimentID:@"exp_2" variantID:@"v1" inExperiments:experiments]);
|
|
|
|
|
XCTAssertTrue([self isExperimentID:@"exp_3" variantID:@"v1" inExperiments:experiments]);
|
|
|
|
|
XCTAssertFalse([self isExperimentID:@"exp_4" variantID:@"v1" inExperiments:experiments]);
|
|
|
|
|
|
|
|
|
|
// Try setting a new experiment with discard oldest policy.
|
|
|
|
|
[_ABTCUPController
|
|
|
|
|
setExperimentWithOrigin:gABTTestOrigin
|
2020-07-06 13:06:39 -04:00
|
|
|
payload:payload4
|
2019-08-05 19:30:54 -07:00
|
|
|
events:events
|
2020-07-06 13:06:39 -04:00
|
|
|
policy:ABTExperimentPayloadExperimentOverflowPolicyDiscardOldest];
|
2019-08-05 19:30:54 -07:00
|
|
|
experiments = [_ABTCUPController experimentsWithOrigin:gABTTestOrigin];
|
|
|
|
|
XCTAssertEqual(experiments.count, 3);
|
|
|
|
|
XCTAssertFalse([self isExperimentID:@"exp_1" variantID:@"v1" inExperiments:experiments]);
|
|
|
|
|
XCTAssertTrue([self isExperimentID:@"exp_4" variantID:@"v1" inExperiments:experiments]);
|
|
|
|
|
|
|
|
|
|
// Try setting a new experiment with unspecified policy
|
2020-07-06 13:06:39 -04:00
|
|
|
id payload5 = OCMClassMock([ABTExperimentPayload class]);
|
|
|
|
|
OCMStub([payload5 experimentId]).andReturn(@"exp_5");
|
|
|
|
|
OCMStub([payload5 variantId]).andReturn(@"v1");
|
2019-08-05 19:30:54 -07:00
|
|
|
[_ABTCUPController
|
|
|
|
|
setExperimentWithOrigin:gABTTestOrigin
|
2020-07-06 13:06:39 -04:00
|
|
|
payload:payload5
|
2019-08-05 19:30:54 -07:00
|
|
|
events:events
|
2020-07-06 13:06:39 -04:00
|
|
|
policy:ABTExperimentPayloadExperimentOverflowPolicyUnspecified];
|
2019-08-05 19:30:54 -07:00
|
|
|
|
|
|
|
|
experiments = [_ABTCUPController experimentsWithOrigin:gABTTestOrigin];
|
|
|
|
|
XCTAssertEqual(experiments.count, 3);
|
|
|
|
|
XCTAssertFalse([self isExperimentID:@"exp_2" variantID:@"v1" inExperiments:experiments]);
|
|
|
|
|
XCTAssertTrue([self isExperimentID:@"exp_3" variantID:@"v1" inExperiments:experiments]);
|
|
|
|
|
XCTAssertTrue([self isExperimentID:@"exp_4" variantID:@"v1" inExperiments:experiments]);
|
|
|
|
|
XCTAssertTrue([self isExperimentID:@"exp_5" variantID:@"v1" inExperiments:experiments]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (void)testSetExperimentWithTheSameVariantID {
|
2020-07-06 13:06:39 -04:00
|
|
|
id payload1 = OCMClassMock([ABTExperimentPayload class]);
|
|
|
|
|
OCMStub([payload1 experimentId]).andReturn(@"exp_1");
|
|
|
|
|
OCMStub([payload1 variantId]).andReturn(@"v1");
|
2019-08-05 19:30:54 -07:00
|
|
|
FIRLifecycleEvents *events = [[FIRLifecycleEvents alloc] init];
|
|
|
|
|
[_ABTCUPController
|
|
|
|
|
setExperimentWithOrigin:gABTTestOrigin
|
2020-07-06 13:06:39 -04:00
|
|
|
payload:payload1
|
2019-08-05 19:30:54 -07:00
|
|
|
events:events
|
2020-07-06 13:06:39 -04:00
|
|
|
policy:ABTExperimentPayloadExperimentOverflowPolicyIgnoreNewest];
|
2019-08-05 19:30:54 -07:00
|
|
|
|
|
|
|
|
NSArray *experiments = [_ABTCUPController experimentsWithOrigin:gABTTestOrigin];
|
|
|
|
|
XCTAssertEqual(experiments.count, 1);
|
|
|
|
|
XCTAssertTrue([self isExperimentID:@"exp_1" variantID:@"v1" inExperiments:experiments]);
|
|
|
|
|
|
2020-07-06 13:06:39 -04:00
|
|
|
id payload2 = OCMClassMock([ABTExperimentPayload class]);
|
|
|
|
|
OCMStub([payload2 experimentId]).andReturn(@"exp_1");
|
|
|
|
|
OCMStub([payload2 variantId]).andReturn(@"v2");
|
2019-08-05 19:30:54 -07:00
|
|
|
[_ABTCUPController
|
|
|
|
|
setExperimentWithOrigin:gABTTestOrigin
|
2020-07-06 13:06:39 -04:00
|
|
|
payload:payload2
|
2019-08-05 19:30:54 -07:00
|
|
|
events:events
|
2020-07-06 13:06:39 -04:00
|
|
|
policy:ABTExperimentPayloadExperimentOverflowPolicyIgnoreNewest];
|
2019-08-05 19:30:54 -07:00
|
|
|
|
|
|
|
|
experiments = [_ABTCUPController experimentsWithOrigin:gABTTestOrigin];
|
|
|
|
|
XCTAssertEqual(experiments.count, 1);
|
|
|
|
|
XCTAssertTrue([self isExperimentID:@"exp_1" variantID:@"v2" inExperiments:experiments]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (BOOL)isExperimentID:(NSString *)experimentID
|
|
|
|
|
variantID:(NSString *)variantID
|
|
|
|
|
inExperiments:(NSArray *)experiments {
|
|
|
|
|
for (NSDictionary<NSString *, NSString *> *experiment in experiments) {
|
|
|
|
|
if ([experiment[@"name"] isEqualToString:experimentID] &&
|
|
|
|
|
[experiment[@"value"] isEqualToString:variantID]) {
|
|
|
|
|
return YES;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return NO;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (void)testClearExperiment {
|
2020-07-06 13:06:39 -04:00
|
|
|
id payload = OCMClassMock([ABTExperimentPayload class]);
|
|
|
|
|
OCMStub([payload experimentId]).andReturn(@"exp_1");
|
|
|
|
|
OCMStub([payload variantId]).andReturn(@"v1");
|
|
|
|
|
OCMStub([payload clearEventToLog]).andReturn(@"override_clear_event");
|
2019-08-05 19:30:54 -07:00
|
|
|
FIRLifecycleEvents *events = [[FIRLifecycleEvents alloc] init];
|
|
|
|
|
[_ABTCUPController
|
|
|
|
|
setExperimentWithOrigin:gABTTestOrigin
|
|
|
|
|
payload:payload
|
|
|
|
|
events:events
|
2020-07-06 13:06:39 -04:00
|
|
|
policy:ABTExperimentPayloadExperimentOverflowPolicyIgnoreNewest];
|
2019-08-05 19:30:54 -07:00
|
|
|
|
|
|
|
|
NSArray *experiments = [_ABTCUPController experimentsWithOrigin:gABTTestOrigin];
|
|
|
|
|
XCTAssertEqual(experiments.count, 1);
|
|
|
|
|
|
|
|
|
|
[_ABTCUPController clearExperiment:@"exp_1"
|
|
|
|
|
variantID:@"v1"
|
|
|
|
|
withOrigin:gABTTestOrigin
|
|
|
|
|
payload:payload
|
|
|
|
|
events:events];
|
|
|
|
|
experiments = [_ABTCUPController experimentsWithOrigin:gABTTestOrigin];
|
|
|
|
|
XCTAssertEqual(experiments.count, 0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (void)testMaxNumberOfExperiments {
|
|
|
|
|
XCTAssertEqual([_ABTCUPController maxNumberOfExperimentsOfOrigin:gABTTestOrigin], 3);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (void)testCreateExperiment {
|
|
|
|
|
NSTimeInterval now = [[NSDate date] timeIntervalSince1970];
|
2020-07-06 13:06:39 -04:00
|
|
|
|
|
|
|
|
id payload = OCMClassMock([ABTExperimentPayload class]);
|
|
|
|
|
OCMStub([payload experimentId]).andReturn(@"exp_1");
|
|
|
|
|
OCMStub([payload variantId]).andReturn(@"variant_B");
|
|
|
|
|
int64_t startTimeMillis = now * ABT_MSEC_PER_SEC;
|
|
|
|
|
OCMStub([payload experimentStartTimeMillis]).andReturn(startTimeMillis);
|
|
|
|
|
OCMStub([payload triggerEvent]).andReturn(@"");
|
|
|
|
|
int64_t triggerTimeoutMillis = (now + 1500) * ABT_MSEC_PER_SEC;
|
|
|
|
|
OCMStub([payload triggerTimeoutMillis]).andReturn(triggerTimeoutMillis);
|
|
|
|
|
int64_t timeToLiveMillis = (now + 60000) * ABT_MSEC_PER_SEC;
|
|
|
|
|
OCMStub([payload timeToLiveMillis]).andReturn(timeToLiveMillis);
|
2019-08-05 19:30:54 -07:00
|
|
|
|
|
|
|
|
FIRLifecycleEvents *events = [[FIRLifecycleEvents alloc] init];
|
|
|
|
|
events.activateExperimentEventName = @"_lifecycle_override_activate";
|
|
|
|
|
events.expireExperimentEventName = @"lifecycle_override_time_to_live";
|
|
|
|
|
|
|
|
|
|
NSDictionary<NSString *, id> *experiment =
|
|
|
|
|
[_ABTCUPController createExperimentFromOrigin:gABTTestOrigin payload:payload events:events];
|
|
|
|
|
|
|
|
|
|
NSDictionary<NSString *, id> *triggeredEvent = [experiment objectForKey:@"triggeredEvent"];
|
|
|
|
|
XCTAssertEqualObjects([experiment objectForKey:@"name"], @"exp_1");
|
|
|
|
|
XCTAssertEqualObjects([experiment objectForKey:@"value"], @"variant_B");
|
|
|
|
|
XCTAssertEqualObjects(gABTTestOrigin, [experiment objectForKey:@"origin"]);
|
2020-07-06 13:06:39 -04:00
|
|
|
XCTAssertEqualWithAccuracy(now, [[experiment objectForKey:@"creationTimestamp"] longLongValue],
|
|
|
|
|
1.0);
|
2019-08-05 19:30:54 -07:00
|
|
|
|
|
|
|
|
// Trigger event
|
|
|
|
|
XCTAssertEqualObjects(gABTTestOrigin, triggeredEvent[@"origin"]);
|
|
|
|
|
XCTAssertEqualObjects(triggeredEvent[@"name"], @"_lifecycle_override_activate",
|
|
|
|
|
@"Activate event name is overrided by lifecycle events.");
|
|
|
|
|
|
|
|
|
|
// Timeout event
|
|
|
|
|
NSDictionary<NSString *, id> *timedOutEvent = [experiment objectForKey:@"timedOutEvent"];
|
|
|
|
|
XCTAssertEqualObjects(gABTTestOrigin, timedOutEvent[@"origin"]);
|
|
|
|
|
XCTAssertEqualObjects(FIRTimeoutExperimentEventName, timedOutEvent[@"name"],
|
|
|
|
|
@"payload doesn't have timeout event name, use default one");
|
|
|
|
|
|
|
|
|
|
// Expired event
|
|
|
|
|
NSDictionary<NSString *, id> *expiredEvent = [experiment objectForKey:@"expiredEvent"];
|
|
|
|
|
XCTAssertEqualObjects(gABTTestOrigin, expiredEvent[@"origin"]);
|
|
|
|
|
XCTAssertEqualObjects(
|
|
|
|
|
@"lifecycle_override_time_to_live", expiredEvent[@"name"],
|
|
|
|
|
@"payload doesn't have expiry event name, but lifecycle event does, use lifecycle event");
|
|
|
|
|
|
|
|
|
|
// Trigger event name
|
|
|
|
|
XCTAssertEqualObjects(nil, [experiment objectForKey:@"triggerEventName"],
|
|
|
|
|
@"Empty trigger event must be set to nil");
|
|
|
|
|
|
|
|
|
|
// trigger timeout
|
2020-07-06 13:06:39 -04:00
|
|
|
XCTAssertEqualWithAccuracy(now + 1500,
|
|
|
|
|
[[experiment objectForKey:@"triggerTimeout"] longLongValue], 1.0);
|
2019-08-05 19:30:54 -07:00
|
|
|
|
|
|
|
|
// time to live
|
2020-07-06 13:06:39 -04:00
|
|
|
XCTAssertEqualWithAccuracy(now + 60000, [[experiment objectForKey:@"timeToLive"] longLongValue],
|
|
|
|
|
1.0);
|
2019-08-05 19:30:54 -07:00
|
|
|
|
|
|
|
|
// Overwrite all event names
|
2020-07-06 13:06:39 -04:00
|
|
|
id payloadWithCustomEventNames = OCMClassMock([ABTExperimentPayload class]);
|
|
|
|
|
OCMStub([payloadWithCustomEventNames experimentId]).andReturn(@"exp_1");
|
|
|
|
|
OCMStub([payloadWithCustomEventNames variantId]).andReturn(@"variant_B");
|
|
|
|
|
OCMStub([payloadWithCustomEventNames activateEventToLog]).andReturn(@"payload_override_activate");
|
|
|
|
|
OCMStub([payloadWithCustomEventNames ttlExpiryEventToLog])
|
|
|
|
|
.andReturn(@"payload_override_time_to_live");
|
|
|
|
|
OCMStub([payloadWithCustomEventNames timeoutEventToLog]).andReturn(@"payload_override_timeout");
|
|
|
|
|
OCMStub([payloadWithCustomEventNames triggerEvent]).andReturn(@"payload_override_trigger_event");
|
2019-08-05 19:30:54 -07:00
|
|
|
|
|
|
|
|
experiment = [_ABTCUPController createExperimentFromOrigin:gABTTestOrigin
|
2020-07-06 13:06:39 -04:00
|
|
|
payload:payloadWithCustomEventNames
|
2019-08-05 19:30:54 -07:00
|
|
|
events:events];
|
|
|
|
|
triggeredEvent = [experiment objectForKey:@"triggeredEvent"];
|
|
|
|
|
XCTAssertEqual(triggeredEvent[@"name"], @"payload_override_activate");
|
|
|
|
|
timedOutEvent = [experiment objectForKey:@"timedOutEvent"];
|
|
|
|
|
XCTAssertEqualObjects(timedOutEvent[@"name"], @"payload_override_timeout");
|
|
|
|
|
expiredEvent = [experiment objectForKey:@"expiredEvent"];
|
|
|
|
|
XCTAssertEqual(expiredEvent[@"name"], @"payload_override_time_to_live");
|
2020-07-06 13:06:39 -04:00
|
|
|
XCTAssertEqualObjects([experiment objectForKey:@"triggerEventName"],
|
|
|
|
|
@"payload_override_trigger_event");
|
2019-08-05 19:30:54 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#pragma mark - helpers
|
|
|
|
|
|
|
|
|
|
- (void)testIsExperimentTheSameAsPayload {
|
|
|
|
|
NSDictionary<NSString *, NSString *> *experiment =
|
|
|
|
|
@{@"name" : @"exp_1", @"value" : @"variant_control_group"};
|
|
|
|
|
|
2020-07-06 13:06:39 -04:00
|
|
|
id payload2 = OCMClassMock([ABTExperimentPayload class]);
|
|
|
|
|
OCMStub([payload2 experimentId]).andReturn(@"exp_2");
|
|
|
|
|
OCMStub([payload2 variantId]).andReturn(@"variant_group_A");
|
2019-08-05 19:30:54 -07:00
|
|
|
|
2020-07-06 13:06:39 -04:00
|
|
|
XCTAssertFalse([_ABTCUPController isExperiment:experiment theSameAsPayload:payload2]);
|
2019-08-05 19:30:54 -07:00
|
|
|
|
2020-07-06 13:06:39 -04:00
|
|
|
id payload1 = OCMClassMock([ABTExperimentPayload class]);
|
|
|
|
|
OCMStub([payload1 experimentId]).andReturn(@"exp_1");
|
|
|
|
|
OCMStub([payload1 variantId]).andReturn(@"variant_group_A");
|
2019-08-05 19:30:54 -07:00
|
|
|
|
2020-07-06 13:06:39 -04:00
|
|
|
XCTAssertFalse([_ABTCUPController isExperiment:experiment theSameAsPayload:payload1]);
|
|
|
|
|
|
|
|
|
|
id payloadJustRight = OCMClassMock([ABTExperimentPayload class]);
|
|
|
|
|
OCMStub([payloadJustRight experimentId]).andReturn(@"exp_1");
|
|
|
|
|
OCMStub([payloadJustRight variantId]).andReturn(@"variant_control_group");
|
|
|
|
|
XCTAssertTrue([_ABTCUPController isExperiment:experiment theSameAsPayload:payloadJustRight]);
|
2019-08-05 19:30:54 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (void)testOverflowPolicyWithPayload {
|
2020-07-06 13:06:39 -04:00
|
|
|
ABTExperimentPayload *payloadUnspecifiedPolicy =
|
|
|
|
|
[ABTTestUtilities payloadFromTestFilename:@"TestABTPayload3"];
|
2019-08-05 19:30:54 -07:00
|
|
|
|
2020-07-06 13:06:39 -04:00
|
|
|
XCTAssertEqual(ABTExperimentPayloadExperimentOverflowPolicyDiscardOldest,
|
|
|
|
|
[_ABTCUPController overflowPolicyWithPayload:payloadUnspecifiedPolicy
|
|
|
|
|
originalPolicy:-1000],
|
2019-08-05 19:30:54 -07:00
|
|
|
@"Payload policy is unspecified, original policy is invalid, should return "
|
|
|
|
|
@"default: DiscardOldest.");
|
|
|
|
|
|
|
|
|
|
XCTAssertEqual(
|
2020-07-06 13:06:39 -04:00
|
|
|
ABTExperimentPayloadExperimentOverflowPolicyIgnoreNewest,
|
2019-08-05 19:30:54 -07:00
|
|
|
[_ABTCUPController
|
2020-07-06 13:06:39 -04:00
|
|
|
overflowPolicyWithPayload:payloadUnspecifiedPolicy
|
|
|
|
|
originalPolicy:ABTExperimentPayloadExperimentOverflowPolicyIgnoreNewest],
|
2019-08-05 19:30:54 -07:00
|
|
|
@"Payload policy is unspecified, original policy is valid, use "
|
|
|
|
|
@"original policy.");
|
|
|
|
|
|
2020-07-06 13:06:39 -04:00
|
|
|
ABTExperimentPayload *payloadDiscardOldest =
|
|
|
|
|
[ABTTestUtilities payloadFromTestFilename:@"TestABTPayload1"];
|
|
|
|
|
payloadDiscardOldest.overflowPolicy = ABTExperimentPayloadExperimentOverflowPolicyDiscardOldest;
|
2019-08-05 19:30:54 -07:00
|
|
|
XCTAssertEqual(
|
2020-07-06 13:06:39 -04:00
|
|
|
ABTExperimentPayloadExperimentOverflowPolicyDiscardOldest,
|
2019-08-05 19:30:54 -07:00
|
|
|
[_ABTCUPController
|
2020-07-06 13:06:39 -04:00
|
|
|
overflowPolicyWithPayload:payloadDiscardOldest
|
|
|
|
|
originalPolicy:ABTExperimentPayloadExperimentOverflowPolicyIgnoreNewest],
|
2019-08-05 19:30:54 -07:00
|
|
|
@"Payload policy is specified, original policy is valid, but "
|
|
|
|
|
@"use Payload because Payload always wins.");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@end
|