mirror of
https://github.com/firebase/firebase-ios-sdk
synced 2026-11-09 22:36:01 +05:45
274 lines
12 KiB
Objective-C
274 lines
12 KiB
Objective-C
// Copyright 2020 Google LLC
|
|
//
|
|
// 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.
|
|
|
|
#import <XCTest/XCTest.h>
|
|
|
|
#import "FirebaseABTesting/Sources/ABTConstants.h"
|
|
#import "FirebaseABTesting/Sources/Private/ABTExperimentPayload.h"
|
|
#import "FirebaseABTesting/Tests/Unit/Utilities/ABTTestUtilities.h"
|
|
|
|
@interface ABTExperimentPayload (ClassTesting)
|
|
|
|
+ (NSDateFormatter *)experimentStartTimeFormatter;
|
|
|
|
@end
|
|
|
|
@interface ABTExperimentPayloadTest : XCTestCase
|
|
|
|
@end
|
|
|
|
@implementation ABTExperimentPayloadTest
|
|
|
|
- (void)testPayloadWithTrigger {
|
|
ABTExperimentPayload *testPayload = [ABTTestUtilities payloadFromTestFilename:@"TestABTPayload1"];
|
|
XCTAssertEqualObjects(testPayload.experimentId, @"exp_1");
|
|
XCTAssertEqualObjects(testPayload.variantId, @"var_1");
|
|
XCTAssertEqualObjects(testPayload.triggerEvent, @"customTrigger");
|
|
|
|
// From the experiment resource file.
|
|
NSString *startTimeString = @"2020-04-08T16:44:39.023Z";
|
|
NSDate *startTime = [self dateFromFormattedDateString:startTimeString];
|
|
NSTimeInterval startTimeInterval = [startTime timeIntervalSince1970];
|
|
XCTAssertEqual(testPayload.experimentStartTimeMillis, startTimeInterval * ABT_MSEC_PER_SEC);
|
|
|
|
XCTAssertEqual(testPayload.triggerTimeoutMillis, 15552000000);
|
|
XCTAssertEqual(testPayload.timeToLiveMillis, 15552000000);
|
|
XCTAssertEqualObjects(testPayload.setEventToLog, @"set_event");
|
|
XCTAssertEqualObjects(testPayload.activateEventToLog, @"activate_event");
|
|
XCTAssertEqualObjects(testPayload.clearEventToLog, @"clear_event");
|
|
XCTAssertEqualObjects(testPayload.timeoutEventToLog, @"timeout_event");
|
|
XCTAssertEqualObjects(testPayload.ttlExpiryEventToLog, @"ttl_expiry_event");
|
|
XCTAssertEqual(testPayload.overflowPolicy,
|
|
ABTExperimentPayloadExperimentOverflowPolicyIgnoreNewest);
|
|
XCTAssertEqual(testPayload.ongoingExperiments.count, 1);
|
|
ABTExperimentLite *liteExperiment = testPayload.ongoingExperiments.firstObject;
|
|
XCTAssertEqualObjects(liteExperiment.experimentId, @"exp_1");
|
|
}
|
|
|
|
- (void)testPayloadWithoutTrigger {
|
|
ABTExperimentPayload *testPayload = [ABTTestUtilities payloadFromTestFilename:@"TestABTPayload2"];
|
|
XCTAssertEqualObjects(testPayload.experimentId, @"exp_2");
|
|
XCTAssertEqualObjects(testPayload.variantId, @"v200");
|
|
XCTAssertNil(testPayload.triggerEvent);
|
|
|
|
// From the experiment resource file.
|
|
NSString *startTimeString = @"2020-06-01T16:00:00.000Z";
|
|
NSDate *startTime = [self dateFromFormattedDateString:startTimeString];
|
|
NSTimeInterval startTimeInterval = [startTime timeIntervalSince1970];
|
|
XCTAssertEqual(testPayload.experimentStartTimeMillis, startTimeInterval * ABT_MSEC_PER_SEC);
|
|
|
|
XCTAssertEqual(testPayload.triggerTimeoutMillis, 15452000000);
|
|
XCTAssertEqual(testPayload.timeToLiveMillis, 15452000000);
|
|
XCTAssertEqualObjects(testPayload.setEventToLog, @"set_event_override");
|
|
XCTAssertEqualObjects(testPayload.activateEventToLog, @"activate_event_override");
|
|
XCTAssertEqualObjects(testPayload.clearEventToLog, @"clear_event_override");
|
|
XCTAssertEqualObjects(testPayload.timeoutEventToLog, @"timeout_event_override");
|
|
XCTAssertEqualObjects(testPayload.ttlExpiryEventToLog, @"ttl_expiry_event_override");
|
|
XCTAssertEqual(testPayload.overflowPolicy,
|
|
ABTExperimentPayloadExperimentOverflowPolicyDiscardOldest);
|
|
}
|
|
|
|
/// Verifies that we initialize the payload if it has a start time parameter with millis, rather
|
|
/// than a date string.
|
|
- (void)testPayloadInitializesStartTimeWithMillis {
|
|
ABTExperimentPayload *testPayload = [ABTTestUtilities payloadFromTestFilename:@"TestABTPayload5"];
|
|
XCTAssertEqual(testPayload.experimentStartTimeMillis, 143);
|
|
}
|
|
|
|
- (void)testPayloadInitializationWithString {
|
|
ABTExperimentPayload *unrecognizedOverflowPolicyString =
|
|
[[ABTExperimentPayload alloc] initWithDictionary:@{@"overflowPolicy" : @"WWDC"}];
|
|
XCTAssertEqual(unrecognizedOverflowPolicyString.overflowPolicy,
|
|
ABTExperimentPayloadExperimentOverflowPolicyUnrecognizedValue);
|
|
|
|
ABTExperimentPayload *ignoreNewestOverflowPolicyString =
|
|
[[ABTExperimentPayload alloc] initWithDictionary:@{@"overflowPolicy" : @"IGNORE_NEWEST"}];
|
|
XCTAssertEqual(ignoreNewestOverflowPolicyString.overflowPolicy,
|
|
ABTExperimentPayloadExperimentOverflowPolicyIgnoreNewest);
|
|
|
|
ABTExperimentPayload *discardOldestOverflowPolicyString =
|
|
[[ABTExperimentPayload alloc] initWithDictionary:@{@"overflowPolicy" : @"DISCARD_OLDEST"}];
|
|
XCTAssertEqual(discardOldestOverflowPolicyString.overflowPolicy,
|
|
ABTExperimentPayloadExperimentOverflowPolicyDiscardOldest);
|
|
}
|
|
|
|
- (void)testUtilityMethods {
|
|
ABTExperimentPayload *testPayload1 =
|
|
[ABTTestUtilities payloadFromTestFilename:@"TestABTPayload1"];
|
|
XCTAssertTrue([testPayload1 overflowPolicyIsValid]);
|
|
|
|
// Clear trigger event and make sure it's now nil.
|
|
[testPayload1 clearTriggerEvent];
|
|
|
|
// This one has an unspecified overflow policy.
|
|
ABTExperimentPayload *testPayload3 =
|
|
[ABTTestUtilities payloadFromTestFilename:@"TestABTPayload3"];
|
|
XCTAssertFalse([testPayload3 overflowPolicyIsValid]);
|
|
}
|
|
|
|
#pragma mark - Malformed payloads
|
|
|
|
- (void)testParseFromNilOrEmptyData {
|
|
#pragma clang diagnostic push
|
|
#pragma clang diagnostic ignored "-Wnonnull"
|
|
XCTAssertNil([ABTExperimentPayload parseFromData:nil]);
|
|
#pragma clang diagnostic pop
|
|
XCTAssertNil([ABTExperimentPayload parseFromData:[NSData data]]);
|
|
}
|
|
|
|
- (void)testParseFromNonDataObject {
|
|
// Callers pass objects out of untyped arrays; a non-NSData object must not crash.
|
|
XCTAssertNil([ABTExperimentPayload parseFromData:(NSData *)[NSNull null]]);
|
|
XCTAssertNil([ABTExperimentPayload parseFromData:(NSData *)@"{\"experimentId\":\"exp_1\"}"]);
|
|
}
|
|
|
|
- (void)testParseFromGarbageData {
|
|
NSData *garbage = [@"{\"experimentId\": \"exp_1\", " dataUsingEncoding:NSUTF8StringEncoding];
|
|
XCTAssertNil([ABTExperimentPayload parseFromData:garbage]);
|
|
}
|
|
|
|
- (void)testParseFromNonObjectJSON {
|
|
// Valid JSON whose top level is not an object must be rejected rather than crash.
|
|
for (NSString *json in
|
|
@[ @"[]", @"[{\"experimentId\":\"exp_1\"}]", @"\"exp_1\"", @"42", @"null", @"true" ]) {
|
|
NSData *data = [json dataUsingEncoding:NSUTF8StringEncoding];
|
|
XCTAssertNil([ABTExperimentPayload parseFromData:data], @"%@", json);
|
|
}
|
|
}
|
|
|
|
- (void)testInitWithNonDictionary {
|
|
// In-App Messaging hands the server's `experimentPayload` node straight to this initializer.
|
|
for (id notADictionary in @[ @"exp_1", @[ @"exp_1" ], @42, [NSNull null] ]) {
|
|
ABTExperimentPayload *payload =
|
|
[[ABTExperimentPayload alloc] initWithDictionary:(NSDictionary *)notADictionary];
|
|
XCTAssertNotNil(payload);
|
|
XCTAssertNil(payload.experimentId);
|
|
XCTAssertNil(payload.variantId);
|
|
XCTAssertEqual(payload.experimentStartTimeMillis, 0);
|
|
XCTAssertEqual(payload.ongoingExperiments.count, 0);
|
|
XCTAssertFalse([payload overflowPolicyIsValid]);
|
|
}
|
|
}
|
|
|
|
- (void)testInitWithNullValues {
|
|
NSNull *null = [NSNull null];
|
|
NSDictionary *dictionary = @{
|
|
@"experimentId" : null,
|
|
@"variantId" : null,
|
|
@"experimentStartTime" : null,
|
|
@"triggerEvent" : null,
|
|
@"triggerTimeoutMillis" : null,
|
|
@"timeToLiveMillis" : null,
|
|
@"setEventToLog" : null,
|
|
@"activateEventToLog" : null,
|
|
@"clearEventToLog" : null,
|
|
@"timeoutEventToLog" : null,
|
|
@"ttlExpiryEventToLog" : null,
|
|
@"overflowPolicy" : null,
|
|
@"ongoingExperiments" : null,
|
|
};
|
|
ABTExperimentPayload *payload = [[ABTExperimentPayload alloc] initWithDictionary:dictionary];
|
|
XCTAssertNil(payload.experimentId);
|
|
XCTAssertNil(payload.variantId);
|
|
XCTAssertNil(payload.triggerEvent);
|
|
XCTAssertNil(payload.setEventToLog);
|
|
XCTAssertNil(payload.activateEventToLog);
|
|
XCTAssertNil(payload.clearEventToLog);
|
|
XCTAssertNil(payload.timeoutEventToLog);
|
|
XCTAssertNil(payload.ttlExpiryEventToLog);
|
|
XCTAssertEqual(payload.experimentStartTimeMillis, 0);
|
|
XCTAssertEqual(payload.triggerTimeoutMillis, 0);
|
|
XCTAssertEqual(payload.timeToLiveMillis, 0);
|
|
XCTAssertEqual(payload.overflowPolicy, ABTExperimentPayloadExperimentOverflowPolicyUnspecified);
|
|
XCTAssertEqual(payload.ongoingExperiments.count, 0);
|
|
}
|
|
|
|
- (void)testInitWithWrongValueTypes {
|
|
NSDictionary *dictionary = @{
|
|
@"experimentId" : @123,
|
|
@"variantId" : @[ @"v1" ],
|
|
@"experimentStartTime" : @456,
|
|
@"experimentStartTimeMillis" : @{},
|
|
@"triggerEvent" : @7,
|
|
@"triggerTimeoutMillis" : @[ @1 ],
|
|
@"timeToLiveMillis" : @{@"a" : @"b"},
|
|
@"setEventToLog" : @1,
|
|
@"activateEventToLog" : @[],
|
|
@"clearEventToLog" : @{},
|
|
@"timeoutEventToLog" : @YES,
|
|
@"ttlExpiryEventToLog" : @2.5,
|
|
@"overflowPolicy" : @[ @"DISCARD_OLDEST" ],
|
|
@"ongoingExperiments" : @"exp_1",
|
|
};
|
|
ABTExperimentPayload *payload = [[ABTExperimentPayload alloc] initWithDictionary:dictionary];
|
|
XCTAssertNil(payload.experimentId);
|
|
XCTAssertNil(payload.variantId);
|
|
XCTAssertNil(payload.triggerEvent);
|
|
XCTAssertNil(payload.setEventToLog);
|
|
XCTAssertNil(payload.activateEventToLog);
|
|
XCTAssertNil(payload.clearEventToLog);
|
|
XCTAssertNil(payload.timeoutEventToLog);
|
|
XCTAssertNil(payload.ttlExpiryEventToLog);
|
|
XCTAssertEqual(payload.experimentStartTimeMillis, 0);
|
|
XCTAssertEqual(payload.triggerTimeoutMillis, 0);
|
|
XCTAssertEqual(payload.timeToLiveMillis, 0);
|
|
XCTAssertEqual(payload.overflowPolicy, ABTExperimentPayloadExperimentOverflowPolicyUnspecified);
|
|
XCTAssertEqual(payload.ongoingExperiments.count, 0);
|
|
}
|
|
|
|
- (void)testInitWithNumericStrings {
|
|
// Int64 JSON values are commonly encoded as strings; keep accepting them.
|
|
ABTExperimentPayload *payload = [[ABTExperimentPayload alloc] initWithDictionary:@{
|
|
@"experimentStartTimeMillis" : @"143",
|
|
@"triggerTimeoutMillis" : @"1000",
|
|
@"timeToLiveMillis" : @2000,
|
|
@"overflowPolicy" : @2,
|
|
}];
|
|
XCTAssertEqual(payload.experimentStartTimeMillis, 143);
|
|
XCTAssertEqual(payload.triggerTimeoutMillis, 1000);
|
|
XCTAssertEqual(payload.timeToLiveMillis, 2000);
|
|
XCTAssertEqual(payload.overflowPolicy, ABTExperimentPayloadExperimentOverflowPolicyIgnoreNewest);
|
|
}
|
|
|
|
- (void)testInitWithUnparseableStartTimeString {
|
|
ABTExperimentPayload *payload = [[ABTExperimentPayload alloc]
|
|
initWithDictionary:@{@"experimentId" : @"exp_1", @"experimentStartTime" : @"not a date"}];
|
|
XCTAssertEqualObjects(payload.experimentId, @"exp_1");
|
|
XCTAssertEqual(payload.experimentStartTimeMillis, 0);
|
|
}
|
|
|
|
- (void)testInitWithMalformedOngoingExperiments {
|
|
NSDictionary *dictionary = @{
|
|
@"experimentId" : @"exp_1",
|
|
@"ongoingExperiments" : @[
|
|
[NSNull null], @"exp_2", @42, @[ @"exp_3" ], @{}, @{@"experimentId" : [NSNull null]},
|
|
@{@"experimentId" : @99}, @{@"experimentId" : @"exp_4"}
|
|
],
|
|
};
|
|
ABTExperimentPayload *payload = [[ABTExperimentPayload alloc] initWithDictionary:dictionary];
|
|
XCTAssertEqual(payload.ongoingExperiments.count, 1);
|
|
XCTAssertEqualObjects(payload.ongoingExperiments.firstObject.experimentId, @"exp_4");
|
|
|
|
// A dictionary instead of an array must not be enumerated.
|
|
payload = [[ABTExperimentPayload alloc]
|
|
initWithDictionary:@{@"ongoingExperiments" : @{@"experimentId" : @"exp_5"}}];
|
|
XCTAssertEqual(payload.ongoingExperiments.count, 0);
|
|
}
|
|
|
|
- (NSDate *)dateFromFormattedDateString:(NSString *)dateString {
|
|
return [[ABTExperimentPayload experimentStartTimeFormatter] dateFromString:dateString];
|
|
}
|
|
|
|
@end
|