From c5daeaee5670642a47f56795e8766e0da8e966db Mon Sep 17 00:00:00 2001 From: Martin Cardozo Date: Wed, 21 May 2025 12:16:40 -0300 Subject: [PATCH 01/46] Changing branch. Much stuff needs correction --- Split/Api/SplitView.swift | 17 +++++++++++++++++ Split/Models/SplitModel/Split.swift | 2 ++ Split/Storage/Splits/SplitsStorage.swift | 2 ++ SplitTests/Helpers/SplitTestHelper.swift | 1 + SplitTests/Helpers/TestDataHelper.swift | 4 ++++ SplitTests/Resources/splitchanges_1.json | 24 ++++++++++++++++++++++++ SplitTests/Resources/splits.json | 1 + 7 files changed, 51 insertions(+) diff --git a/Split/Api/SplitView.swift b/Split/Api/SplitView.swift index 0c665ec49..4b24f6e2e 100644 --- a/Split/Api/SplitView.swift +++ b/Split/Api/SplitView.swift @@ -28,3 +28,20 @@ public class SplitView: NSObject, Codable { @objc public var impressionsDisabled: Bool = false } + + +var name: String? +var seed: Int? +var status: Status? +var killed: Bool? +var defaultTreatment: String? +var conditions: [Condition]? +var trafficTypeName: String? +var changeNumber: Int64? +var trafficAllocation: Int? +var trafficAllocationSeed: Int? +var algo: Int? +var configurations: [String: String]? +var sets: Set? +var impressionsDisabled: Bool? +var preTrequisites: [String: [String]]? diff --git a/Split/Models/SplitModel/Split.swift b/Split/Models/SplitModel/Split.swift index 452deacfe..f1e8422af 100644 --- a/Split/Models/SplitModel/Split.swift +++ b/Split/Models/SplitModel/Split.swift @@ -25,6 +25,7 @@ class SplitDTO: NSObject, SplitBase, Codable { var configurations: [String: String]? var sets: Set? var impressionsDisabled: Bool? + var preTrequisites: [String: [String]]? var json: String = "" @@ -56,5 +57,6 @@ class SplitDTO: NSObject, SplitBase, Codable { case configurations case sets case impressionsDisabled + case preTrequisites } } diff --git a/Split/Storage/Splits/SplitsStorage.swift b/Split/Storage/Splits/SplitsStorage.swift index bf24625e5..679487f6c 100644 --- a/Split/Storage/Splits/SplitsStorage.swift +++ b/Split/Storage/Splits/SplitsStorage.swift @@ -221,3 +221,5 @@ class BackgroundSyncSplitsStorage: SyncSplitsStorage { persistentStorage.clear() } } + + diff --git a/SplitTests/Helpers/SplitTestHelper.swift b/SplitTests/Helpers/SplitTestHelper.swift index bb470553b..9c1bb910d 100644 --- a/SplitTests/Helpers/SplitTestHelper.swift +++ b/SplitTests/Helpers/SplitTestHelper.swift @@ -27,6 +27,7 @@ class SplitTestHelper { { "trafficTypeName":"account", "name":"FACUNDO_TEST", + "prereKILSITS":0, "trafficAllocation":59, "trafficAllocationSeed":-2108186082, "seed":-1947050785, diff --git a/SplitTests/Helpers/TestDataHelper.swift b/SplitTests/Helpers/TestDataHelper.swift index 417381c21..74196a9e2 100644 --- a/SplitTests/Helpers/TestDataHelper.swift +++ b/SplitTests/Helpers/TestDataHelper.swift @@ -54,6 +54,10 @@ class TestDataHelper { { "trafficTypeName":"account", "name":"FACUNDO_TEST", + "prereQquisites": [ + { "n": "flag1", "ts": ["on","v1"] }, + { "n": "flag2", "ts": ["off"] } + ], "trafficAllocation":59, "trafficAllocationSeed":-2108186082, "seed":-1947050785, diff --git a/SplitTests/Resources/splitchanges_1.json b/SplitTests/Resources/splitchanges_1.json index 420ef8e70..df0cd2209 100644 --- a/SplitTests/Resources/splitchanges_1.json +++ b/SplitTests/Resources/splitchanges_1.json @@ -3,6 +3,7 @@ { "trafficTypeName":"account", "name":"FACUNDO_TEST", + "preQER":[], "trafficAllocation":59, "trafficAllocationSeed":-2108186082, "seed":-1947050785, @@ -230,6 +231,7 @@ { "trafficTypeName":"account", "name":"testing222", + "preQER":[], "trafficAllocation":100, "trafficAllocationSeed":-397360967, "seed":1058132210, @@ -575,6 +577,7 @@ { "trafficTypeName":"account", "name":"test_string_without_attr", + "preQER":[], "trafficAllocation":100, "trafficAllocationSeed":-782597068, "seed":-1682478887, @@ -644,6 +647,7 @@ { "trafficTypeName":"user", "name":"OldTest", + "preQER":[], "trafficAllocation":100, "trafficAllocationSeed":217539832, "seed":52164426, @@ -827,6 +831,7 @@ { "trafficTypeName":"account", "name":"Test_Save_1", + "preQER":[], "trafficAllocation":100, "trafficAllocationSeed":-257595325, "seed":-665945237, @@ -1024,6 +1029,7 @@ { "trafficTypeName":"user", "name":"benchmark_jw_1", + "preQER":[], "trafficAllocation":100, "trafficAllocationSeed":987354894, "seed":1292874260, @@ -1110,6 +1116,7 @@ { "trafficTypeName":"user", "name":"nico_tests", + "preQER":[], "trafficAllocation":100, "trafficAllocationSeed":1409699192, "seed":-1997241870, @@ -1191,6 +1198,7 @@ { "trafficTypeName":"account", "name":"testo2222", + "preQER":[], "trafficAllocation":100, "trafficAllocationSeed":1164474086, "seed":1270508512, @@ -1432,6 +1440,7 @@ { "trafficTypeName":"user", "name":"Tagging", + "preQER":[], "trafficAllocation":100, "trafficAllocationSeed":1910132597, "seed":-311493896, @@ -1579,6 +1588,7 @@ { "trafficTypeName":"account", "name":"testo23", + "preQER":[], "trafficAllocation":100, "trafficAllocationSeed":-689658216, "seed":1711156051, @@ -1793,6 +1803,7 @@ { "trafficTypeName":"account", "name":"testo22", + "preQER":[], "trafficAllocation":100, "trafficAllocationSeed":1223277820, "seed":-1152948537, @@ -1889,6 +1900,7 @@ { "trafficTypeName":"account", "name":"test_dep_2", + "preQER":[], "trafficAllocation":100, "trafficAllocationSeed":-806171485, "seed":922684950, @@ -1981,6 +1993,7 @@ { "trafficTypeName":"account", "name":"Definition_As_Of_Clickable_UI", + "preQER":[], "trafficAllocation":100, "trafficAllocationSeed":-198035199, "seed":-151947071, @@ -2061,6 +2074,7 @@ { "trafficTypeName":"account", "name":"Identify_UI", + "preQER":[], "trafficAllocation":100, "trafficAllocationSeed":-139516103, "seed":1543172523, @@ -2109,6 +2123,7 @@ { "trafficTypeName":"account", "name":"test_definition_as_of", + "preQER":[], "trafficAllocation":100, "trafficAllocationSeed":1025823325, "seed":-554248124, @@ -2157,6 +2172,7 @@ { "trafficTypeName":"user", "name":"Test-jw-go", + "preQER":[], "trafficAllocation":100, "trafficAllocationSeed":768122971, "seed":1539205707, @@ -2235,6 +2251,7 @@ { "trafficTypeName":"user", "name":"benchmark_jw_7", + "preQER":[], "trafficAllocation":100, "trafficAllocationSeed":-1340337178, "seed":-1091938685, @@ -2283,6 +2300,7 @@ { "trafficTypeName":"user", "name":"benchmark_jw_6", + "preQER":[], "trafficAllocation":100, "trafficAllocationSeed":-1202331834, "seed":-48445256, @@ -2379,6 +2397,7 @@ { "trafficTypeName":"user", "name":"benchmark_jw_4", + "preQER":[], "trafficAllocation":100, "trafficAllocationSeed":1066635158, "seed":-850704283, @@ -2427,6 +2446,7 @@ { "trafficTypeName":"user", "name":"benchmark_jw_3", + "preQER":[], "trafficAllocation":100, "trafficAllocationSeed":1252392550, "seed":971538037, @@ -2475,6 +2495,7 @@ { "trafficTypeName":"user", "name":"benchmark_jw_2", + "preQER":[], "trafficAllocation":100, "trafficAllocationSeed":-285565213, "seed":-1992295819, @@ -2533,6 +2554,7 @@ { "trafficTypeName":"account", "name":"TEST_SETS_1", + "preQER":[], "trafficAllocation":59, "trafficAllocationSeed":-2108186082, "seed":-1947050785, @@ -2649,6 +2671,7 @@ { "trafficTypeName":"account", "name":"TEST_SETS_2", + "preQER":[], "trafficAllocation":59, "trafficAllocationSeed":-2108186082, "seed":-1947050785, @@ -2768,6 +2791,7 @@ { "trafficTypeName":"account", "name":"TEST_SETS_3", + "preQER":[], "trafficAllocation":59, "trafficAllocationSeed":-2108186082, "seed":-1947050785, diff --git a/SplitTests/Resources/splits.json b/SplitTests/Resources/splits.json index 113ecb633..e2a608b48 100644 --- a/SplitTests/Resources/splits.json +++ b/SplitTests/Resources/splits.json @@ -1,6 +1,7 @@ [{ "trafficTypeName": "custom", "name": "SAMPLE_FEATURE0", + "ASDSAL":[], "trafficAllocation": 100, "trafficAllocationSeed": 1595297106, "seed": -1332540447, From 1ae502c6f9a18be8b9e15b01ba333fa508bd1158 Mon Sep 17 00:00:00 2001 From: Martin Cardozo Date: Wed, 21 May 2025 17:00:36 -0300 Subject: [PATCH 02/46] Tests added --- Split.xcodeproj/project.pbxproj | 25 ++-- Split/Common/Utils/SplitHelper.swift | 2 +- Split/Models/SplitModel/Split.swift | 23 ++-- Split/Storage/Splits/SplitsStorage.swift | 2 +- SplitTests/Helpers/SplitTestHelper.swift | 4 +- SplitTests/Helpers/TestingHelper.swift | 4 +- .../Splits/PeriodicSplitsSyncWorkerTest.swift | 2 +- .../Service/Splits/SplitsSyncWorkerTest.swift | 2 +- SplitTests/SplitDTOTests.swift | 114 ++++++++++++++++++ SplitTests/Storage/SplitsStorageTests.swift | 2 +- .../SplitsStorageTrafficTypesTests.swift | 2 +- .../ValidatorsTests/EventValidatorTests.swift | 2 +- .../ValidatorsTests/SplitValidatorTests.swift | 2 +- 13 files changed, 153 insertions(+), 33 deletions(-) create mode 100644 SplitTests/SplitDTOTests.swift diff --git a/Split.xcodeproj/project.pbxproj b/Split.xcodeproj/project.pbxproj index 1bb0283b4..4e1c1d741 100644 --- a/Split.xcodeproj/project.pbxproj +++ b/Split.xcodeproj/project.pbxproj @@ -353,6 +353,7 @@ 59FB7C35220329B900ECC96A /* SplitFactoryBuilderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 59FB7C34220329B900ECC96A /* SplitFactoryBuilderTests.swift */; }; 59FB7C3C2203795F00ECC96A /* LocalhostSplitsParser.swift in Sources */ = {isa = PBXBuildFile; fileRef = 59FB7C3B2203795F00ECC96A /* LocalhostSplitsParser.swift */; }; 59FB7C3E22037B9400ECC96A /* SpaceDelimitedLocalhostSplitsParser.swift in Sources */ = {isa = PBXBuildFile; fileRef = 59FB7C3D22037B9400ECC96A /* SpaceDelimitedLocalhostSplitsParser.swift */; }; + 5B91B8392DDE4A3B000510F0 /* SplitDTOTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B91B8382DDE4A30000510F0 /* SplitDTOTests.swift */; }; 9500D9922C56F9BA00383593 /* HostDomainFilterTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9500D9912C56F9BA00383593 /* HostDomainFilterTests.swift */; }; 9500D9A92C59297400383593 /* HostDomainFilter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9500D9A82C59297400383593 /* HostDomainFilter.swift */; }; 9500D9AA2C59382000383593 /* HostDomainFilter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9500D9A82C59297400383593 /* HostDomainFilter.swift */; }; @@ -1074,12 +1075,6 @@ 95F8F06828170473009E09B1 /* multi_client_test.json in Resources */ = {isa = PBXBuildFile; fileRef = 95F8F06728170473009E09B1 /* multi_client_test.json */; }; C514F6B62BEA6F1F00C8DF78 /* HttpParameters.swift in Sources */ = {isa = PBXBuildFile; fileRef = C514F6B52BEA6F1F00C8DF78 /* HttpParameters.swift */; }; C526DE4C2D9B09EB0051DAB8 /* ImpressionsPropertiesE2ETest.swift in Sources */ = {isa = PBXBuildFile; fileRef = C526DE4B2D9B09EB0051DAB8 /* ImpressionsPropertiesE2ETest.swift */; }; - C539CADC2D93229D0050C732 /* EvaluationOptions.swift in Sources */ = {isa = PBXBuildFile; fileRef = C539CADB2D93229D0050C732 /* EvaluationOptions.swift */; }; - C539CADD2D93229D0050C732 /* EvaluationOptions.swift in Sources */ = {isa = PBXBuildFile; fileRef = C539CADB2D93229D0050C732 /* EvaluationOptions.swift */; }; - C539CADF2D9475CD0050C732 /* PropertyValidator.swift in Sources */ = {isa = PBXBuildFile; fileRef = C539CADE2D9475CD0050C732 /* PropertyValidator.swift */; }; - C539CAE02D9475CD0050C732 /* PropertyValidator.swift in Sources */ = {isa = PBXBuildFile; fileRef = C539CADE2D9475CD0050C732 /* PropertyValidator.swift */; }; - C539CAE22D9477770050C732 /* PropertyValidatorStub.swift in Sources */ = {isa = PBXBuildFile; fileRef = C539CAE12D9477770050C732 /* PropertyValidatorStub.swift */; }; - C539CAE62D947D2A0050C732 /* PropertyValidatorTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = C539CAE32D947D2A0050C732 /* PropertyValidatorTest.swift */; }; C539CAB62D88C1F10050C732 /* RuleBasedSegment.swift in Sources */ = {isa = PBXBuildFile; fileRef = C539CAB52D88C1F10050C732 /* RuleBasedSegment.swift */; }; C539CABE2D88C7410050C732 /* PersistentRuleBasedSegmentsStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = C539CABC2D88C7410050C732 /* PersistentRuleBasedSegmentsStorage.swift */; }; C539CABF2D88C7410050C732 /* RuleBasedSegmentsStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = C539CABD2D88C7410050C732 /* RuleBasedSegmentsStorage.swift */; }; @@ -1099,6 +1094,12 @@ C539CAD72D8B5AD00050C732 /* TargetingRulesChange.swift in Sources */ = {isa = PBXBuildFile; fileRef = C539CAD32D8B5AD00050C732 /* TargetingRulesChange.swift */; }; C539CAD92D8B5AF00050C732 /* ProcessedRuleBasedSegmentChange.swift in Sources */ = {isa = PBXBuildFile; fileRef = C539CAD82D8B5AF00050C732 /* ProcessedRuleBasedSegmentChange.swift */; }; C539CADA2D8B5AF00050C732 /* ProcessedRuleBasedSegmentChange.swift in Sources */ = {isa = PBXBuildFile; fileRef = C539CAD82D8B5AF00050C732 /* ProcessedRuleBasedSegmentChange.swift */; }; + C539CADC2D93229D0050C732 /* EvaluationOptions.swift in Sources */ = {isa = PBXBuildFile; fileRef = C539CADB2D93229D0050C732 /* EvaluationOptions.swift */; }; + C539CADD2D93229D0050C732 /* EvaluationOptions.swift in Sources */ = {isa = PBXBuildFile; fileRef = C539CADB2D93229D0050C732 /* EvaluationOptions.swift */; }; + C539CADF2D9475CD0050C732 /* PropertyValidator.swift in Sources */ = {isa = PBXBuildFile; fileRef = C539CADE2D9475CD0050C732 /* PropertyValidator.swift */; }; + C539CAE02D9475CD0050C732 /* PropertyValidator.swift in Sources */ = {isa = PBXBuildFile; fileRef = C539CADE2D9475CD0050C732 /* PropertyValidator.swift */; }; + C539CAE22D9477770050C732 /* PropertyValidatorStub.swift in Sources */ = {isa = PBXBuildFile; fileRef = C539CAE12D9477770050C732 /* PropertyValidatorStub.swift */; }; + C539CAE62D947D2A0050C732 /* PropertyValidatorTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = C539CAE32D947D2A0050C732 /* PropertyValidatorTest.swift */; }; C53EDFA92DD295E5000DCDBC /* TargetingRulesChangeDecoderTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = C53EDFA82DD295E5000DCDBC /* TargetingRulesChangeDecoderTest.swift */; }; C53EDFAB2DD29640000DCDBC /* TargetingRulesChangeDecoder.swift in Sources */ = {isa = PBXBuildFile; fileRef = C53EDFAA2DD29640000DCDBC /* TargetingRulesChangeDecoder.swift */; }; C53EDFAC2DD29640000DCDBC /* TargetingRulesChangeDecoder.swift in Sources */ = {isa = PBXBuildFile; fileRef = C53EDFAA2DD29640000DCDBC /* TargetingRulesChangeDecoder.swift */; }; @@ -1548,6 +1549,7 @@ 59FB7C34220329B900ECC96A /* SplitFactoryBuilderTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SplitFactoryBuilderTests.swift; sourceTree = ""; }; 59FB7C3B2203795F00ECC96A /* LocalhostSplitsParser.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocalhostSplitsParser.swift; sourceTree = ""; }; 59FB7C3D22037B9400ECC96A /* SpaceDelimitedLocalhostSplitsParser.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SpaceDelimitedLocalhostSplitsParser.swift; sourceTree = ""; }; + 5B91B8382DDE4A30000510F0 /* SplitDTOTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SplitDTOTests.swift; sourceTree = ""; }; 9500D9912C56F9BA00383593 /* HostDomainFilterTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HostDomainFilterTests.swift; sourceTree = ""; }; 9500D9A82C59297400383593 /* HostDomainFilter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HostDomainFilter.swift; sourceTree = ""; }; 9500D9AC2C5A918300383593 /* split_cache_v5.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = split_cache_v5.xcdatamodel; sourceTree = ""; }; @@ -1961,10 +1963,6 @@ 95F8F06728170473009E09B1 /* multi_client_test.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = multi_client_test.json; sourceTree = ""; }; C514F6B52BEA6F1F00C8DF78 /* HttpParameters.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HttpParameters.swift; sourceTree = ""; }; C526DE4B2D9B09EB0051DAB8 /* ImpressionsPropertiesE2ETest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImpressionsPropertiesE2ETest.swift; sourceTree = ""; }; - C539CADB2D93229D0050C732 /* EvaluationOptions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EvaluationOptions.swift; sourceTree = ""; }; - C539CADE2D9475CD0050C732 /* PropertyValidator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PropertyValidator.swift; sourceTree = ""; }; - C539CAE12D9477770050C732 /* PropertyValidatorStub.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PropertyValidatorStub.swift; sourceTree = ""; }; - C539CAE32D947D2A0050C732 /* PropertyValidatorTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PropertyValidatorTest.swift; sourceTree = ""; }; C539CAB52D88C1F10050C732 /* RuleBasedSegment.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RuleBasedSegment.swift; sourceTree = ""; }; C539CABC2D88C7410050C732 /* PersistentRuleBasedSegmentsStorage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PersistentRuleBasedSegmentsStorage.swift; sourceTree = ""; }; C539CABD2D88C7410050C732 /* RuleBasedSegmentsStorage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RuleBasedSegmentsStorage.swift; sourceTree = ""; }; @@ -1978,6 +1976,10 @@ C539CAD22D8B5AD00050C732 /* RuleBasedSegmentChange.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RuleBasedSegmentChange.swift; sourceTree = ""; }; C539CAD32D8B5AD00050C732 /* TargetingRulesChange.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TargetingRulesChange.swift; sourceTree = ""; }; C539CAD82D8B5AF00050C732 /* ProcessedRuleBasedSegmentChange.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProcessedRuleBasedSegmentChange.swift; sourceTree = ""; }; + C539CADB2D93229D0050C732 /* EvaluationOptions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EvaluationOptions.swift; sourceTree = ""; }; + C539CADE2D9475CD0050C732 /* PropertyValidator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PropertyValidator.swift; sourceTree = ""; }; + C539CAE12D9477770050C732 /* PropertyValidatorStub.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PropertyValidatorStub.swift; sourceTree = ""; }; + C539CAE32D947D2A0050C732 /* PropertyValidatorTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PropertyValidatorTest.swift; sourceTree = ""; }; C53EDFA82DD295E5000DCDBC /* TargetingRulesChangeDecoderTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TargetingRulesChangeDecoderTest.swift; sourceTree = ""; }; C53EDFAA2DD29640000DCDBC /* TargetingRulesChangeDecoder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TargetingRulesChangeDecoder.swift; sourceTree = ""; }; C53EDFAD2DD299CB000DCDBC /* RestClientSplitChangesTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RestClientSplitChangesTest.swift; sourceTree = ""; }; @@ -2869,6 +2871,7 @@ isa = PBXGroup; children = ( C539CAE52D947D2A0050C732 /* Common */, + 5B91B8382DDE4A30000510F0 /* SplitDTOTests.swift */, C53EDFCC2DD4E10A000DCDBC /* SplitsSyncHelperWithProxyHandlerTests.swift */, C53EDFCA2DD3E257000DCDBC /* OutdatedSplitProxyHandlerTests.swift */, C53EDFC52DD3C53F000DCDBC /* SplitChangesErrorHandlerTests.swift */, @@ -4624,6 +4627,7 @@ 956A7E2E297ED7600080D53C /* SplitsEncoderTest.swift in Sources */, 59ED408824EABD4700EF7B09 /* SseClientMock.swift in Sources */, 5921ED422538EA39000D6C8B /* StreamingIntegrationHelper.swift in Sources */, + 5B91B8392DDE4A3B000510F0 /* SplitDTOTests.swift in Sources */, 599EA57F2266708D006CBA89 /* LocalhostYamlParserTest.swift in Sources */, 594F5F5A253CFC2D00A945B4 /* StreamingControlTest.swift in Sources */, 59ED408C24EAF86000EF7B09 /* TimersManagerMock.swift in Sources */, @@ -4856,7 +4860,6 @@ 956A7E21297590DF0080D53C /* Array+Chunked.swift in Sources */, 95B02CED28D0BDC20030EC8B /* MatcherGroup.swift in Sources */, 95CED0502B4DCDB1005E3C34 /* LocalhostClientManager.swift in Sources */, - C539CAB72D88C1F10050C732 /* RuleBasedSegment.swift in Sources */, 95B02CEE28D0BDC20030EC8B /* MatcherProtocol.swift in Sources */, C5E9674F2D37098000112DAC /* RolloutCacheManager.swift in Sources */, 95B02CEF28D0BDC20030EC8B /* MatcherType.swift in Sources */, diff --git a/Split/Common/Utils/SplitHelper.swift b/Split/Common/Utils/SplitHelper.swift index e3cf36ac5..21e0c96f6 100644 --- a/Split/Common/Utils/SplitHelper.swift +++ b/Split/Common/Utils/SplitHelper.swift @@ -21,7 +21,7 @@ class SplitHelper { split.trafficAllocation = 100 split.trafficAllocationSeed = 1 split.seed = 1 - split.isParsed = true + split.isCompletelyParsed = true return split } diff --git a/Split/Models/SplitModel/Split.swift b/Split/Models/SplitModel/Split.swift index f1e8422af..9c628e0f4 100644 --- a/Split/Models/SplitModel/Split.swift +++ b/Split/Models/SplitModel/Split.swift @@ -1,13 +1,11 @@ -// -// Split.swift -// Split -// // Created by Brian Sztamfater on 28/9/17. -// -// import Foundation +// The JSON is -partially- parsed at startup to improve SDK ready times (for example "conditions" are left out). +// After parsing just the strictly necesary stuff, it saves the complete JSON to finish parsing later. +// Once .sdkReady is fired, it concurrently finishes parsing the rest. + typealias Split = SplitDTO class SplitDTO: NSObject, SplitBase, Codable { @@ -25,11 +23,11 @@ class SplitDTO: NSObject, SplitBase, Codable { var configurations: [String: String]? var sets: Set? var impressionsDisabled: Bool? - var preTrequisites: [String: [String]]? + var prerequisites: [Prerequisite]? var json: String = "" - var isParsed = true + var isCompletelyParsed = true init(name: String, trafficType: String, status: Status, sets: Set?, json: String, killed: Bool = false, impressionsDisabled: Bool = false) { self.name = name @@ -38,7 +36,7 @@ class SplitDTO: NSObject, SplitBase, Codable { self.sets = sets self.json = json self.killed = killed - self.isParsed = false + self.isCompletelyParsed = false self.impressionsDisabled = impressionsDisabled } @@ -57,6 +55,11 @@ class SplitDTO: NSObject, SplitBase, Codable { case configurations case sets case impressionsDisabled - case preTrequisites + case prerequisites } } + +struct Prerequisite: Codable { + var n: String? + var ts: [String]? +} diff --git a/Split/Storage/Splits/SplitsStorage.swift b/Split/Storage/Splits/SplitsStorage.swift index 679487f6c..80c8b2a14 100644 --- a/Split/Storage/Splits/SplitsStorage.swift +++ b/Split/Storage/Splits/SplitsStorage.swift @@ -60,7 +60,7 @@ class DefaultSplitsStorage: SplitsStorage { guard let split = inMemorySplits.value(forKey: name.lowercased()) else { return nil } - if !split.isParsed { + if !split.isCompletelyParsed { if let parsed = try? Json.decodeFrom(json: split.json, to: Split.self) { if isUnsupportedMatcher(split: parsed) { parsed.conditions = [SplitHelper.createDefaultCondition()] diff --git a/SplitTests/Helpers/SplitTestHelper.swift b/SplitTests/Helpers/SplitTestHelper.swift index 9c1bb910d..bae4eb755 100644 --- a/SplitTests/Helpers/SplitTestHelper.swift +++ b/SplitTests/Helpers/SplitTestHelper.swift @@ -151,7 +151,7 @@ class SplitTestHelper { var splits = [Split]() for i in 0.. Split { let split = Split(name: name, trafficType: trafficType, status: .active, sets: nil, json: "") - split.isParsed = true + split.isCompletelyParsed = true return split } } diff --git a/SplitTests/Helpers/TestingHelper.swift b/SplitTests/Helpers/TestingHelper.swift index e33a45b64..c908d1cd7 100644 --- a/SplitTests/Helpers/TestingHelper.swift +++ b/SplitTests/Helpers/TestingHelper.swift @@ -108,7 +108,7 @@ struct TestingHelper { sets: Set? = nil) -> Split { let split = Split(name: name, trafficType: trafficType, status: status, sets: sets, json: "") - split.isParsed = true + split.isCompletelyParsed = true return split } @@ -116,7 +116,7 @@ struct TestingHelper { var splits = [Split]() for i in 0..<10 { let split = Split(name: "feat_\(i)", trafficType: "tt_\(i)", status: .active, sets: nil, json: "") - split.isParsed = true + split.isCompletelyParsed = true splits.append(split) } return splits diff --git a/SplitTests/Service/Splits/PeriodicSplitsSyncWorkerTest.swift b/SplitTests/Service/Splits/PeriodicSplitsSyncWorkerTest.swift index fbfc26ce0..cf9c6e2dc 100644 --- a/SplitTests/Service/Splits/PeriodicSplitsSyncWorkerTest.swift +++ b/SplitTests/Service/Splits/PeriodicSplitsSyncWorkerTest.swift @@ -84,7 +84,7 @@ class PeriodicSplitsSyncWorkerTest: XCTestCase { private func createSplit(name: String) -> Split { let split = SplitTestHelper.newSplit(name: name, trafficType: "tt") - split.isParsed = true + split.isCompletelyParsed = true return split } diff --git a/SplitTests/Service/Splits/SplitsSyncWorkerTest.swift b/SplitTests/Service/Splits/SplitsSyncWorkerTest.swift index 0b360c417..5dfcb25c8 100644 --- a/SplitTests/Service/Splits/SplitsSyncWorkerTest.swift +++ b/SplitTests/Service/Splits/SplitsSyncWorkerTest.swift @@ -166,7 +166,7 @@ class SplitsSyncWorkerTest: XCTestCase { private func createSplit(name: String) -> Split { let split = SplitTestHelper.newSplit(name: name, trafficType: "tt") - split.isParsed = true + split.isCompletelyParsed = true return split } diff --git a/SplitTests/SplitDTOTests.swift b/SplitTests/SplitDTOTests.swift new file mode 100644 index 000000000..b8130dcb6 --- /dev/null +++ b/SplitTests/SplitDTOTests.swift @@ -0,0 +1,114 @@ +// Created by Martin Cardozo on 21/05/2025. +// Copyright © 2025 Split. All rights reserved. + +import XCTest +@testable import Split + +class SplitDTOTests: XCTestCase { + + func testPrerequisitesField() { + // Happy case + var data = json.data(using: .utf8)! + var result = try? TargetingRulesChangeDecoder.decode(from: data) + XCTAssertEqual(result?.featureFlags.splits.first?.prerequisites?.first?.n, "flag1") + XCTAssertEqual(result?.featureFlags.splits.first?.prerequisites?.first?.ts?[1], "v1") + + // Empty prerequisites + data = jsonWithEmptyPrerequisites.data(using: .utf8)! + result = try? TargetingRulesChangeDecoder.decode(from: data) + XCTAssertEqual(result?.featureFlags.splits.first?.prerequisites?.first?.n, nil) + XCTAssertEqual(result?.ruleBasedSegments.segments.first?.name, "test_segment") + + // Errors in JSON + data = jsonWithMalformedPrerequisites.data(using: .utf8)! + XCTAssertThrowsError(try TargetingRulesChangeDecoder.decode(from: data)) + } + + let json = """ + { + "ff": { + "s": 1000, + "t": 1001, + "d": [ + { + "name": "test_split", + "trafficTypeName": "user", + "status": "active", + "prerequisites": [ + { "n": "flag1", "ts": ["on","v1"] }, + { "n": "flag2", "ts": ["off"] } + ], + } + ] + }, + "rbs": { + "s": 500, + "t": 501, + "d": [ + { + "name": "test_segment", + "trafficTypeName": "user", + "status": "active" + } + ] + } + } + """ + + let jsonWithEmptyPrerequisites = """ + { + "ff": { + "s": 1000, + "t": 1001, + "d": [ + { + "name": "test_split", + "trafficTypeName": "user", + "status": "active", + } + ] + }, + "rbs": { + "s": 500, + "t": 501, + "d": [ + { + "name": "test_segment", + "trafficTypeName": "user", + "status": "active" + } + ] + } + } + """ + + let jsonWithMalformedPrerequisites = """ + { + "ff": { + "s": 1000, + "t": 1001, + "d": [ + { + "name": "test_split", + "trafficTypeName": "user", + "status": "active", + "prerequisites": [ + somes, 13, https:// + ], + } + ] + }, + "rbs": { + "s": 500, + "t": 501, + "d": [ + { + "name": "test_segment", + "trafficTypeName": "user", + "status": "active" + } + ] + } + } + """ +} diff --git a/SplitTests/Storage/SplitsStorageTests.swift b/SplitTests/Storage/SplitsStorageTests.swift index 81e5be59a..e6473a043 100644 --- a/SplitTests/Storage/SplitsStorageTests.swift +++ b/SplitTests/Storage/SplitsStorageTests.swift @@ -287,7 +287,7 @@ class SplitsStorageTest: XCTestCase { var splits = [Split]() for i in 0.. Split { let split = SplitTestHelper.newSplit(name: name, trafficType: trafficType) split.status = status - split.isParsed = true + split.isCompletelyParsed = true return split } } diff --git a/SplitTests/ValidatorsTests/EventValidatorTests.swift b/SplitTests/ValidatorsTests/EventValidatorTests.swift index a58af44ee..1b13bcde8 100644 --- a/SplitTests/ValidatorsTests/EventValidatorTests.swift +++ b/SplitTests/ValidatorsTests/EventValidatorTests.swift @@ -178,7 +178,7 @@ class EventValidatorTests: XCTestCase { private func newSplit(trafficType: String, status: Status = .active) -> Split { let split = SplitTestHelper.newSplit(name: UUID().uuidString, trafficType: trafficType) split.status = status - split.isParsed = true + split.isCompletelyParsed = true return split } diff --git a/SplitTests/ValidatorsTests/SplitValidatorTests.swift b/SplitTests/ValidatorsTests/SplitValidatorTests.swift index a2b7d8d8b..8222cbd00 100644 --- a/SplitTests/ValidatorsTests/SplitValidatorTests.swift +++ b/SplitTests/ValidatorsTests/SplitValidatorTests.swift @@ -80,7 +80,7 @@ class SplitValidatorTests: XCTestCase { func createSplit(name: String) -> Split { let split = SplitTestHelper.newSplit(name: name, trafficType: "") - split.isParsed = true + split.isCompletelyParsed = true return split } From 65e84e72d51882b090a514fd68ba05563d40ad71 Mon Sep 17 00:00:00 2001 From: Martin Cardozo Date: Wed, 21 May 2025 17:12:18 -0300 Subject: [PATCH 03/46] Clean up --- SplitTests/Resources/splitchanges_1.json | 23 ----------------------- 1 file changed, 23 deletions(-) diff --git a/SplitTests/Resources/splitchanges_1.json b/SplitTests/Resources/splitchanges_1.json index df0cd2209..9312ba6f0 100644 --- a/SplitTests/Resources/splitchanges_1.json +++ b/SplitTests/Resources/splitchanges_1.json @@ -3,7 +3,6 @@ { "trafficTypeName":"account", "name":"FACUNDO_TEST", - "preQER":[], "trafficAllocation":59, "trafficAllocationSeed":-2108186082, "seed":-1947050785, @@ -231,7 +230,6 @@ { "trafficTypeName":"account", "name":"testing222", - "preQER":[], "trafficAllocation":100, "trafficAllocationSeed":-397360967, "seed":1058132210, @@ -577,7 +575,6 @@ { "trafficTypeName":"account", "name":"test_string_without_attr", - "preQER":[], "trafficAllocation":100, "trafficAllocationSeed":-782597068, "seed":-1682478887, @@ -647,7 +644,6 @@ { "trafficTypeName":"user", "name":"OldTest", - "preQER":[], "trafficAllocation":100, "trafficAllocationSeed":217539832, "seed":52164426, @@ -831,7 +827,6 @@ { "trafficTypeName":"account", "name":"Test_Save_1", - "preQER":[], "trafficAllocation":100, "trafficAllocationSeed":-257595325, "seed":-665945237, @@ -1029,7 +1024,6 @@ { "trafficTypeName":"user", "name":"benchmark_jw_1", - "preQER":[], "trafficAllocation":100, "trafficAllocationSeed":987354894, "seed":1292874260, @@ -1116,7 +1110,6 @@ { "trafficTypeName":"user", "name":"nico_tests", - "preQER":[], "trafficAllocation":100, "trafficAllocationSeed":1409699192, "seed":-1997241870, @@ -1198,7 +1191,6 @@ { "trafficTypeName":"account", "name":"testo2222", - "preQER":[], "trafficAllocation":100, "trafficAllocationSeed":1164474086, "seed":1270508512, @@ -1440,7 +1432,6 @@ { "trafficTypeName":"user", "name":"Tagging", - "preQER":[], "trafficAllocation":100, "trafficAllocationSeed":1910132597, "seed":-311493896, @@ -1588,7 +1579,6 @@ { "trafficTypeName":"account", "name":"testo23", - "preQER":[], "trafficAllocation":100, "trafficAllocationSeed":-689658216, "seed":1711156051, @@ -1803,7 +1793,6 @@ { "trafficTypeName":"account", "name":"testo22", - "preQER":[], "trafficAllocation":100, "trafficAllocationSeed":1223277820, "seed":-1152948537, @@ -1900,7 +1889,6 @@ { "trafficTypeName":"account", "name":"test_dep_2", - "preQER":[], "trafficAllocation":100, "trafficAllocationSeed":-806171485, "seed":922684950, @@ -1993,7 +1981,6 @@ { "trafficTypeName":"account", "name":"Definition_As_Of_Clickable_UI", - "preQER":[], "trafficAllocation":100, "trafficAllocationSeed":-198035199, "seed":-151947071, @@ -2074,7 +2061,6 @@ { "trafficTypeName":"account", "name":"Identify_UI", - "preQER":[], "trafficAllocation":100, "trafficAllocationSeed":-139516103, "seed":1543172523, @@ -2123,7 +2109,6 @@ { "trafficTypeName":"account", "name":"test_definition_as_of", - "preQER":[], "trafficAllocation":100, "trafficAllocationSeed":1025823325, "seed":-554248124, @@ -2172,7 +2157,6 @@ { "trafficTypeName":"user", "name":"Test-jw-go", - "preQER":[], "trafficAllocation":100, "trafficAllocationSeed":768122971, "seed":1539205707, @@ -2251,7 +2235,6 @@ { "trafficTypeName":"user", "name":"benchmark_jw_7", - "preQER":[], "trafficAllocation":100, "trafficAllocationSeed":-1340337178, "seed":-1091938685, @@ -2300,7 +2283,6 @@ { "trafficTypeName":"user", "name":"benchmark_jw_6", - "preQER":[], "trafficAllocation":100, "trafficAllocationSeed":-1202331834, "seed":-48445256, @@ -2397,7 +2379,6 @@ { "trafficTypeName":"user", "name":"benchmark_jw_4", - "preQER":[], "trafficAllocation":100, "trafficAllocationSeed":1066635158, "seed":-850704283, @@ -2446,7 +2427,6 @@ { "trafficTypeName":"user", "name":"benchmark_jw_3", - "preQER":[], "trafficAllocation":100, "trafficAllocationSeed":1252392550, "seed":971538037, @@ -2495,7 +2475,6 @@ { "trafficTypeName":"user", "name":"benchmark_jw_2", - "preQER":[], "trafficAllocation":100, "trafficAllocationSeed":-285565213, "seed":-1992295819, @@ -2671,7 +2650,6 @@ { "trafficTypeName":"account", "name":"TEST_SETS_2", - "preQER":[], "trafficAllocation":59, "trafficAllocationSeed":-2108186082, "seed":-1947050785, @@ -2791,7 +2769,6 @@ { "trafficTypeName":"account", "name":"TEST_SETS_3", - "preQER":[], "trafficAllocation":59, "trafficAllocationSeed":-2108186082, "seed":-1947050785, From d6035ef9bae344d753970c01b56ff1e9f7f35c0b Mon Sep 17 00:00:00 2001 From: Gaston Thea Date: Thu, 22 May 2025 13:31:03 -0300 Subject: [PATCH 04/46] Fix? --- Split.xcodeproj/project.pbxproj | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/Split.xcodeproj/project.pbxproj b/Split.xcodeproj/project.pbxproj index c509a5af7..9a3d51dcd 100644 --- a/Split.xcodeproj/project.pbxproj +++ b/Split.xcodeproj/project.pbxproj @@ -1074,6 +1074,7 @@ 95F7BC292C46A4C800C5F2E4 /* SecurityHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 95F7BC282C46A4C800C5F2E4 /* SecurityHelper.swift */; }; 95F8F06828170473009E09B1 /* multi_client_test.json in Resources */ = {isa = PBXBuildFile; fileRef = 95F8F06728170473009E09B1 /* multi_client_test.json */; }; C514F6B62BEA6F1F00C8DF78 /* HttpParameters.swift in Sources */ = {isa = PBXBuildFile; fileRef = C514F6B52BEA6F1F00C8DF78 /* HttpParameters.swift */; }; + C526DE4C2D9B09EB0051DAB8 /* ImpressionsPropertiesE2ETest.swift in Sources */ = {isa = PBXBuildFile; fileRef = C526DE4B2D9B09EB0051DAB8 /* ImpressionsPropertiesE2ETest.swift */; }; C539CAB62D88C1F10050C732 /* RuleBasedSegment.swift in Sources */ = {isa = PBXBuildFile; fileRef = C539CAB52D88C1F10050C732 /* RuleBasedSegment.swift */; }; C539CABE2D88C7410050C732 /* PersistentRuleBasedSegmentsStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = C539CABC2D88C7410050C732 /* PersistentRuleBasedSegmentsStorage.swift */; }; C539CABF2D88C7410050C732 /* RuleBasedSegmentsStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = C539CABD2D88C7410050C732 /* RuleBasedSegmentsStorage.swift */; }; @@ -1112,13 +1113,6 @@ C53EDFCD2DD4E10A000DCDBC /* SplitsSyncHelperWithProxyHandlerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C53EDFCC2DD4E10A000DCDBC /* SplitsSyncHelperWithProxyHandlerTests.swift */; }; C53F3C472DCB956900655753 /* SplitsSyncHelperTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = C53F3C462DCB956900655753 /* SplitsSyncHelperTest.swift */; }; C53F3C4F2DCD112400655753 /* RuleBasedSegmentChangeProcessorStub.swift in Sources */ = {isa = PBXBuildFile; fileRef = C53F3C4E2DCD110700655753 /* RuleBasedSegmentChangeProcessorStub.swift */; }; - C526DE4C2D9B09EB0051DAB8 /* ImpressionsPropertiesE2ETest.swift in Sources */ = {isa = PBXBuildFile; fileRef = C526DE4B2D9B09EB0051DAB8 /* ImpressionsPropertiesE2ETest.swift */; }; - C539CADC2D93229D0050C732 /* EvaluationOptions.swift in Sources */ = {isa = PBXBuildFile; fileRef = C539CADB2D93229D0050C732 /* EvaluationOptions.swift */; }; - C539CADD2D93229D0050C732 /* EvaluationOptions.swift in Sources */ = {isa = PBXBuildFile; fileRef = C539CADB2D93229D0050C732 /* EvaluationOptions.swift */; }; - C539CADF2D9475CD0050C732 /* PropertyValidator.swift in Sources */ = {isa = PBXBuildFile; fileRef = C539CADE2D9475CD0050C732 /* PropertyValidator.swift */; }; - C539CAE02D9475CD0050C732 /* PropertyValidator.swift in Sources */ = {isa = PBXBuildFile; fileRef = C539CADE2D9475CD0050C732 /* PropertyValidator.swift */; }; - C539CAE22D9477770050C732 /* PropertyValidatorStub.swift in Sources */ = {isa = PBXBuildFile; fileRef = C539CAE12D9477770050C732 /* PropertyValidatorStub.swift */; }; - C539CAE62D947D2A0050C732 /* PropertyValidatorTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = C539CAE32D947D2A0050C732 /* PropertyValidatorTest.swift */; }; C58F33732BDAC4AC00D66549 /* split_unsupported_matcher.json in Resources */ = {isa = PBXBuildFile; fileRef = C58F33722BDAC4AC00D66549 /* split_unsupported_matcher.json */; }; C5977BFF2BF27375003E293A /* Semver.swift in Sources */ = {isa = PBXBuildFile; fileRef = C5977BFE2BF27375003E293A /* Semver.swift */; }; C5977C012BF27390003E293A /* SemverTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = C5977C002BF27390003E293A /* SemverTest.swift */; }; @@ -1165,6 +1159,7 @@ C5A7D5532DD672CF0081D190 /* RuleBasedSegmentsIntegrationTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = C5A7D5522DD672CF0081D190 /* RuleBasedSegmentsIntegrationTest.swift */; }; C5A7D5572DDBD4380081D190 /* split_changes_rbs.json in Resources */ = {isa = PBXBuildFile; fileRef = C5A7D5562DDBD4280081D190 /* split_changes_rbs.json */; }; C5A7D5592DDBD7A30081D190 /* OutdatedProxyIntegrationTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = C5A7D5582DDBD7A30081D190 /* OutdatedProxyIntegrationTest.swift */; }; + C5A7D5612DDF88BA0081D190 /* RuleBasedSegment.swift in Sources */ = {isa = PBXBuildFile; fileRef = C539CAB52D88C1F10050C732 /* RuleBasedSegment.swift */; }; C5BD1E4C2D11A993008EF198 /* DecoratedImpression.swift in Sources */ = {isa = PBXBuildFile; fileRef = C5BD1E4B2D11A98E008EF198 /* DecoratedImpression.swift */; }; C5BD1E4D2D11A993008EF198 /* DecoratedImpression.swift in Sources */ = {isa = PBXBuildFile; fileRef = C5BD1E4B2D11A98E008EF198 /* DecoratedImpression.swift */; }; C5BD1E4F2D130EAF008EF198 /* ImpressionsToggleTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = C5BD1E4E2D130EA7008EF198 /* ImpressionsToggleTest.swift */; }; @@ -1971,6 +1966,7 @@ 95F7BC282C46A4C800C5F2E4 /* SecurityHelper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SecurityHelper.swift; sourceTree = ""; }; 95F8F06728170473009E09B1 /* multi_client_test.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = multi_client_test.json; sourceTree = ""; }; C514F6B52BEA6F1F00C8DF78 /* HttpParameters.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HttpParameters.swift; sourceTree = ""; }; + C526DE4B2D9B09EB0051DAB8 /* ImpressionsPropertiesE2ETest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImpressionsPropertiesE2ETest.swift; sourceTree = ""; }; C539CAB52D88C1F10050C732 /* RuleBasedSegment.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RuleBasedSegment.swift; sourceTree = ""; }; C539CABC2D88C7410050C732 /* PersistentRuleBasedSegmentsStorage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PersistentRuleBasedSegmentsStorage.swift; sourceTree = ""; }; C539CABD2D88C7410050C732 /* RuleBasedSegmentsStorage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RuleBasedSegmentsStorage.swift; sourceTree = ""; }; @@ -1999,11 +1995,6 @@ C53EDFCC2DD4E10A000DCDBC /* SplitsSyncHelperWithProxyHandlerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SplitsSyncHelperWithProxyHandlerTests.swift; sourceTree = ""; }; C53F3C462DCB956900655753 /* SplitsSyncHelperTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SplitsSyncHelperTest.swift; sourceTree = ""; }; C53F3C4E2DCD110700655753 /* RuleBasedSegmentChangeProcessorStub.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RuleBasedSegmentChangeProcessorStub.swift; sourceTree = ""; }; - C526DE4B2D9B09EB0051DAB8 /* ImpressionsPropertiesE2ETest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImpressionsPropertiesE2ETest.swift; sourceTree = ""; }; - C539CADB2D93229D0050C732 /* EvaluationOptions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EvaluationOptions.swift; sourceTree = ""; }; - C539CADE2D9475CD0050C732 /* PropertyValidator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PropertyValidator.swift; sourceTree = ""; }; - C539CAE12D9477770050C732 /* PropertyValidatorStub.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PropertyValidatorStub.swift; sourceTree = ""; }; - C539CAE32D947D2A0050C732 /* PropertyValidatorTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PropertyValidatorTest.swift; sourceTree = ""; }; C58F33722BDAC4AC00D66549 /* split_unsupported_matcher.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = split_unsupported_matcher.json; sourceTree = ""; }; C5977BFE2BF27375003E293A /* Semver.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Semver.swift; sourceTree = ""; }; C5977C002BF27390003E293A /* SemverTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SemverTest.swift; sourceTree = ""; }; @@ -4880,6 +4871,7 @@ 95B02CEB28D0BDC20030EC8B /* Matcher.swift in Sources */, 95B02CEC28D0BDC20030EC8B /* MatcherCombiner.swift in Sources */, 956A7E21297590DF0080D53C /* Array+Chunked.swift in Sources */, + C5A7D5612DDF88BA0081D190 /* RuleBasedSegment.swift in Sources */, 95B02CED28D0BDC20030EC8B /* MatcherGroup.swift in Sources */, 95CED0502B4DCDB1005E3C34 /* LocalhostClientManager.swift in Sources */, 95B02CEE28D0BDC20030EC8B /* MatcherProtocol.swift in Sources */, From ebb5827c7db1183393f43e233412fbe0df5726b6 Mon Sep 17 00:00:00 2001 From: Martin Cardozo Date: Thu, 22 May 2025 13:33:04 -0300 Subject: [PATCH 05/46] SplitView fields --- Split.xcodeproj/project.pbxproj | 2 ++ Split/Api/DefaultSplitManager.swift | 1 + Split/Api/SplitView.swift | 18 +----------------- .../SplitView+StringConvertible.swift | 11 +++++++++++ Split/Models/SplitModel/Split.swift | 2 +- 5 files changed, 16 insertions(+), 18 deletions(-) diff --git a/Split.xcodeproj/project.pbxproj b/Split.xcodeproj/project.pbxproj index 4e1c1d741..2ece9d91f 100644 --- a/Split.xcodeproj/project.pbxproj +++ b/Split.xcodeproj/project.pbxproj @@ -354,6 +354,7 @@ 59FB7C3C2203795F00ECC96A /* LocalhostSplitsParser.swift in Sources */ = {isa = PBXBuildFile; fileRef = 59FB7C3B2203795F00ECC96A /* LocalhostSplitsParser.swift */; }; 59FB7C3E22037B9400ECC96A /* SpaceDelimitedLocalhostSplitsParser.swift in Sources */ = {isa = PBXBuildFile; fileRef = 59FB7C3D22037B9400ECC96A /* SpaceDelimitedLocalhostSplitsParser.swift */; }; 5B91B8392DDE4A3B000510F0 /* SplitDTOTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B91B8382DDE4A30000510F0 /* SplitDTOTests.swift */; }; + 5BF52DEF2DDF884D00FEDAFE /* RuleBasedSegment.swift in Sources */ = {isa = PBXBuildFile; fileRef = C539CAB52D88C1F10050C732 /* RuleBasedSegment.swift */; }; 9500D9922C56F9BA00383593 /* HostDomainFilterTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9500D9912C56F9BA00383593 /* HostDomainFilterTests.swift */; }; 9500D9A92C59297400383593 /* HostDomainFilter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9500D9A82C59297400383593 /* HostDomainFilter.swift */; }; 9500D9AA2C59382000383593 /* HostDomainFilter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9500D9A82C59297400383593 /* HostDomainFilter.swift */; }; @@ -4858,6 +4859,7 @@ 95B02CEB28D0BDC20030EC8B /* Matcher.swift in Sources */, 95B02CEC28D0BDC20030EC8B /* MatcherCombiner.swift in Sources */, 956A7E21297590DF0080D53C /* Array+Chunked.swift in Sources */, + 5BF52DEF2DDF884D00FEDAFE /* RuleBasedSegment.swift in Sources */, 95B02CED28D0BDC20030EC8B /* MatcherGroup.swift in Sources */, 95CED0502B4DCDB1005E3C34 /* LocalhostClientManager.swift in Sources */, 95B02CEE28D0BDC20030EC8B /* MatcherProtocol.swift in Sources */, diff --git a/Split/Api/DefaultSplitManager.swift b/Split/Api/DefaultSplitManager.swift index 98e037abc..fb5433404 100644 --- a/Split/Api/DefaultSplitManager.swift +++ b/Split/Api/DefaultSplitManager.swift @@ -41,6 +41,7 @@ import Foundation splitView.trafficType = split.trafficTypeName splitView.defaultTreatment = split.defaultTreatment splitView.killed = split.killed + splitView.prerequisites = split.prerequisites ?? [] splitView.sets = Array(split.sets ?? []) splitView.configs = split.configurations ?? [String: String]() splitView.impressionsDisabled = split.impressionsDisabled ?? false diff --git a/Split/Api/SplitView.swift b/Split/Api/SplitView.swift index 4b24f6e2e..0753f9b61 100644 --- a/Split/Api/SplitView.swift +++ b/Split/Api/SplitView.swift @@ -25,23 +25,7 @@ public class SplitView: NSObject, Codable { return changeNumber as NSNumber? } @objc public var configs: [String: String]? + @objc public var prerequisites: [Prerequisite]? @objc public var impressionsDisabled: Bool = false } - - -var name: String? -var seed: Int? -var status: Status? -var killed: Bool? -var defaultTreatment: String? -var conditions: [Condition]? -var trafficTypeName: String? -var changeNumber: Int64? -var trafficAllocation: Int? -var trafficAllocationSeed: Int? -var algo: Int? -var configurations: [String: String]? -var sets: Set? -var impressionsDisabled: Bool? -var preTrequisites: [String: [String]]? diff --git a/Split/Common/Extensions/SplitView+StringConvertible.swift b/Split/Common/Extensions/SplitView+StringConvertible.swift index 2c1e1baaf..c988bec88 100644 --- a/Split/Common/Extensions/SplitView+StringConvertible.swift +++ b/Split/Common/Extensions/SplitView+StringConvertible.swift @@ -31,6 +31,17 @@ extension SplitView { } else { output+="treatments = nil\n" } + if let prerequisites = prerequisites { + output+="prerequisites = [\n" + prerequisites.forEach { prerequisite in + output+=""" + \(prerequisite.n): {\(prerequisite.ts?.joined(separator: ","))}\n + """ + } + output+="]\n" + } else { + output+="prerequisites = nil\n" + } if let sets = sets { output+="sets = [\(sets.joined(separator: ","))]\n" } else { diff --git a/Split/Models/SplitModel/Split.swift b/Split/Models/SplitModel/Split.swift index 9c628e0f4..b4d6c9b3f 100644 --- a/Split/Models/SplitModel/Split.swift +++ b/Split/Models/SplitModel/Split.swift @@ -59,7 +59,7 @@ class SplitDTO: NSObject, SplitBase, Codable { } } -struct Prerequisite: Codable { +@objc public class Prerequisite: NSObject, Codable { var n: String? var ts: [String]? } From e56b759b20b5e420e506c6162fed64010bbbce11 Mon Sep 17 00:00:00 2001 From: Martin Cardozo Date: Thu, 22 May 2025 13:50:22 -0300 Subject: [PATCH 06/46] Cleanup --- SplitTests/Helpers/SplitTestHelper.swift | 1 - SplitTests/Helpers/TestDataHelper.swift | 4 ---- SplitTests/Resources/splits.json | 1 - 3 files changed, 6 deletions(-) diff --git a/SplitTests/Helpers/SplitTestHelper.swift b/SplitTests/Helpers/SplitTestHelper.swift index bae4eb755..e37ca6c11 100644 --- a/SplitTests/Helpers/SplitTestHelper.swift +++ b/SplitTests/Helpers/SplitTestHelper.swift @@ -27,7 +27,6 @@ class SplitTestHelper { { "trafficTypeName":"account", "name":"FACUNDO_TEST", - "prereKILSITS":0, "trafficAllocation":59, "trafficAllocationSeed":-2108186082, "seed":-1947050785, diff --git a/SplitTests/Helpers/TestDataHelper.swift b/SplitTests/Helpers/TestDataHelper.swift index 74196a9e2..417381c21 100644 --- a/SplitTests/Helpers/TestDataHelper.swift +++ b/SplitTests/Helpers/TestDataHelper.swift @@ -54,10 +54,6 @@ class TestDataHelper { { "trafficTypeName":"account", "name":"FACUNDO_TEST", - "prereQquisites": [ - { "n": "flag1", "ts": ["on","v1"] }, - { "n": "flag2", "ts": ["off"] } - ], "trafficAllocation":59, "trafficAllocationSeed":-2108186082, "seed":-1947050785, diff --git a/SplitTests/Resources/splits.json b/SplitTests/Resources/splits.json index e2a608b48..113ecb633 100644 --- a/SplitTests/Resources/splits.json +++ b/SplitTests/Resources/splits.json @@ -1,7 +1,6 @@ [{ "trafficTypeName": "custom", "name": "SAMPLE_FEATURE0", - "ASDSAL":[], "trafficAllocation": 100, "trafficAllocationSeed": 1595297106, "seed": -1332540447, From 6973f4f5116bd900f5381873a044e4dbdc274c6a Mon Sep 17 00:00:00 2001 From: Martin Cardozo Date: Thu, 22 May 2025 16:45:05 -0300 Subject: [PATCH 07/46] Tests added --- .../Integration/streaming/WatchOS.xctestplan | 1854 +++++++++-------- SplitTests/Resources/splits.json | 4 + SplitTests/SplitManagerTest.swift | 9 +- 3 files changed, 935 insertions(+), 932 deletions(-) diff --git a/SplitTests/Integration/streaming/WatchOS.xctestplan b/SplitTests/Integration/streaming/WatchOS.xctestplan index 0e2c8b227..df42bbfff 100644 --- a/SplitTests/Integration/streaming/WatchOS.xctestplan +++ b/SplitTests/Integration/streaming/WatchOS.xctestplan @@ -1,1120 +1,1122 @@ { - "configurations": [ + "configurations" : [ { - "id": "1582CBF6-2C3F-45DB-A818-CE12E1269C80", - "name": "Configuration 1", - "options": {} + "id" : "1582CBF6-2C3F-45DB-A818-CE12E1269C80", + "name" : "Configuration 1", + "options" : { + + } } ], - "defaultOptions": { - "codeCoverage": false + "defaultOptions" : { + "codeCoverage" : false }, - "testTargets": [ + "testTargets" : [ { - "target": { - "containerPath": "container:Split.xcodeproj", - "identifier": "592C6AA4211B6C99002D120C", - "name": "SplitTests" - }, - "skippedTests": [ + "skippedTests" : [ "AnyValueValidatorTests", - "AnyValueValidatorTests/testInvalidListValues()", - "AnyValueValidatorTests/testInvalidPrimitiveValues()", - "AnyValueValidatorTests/testValidListValues()", - "AnyValueValidatorTests/testValidPrimitiveValues()", + "AnyValueValidatorTests\/testInvalidListValues()", + "AnyValueValidatorTests\/testInvalidPrimitiveValues()", + "AnyValueValidatorTests\/testValidListValues()", + "AnyValueValidatorTests\/testValidPrimitiveValues()", "ApiKeyValidatorTests", - "ApiKeyValidatorTests/testEmptyKey()", - "ApiKeyValidatorTests/testNull()", - "ApiKeyValidatorTests/testValid()", + "ApiKeyValidatorTests\/testEmptyKey()", + "ApiKeyValidatorTests\/testNull()", + "ApiKeyValidatorTests\/testValid()", "AttributesDaoTest", - "AttributesDaoTest/testDataIsEncryptedInDb()", - "AttributesDaoTest/testGetInvalidKeyAes128Cbc()", - "AttributesDaoTest/testGetInvalidKeyPlainText()", - "AttributesDaoTest/testRemoveAllAes128Cbc()", - "AttributesDaoTest/testRemoveAllPlainText()", - "AttributesDaoTest/testUpdateGetAes128Cbc()", - "AttributesDaoTest/testUpdateGetPlainText()", + "AttributesDaoTest\/testDataIsEncryptedInDb()", + "AttributesDaoTest\/testGetInvalidKeyAes128Cbc()", + "AttributesDaoTest\/testGetInvalidKeyPlainText()", + "AttributesDaoTest\/testRemoveAllAes128Cbc()", + "AttributesDaoTest\/testRemoveAllPlainText()", + "AttributesDaoTest\/testUpdateGetAes128Cbc()", + "AttributesDaoTest\/testUpdateGetPlainText()", "AttributesEvaluationTest", - "AttributesEvaluationTest/testAttributesPersistentedCorrectly()", - "AttributesEvaluationTest/testEvaluationPrecedence()", - "AttributesEvaluationTest/testPersistentEvalNoAttributesSeveralOperations()", + "AttributesEvaluationTest\/testAttributesPersistentedCorrectly()", + "AttributesEvaluationTest\/testEvaluationPrecedence()", + "AttributesEvaluationTest\/testPersistentEvalNoAttributesSeveralOperations()", "AttributesStorageTests", - "AttributesStorageTests/testClear()", - "AttributesStorageTests/testNoLoaded()", - "AttributesStorageTests/testRemove()", - "AttributesStorageTests/testUpdateAttributes()", - "AttributesStorageTests/testUpdateEmptyAttributes()", + "AttributesStorageTests\/testClear()", + "AttributesStorageTests\/testNoLoaded()", + "AttributesStorageTests\/testRemove()", + "AttributesStorageTests\/testUpdateAttributes()", + "AttributesStorageTests\/testUpdateEmptyAttributes()", "Base64UtilsTest", - "Base64UtilsTest/testBasicUrlEncoded()", - "Base64UtilsTest/testEmpty()", - "Base64UtilsTest/testNil()", - "Base64UtilsTest/testRealToken()", + "Base64UtilsTest\/testBasicUrlEncoded()", + "Base64UtilsTest\/testEmpty()", + "Base64UtilsTest\/testNil()", + "Base64UtilsTest\/testRealToken()", "BetweenSemverMatcherTest", - "BetweenSemverMatcherTest/testGeneralMatches()", - "BetweenSemverMatcherTest/testMatchShouldReturnFalseWhenGreater()", - "BetweenSemverMatcherTest/testMatchShouldReturnFalseWhenLess()", - "BetweenSemverMatcherTest/testMatchShouldReturnTrueWhenBetween()", - "BetweenSemverMatcherTest/testMatchWithMetadataShouldReturnFalseWhenGreater()", - "BetweenSemverMatcherTest/testMatchWithMetadataShouldReturnFalseWhenLess()", - "BetweenSemverMatcherTest/testMatchWithMetadataShouldReturnTrueWhenBetween()", - "BetweenSemverMatcherTest/testMatchWithNullEndTargetShouldReturnFalse()", - "BetweenSemverMatcherTest/testMatchWithNullKeyShouldReturnFalse()", - "BetweenSemverMatcherTest/testMatchWithNullStartTargetShouldReturnFalse()", - "BetweenSemverMatcherTest/testMatchWithPreReleaseShouldReturnFalseWhenGreater()", - "BetweenSemverMatcherTest/testMatchWithPreReleaseShouldReturnFalseWhenLess()", - "BetweenSemverMatcherTest/testMatchWithPreReleaseShouldReturnTrueWhenBetween()", + "BetweenSemverMatcherTest\/testGeneralMatches()", + "BetweenSemverMatcherTest\/testMatchShouldReturnFalseWhenGreater()", + "BetweenSemverMatcherTest\/testMatchShouldReturnFalseWhenLess()", + "BetweenSemverMatcherTest\/testMatchShouldReturnTrueWhenBetween()", + "BetweenSemverMatcherTest\/testMatchWithMetadataShouldReturnFalseWhenGreater()", + "BetweenSemverMatcherTest\/testMatchWithMetadataShouldReturnFalseWhenLess()", + "BetweenSemverMatcherTest\/testMatchWithMetadataShouldReturnTrueWhenBetween()", + "BetweenSemverMatcherTest\/testMatchWithNullEndTargetShouldReturnFalse()", + "BetweenSemverMatcherTest\/testMatchWithNullKeyShouldReturnFalse()", + "BetweenSemverMatcherTest\/testMatchWithNullStartTargetShouldReturnFalse()", + "BetweenSemverMatcherTest\/testMatchWithPreReleaseShouldReturnFalseWhenGreater()", + "BetweenSemverMatcherTest\/testMatchWithPreReleaseShouldReturnFalseWhenLess()", + "BetweenSemverMatcherTest\/testMatchWithPreReleaseShouldReturnTrueWhenBetween()", "BlockingQueueTest", - "BlockingQueueTest/testAddAndTake()", - "BlockingQueueTest/testInterrupt()", + "BlockingQueueTest\/testAddAndTake()", + "BlockingQueueTest\/testInterrupt()", "BucketSplitTest", - "BucketSplitTest/testMultiClientBuckets()", + "BucketSplitTest\/testMultiClientBuckets()", "ByKeyAttributesStorageTests", - "ByKeyAttributesStorageTests/testClear()", - "ByKeyAttributesStorageTests/testGetAttributesAfterLoad()", - "ByKeyAttributesStorageTests/testNoLoaded()", - "ByKeyAttributesStorageTests/testRemove()", - "ByKeyAttributesStorageTests/testUpdateAttributes()", - "ByKeyAttributesStorageTests/testUpdateEmptyAttributes()", + "ByKeyAttributesStorageTests\/testClear()", + "ByKeyAttributesStorageTests\/testGetAttributesAfterLoad()", + "ByKeyAttributesStorageTests\/testNoLoaded()", + "ByKeyAttributesStorageTests\/testRemove()", + "ByKeyAttributesStorageTests\/testUpdateAttributes()", + "ByKeyAttributesStorageTests\/testUpdateEmptyAttributes()", "ByKeyFacadeTest", - "ByKeyFacadeTest/testAppendRemove()", - "ByKeyFacadeTest/testDestroy()", - "ByKeyFacadeTest/testForceSync()", - "ByKeyFacadeTest/testLoadDataFromCache()", - "ByKeyFacadeTest/testNoPeriodicSync()", - "ByKeyFacadeTest/testPeriodicStartPauseResumeStop()", - "ByKeyFacadeTest/testPeriodicStartStop()", - "ByKeyFacadeTest/testStartSyncForKey()", - "ByKeyFacadeTest/testStartSyncForKeyPolling()", - "ByKeyFacadeTest/testSynchronize()", + "ByKeyFacadeTest\/testAppendRemove()", + "ByKeyFacadeTest\/testDestroy()", + "ByKeyFacadeTest\/testForceSync()", + "ByKeyFacadeTest\/testLoadDataFromCache()", + "ByKeyFacadeTest\/testNoPeriodicSync()", + "ByKeyFacadeTest\/testPeriodicStartPauseResumeStop()", + "ByKeyFacadeTest\/testPeriodicStartStop()", + "ByKeyFacadeTest\/testStartSyncForKey()", + "ByKeyFacadeTest\/testStartSyncForKeyPolling()", + "ByKeyFacadeTest\/testSynchronize()", "ByKeyMySegmentsStorageTests", - "ByKeyMySegmentsStorageTests/testGetMySegmentsAfterLoad()", - "ByKeyMySegmentsStorageTests/testNoLoaded()", - "ByKeyMySegmentsStorageTests/testUpdateEmptySegments()", - "ByKeyMySegmentsStorageTests/testUpdateSegments()", + "ByKeyMySegmentsStorageTests\/testGetMySegmentsAfterLoad()", + "ByKeyMySegmentsStorageTests\/testNoLoaded()", + "ByKeyMySegmentsStorageTests\/testUpdateEmptySegments()", + "ByKeyMySegmentsStorageTests\/testUpdateSegments()", "CdnByPassTest", - "CdnByPassTest/testInit()", - "CdnByPassTest/testInitWithoutSpec()", + "CdnByPassTest\/testInit()", + "CdnByPassTest\/testInitWithoutSpec()", "CertificatePinningConfigTests", - "CertificatePinningConfigTests/testAddCertificateAndHashes()", - "CertificatePinningConfigTests/testAddWrongCertificate()", - "CertificatePinningConfigTests/testAddWrongStringHash()", - "CertificatePinningConfigTests/testAddWrongStringHashFormatHash()", - "CertificatePinningConfigTests/testAddWrongStringHashType()", + "CertificatePinningConfigTests\/testAddCertificateAndHashes()", + "CertificatePinningConfigTests\/testAddWrongCertificate()", + "CertificatePinningConfigTests\/testAddWrongStringHash()", + "CertificatePinningConfigTests\/testAddWrongStringHashFormatHash()", + "CertificatePinningConfigTests\/testAddWrongStringHashType()", "CipherTest", - "CipherTest/testBasicEncryptDecrypt()", - "CipherTest/testEventEncryptDecrypt()", - "CipherTest/testImpressionEncryptDecrypt()", - "CipherTest/testJsonSplitEncryptDecrypt()", - "CipherTest/testVeryLongTextEncryptDecrypt()", + "CipherTest\/testBasicEncryptDecrypt()", + "CipherTest\/testEventEncryptDecrypt()", + "CipherTest\/testImpressionEncryptDecrypt()", + "CipherTest\/testJsonSplitEncryptDecrypt()", + "CipherTest\/testVeryLongTextEncryptDecrypt()", "ComputeProcessTest", - "ComputeProcessTest/testOneLessThanMinProcess()", - "ComputeProcessTest/testOneProcessEqualsMin()", - "ComputeProcessTest/testTwoProcess()", - "ComputeProcessTest/testTwoProcessEdge()", + "ComputeProcessTest\/testOneLessThanMinProcess()", + "ComputeProcessTest\/testOneProcessEqualsMin()", + "ComputeProcessTest\/testTwoProcess()", + "ComputeProcessTest\/testTwoProcessEdge()", "ConcurrentSetTests", - "ConcurrentSetTests/testDeleteAll()", - "ConcurrentSetTests/testInsert()", - "ConcurrentSetTests/testSet()", - "ConcurrentSetTests/testTakeAll()", - "ConfigTest/testImpressionsModeEmpty()", - "ConfigTest/testImpressionsModeInvalid()", - "ConfigTest/testImpressionsModedebug()", - "ConfigTest/testImpressionsModenone()", - "ConfigTest/testImpressionsModeoptimized()", + "ConcurrentSetTests\/testDeleteAll()", + "ConcurrentSetTests\/testInsert()", + "ConcurrentSetTests\/testSet()", + "ConcurrentSetTests\/testTakeAll()", + "ConfigTest\/testImpressionsModeEmpty()", + "ConfigTest\/testImpressionsModeInvalid()", + "ConfigTest\/testImpressionsModedebug()", + "ConfigTest\/testImpressionsModenone()", + "ConfigTest\/testImpressionsModeoptimized()", "CountsRecorderCountWorkerTests", - "CountsRecorderCountWorkerTests/testFailToSendSome()", - "CountsRecorderCountWorkerTests/testSendNoImpressions()", - "CountsRecorderCountWorkerTests/testSendOneImpression()", - "CountsRecorderCountWorkerTests/testSendSuccess()", + "CountsRecorderCountWorkerTests\/testFailToSendSome()", + "CountsRecorderCountWorkerTests\/testSendNoImpressions()", + "CountsRecorderCountWorkerTests\/testSendOneImpression()", + "CountsRecorderCountWorkerTests\/testSendSuccess()", "CredentialPinParserTests", - "CredentialPinParserTests/testPinsForHost()", + "CredentialPinParserTests\/testPinsForHost()", "DbCipherTest", - "DbCipherTest/testEncryptDecryptDb()", + "DbCipherTest\/testEncryptDecryptDb()", "DbForDifferentApiKeysTest", - "DbForDifferentApiKeysTest/testInitialization()", - "DecompressionTest/testGzipAllHeaders()", - "DecompressionTest/testGzipCompressionMethodHeader()", - "DecompressionTest/testGzipHeaderComments()", - "DecompressionTest/testGzipHeaderCrc16()", - "DecompressionTest/testGzipHeaderExtraField()", - "DecompressionTest/testGzipHeaderFileName()", - "DecompressionTest/testGzipIncorrectHeader()", - "DecompressionTest/testLoremIpsumGzip()", - "DecompressionTest/testLoremIpsumZlib()", - "DecompressionTest/testZlibCompressionMethodHeader()", + "DbForDifferentApiKeysTest\/testInitialization()", + "DecompressionTest\/testGzipAllHeaders()", + "DecompressionTest\/testGzipCompressionMethodHeader()", + "DecompressionTest\/testGzipHeaderComments()", + "DecompressionTest\/testGzipHeaderCrc16()", + "DecompressionTest\/testGzipHeaderExtraField()", + "DecompressionTest\/testGzipHeaderFileName()", + "DecompressionTest\/testGzipIncorrectHeader()", + "DecompressionTest\/testLoremIpsumGzip()", + "DecompressionTest\/testLoremIpsumZlib()", + "DecompressionTest\/testZlibCompressionMethodHeader()", "EndpointFactoryTest", - "EndpointFactoryTest/testMySegmentsEndpoint()", - "EndpointFactoryTest/testMySegmentsEndpointSlashKeyEncoding()", - "EndpointFactoryTest/testRecordEventsEndpoint()", - "EndpointFactoryTest/testRecordImpressionsEndpoint()", - "EndpointFactoryTest/testSplitChangesEndpoint()", - "EndpointFactoryTest/testStreamingAuthEndpoint()", - "EndpointFactoryTest/testStreamingEndpoint()", - "EndpointFactoryTest/testTelemetryConfigEndpoint()", - "EndpointFactoryTest/testTelemetryUsageEndpoint()", + "EndpointFactoryTest\/testMySegmentsEndpoint()", + "EndpointFactoryTest\/testMySegmentsEndpointSlashKeyEncoding()", + "EndpointFactoryTest\/testRecordEventsEndpoint()", + "EndpointFactoryTest\/testRecordImpressionsEndpoint()", + "EndpointFactoryTest\/testSplitChangesEndpoint()", + "EndpointFactoryTest\/testStreamingAuthEndpoint()", + "EndpointFactoryTest\/testStreamingEndpoint()", + "EndpointFactoryTest\/testTelemetryConfigEndpoint()", + "EndpointFactoryTest\/testTelemetryUsageEndpoint()", "EndpointTest", - "EndpointTest/testHeadersEndpointBuild()", - "EndpointTest/testPostEndpointBuild()", - "EqualToSemverMatcherTest/testGeneralUnsuccessfulMatches()", - "EqualToSemverMatcherTest/testMatchShouldReturnFalseWhenKeyIsNull()", - "EqualToSemverMatcherTest/testMatchShouldReturnFalseWhenPatchDiffers()", - "EqualToSemverMatcherTest/testMatchShouldReturnFalseWhenTargetIsNull()", - "EqualToSemverMatcherTest/testMatchShouldReturnTrueWhenVersionsAreEqual()", - "EqualToSemverMatcherTest/testMatchWithMetadataShouldReturnFalseWhenVersionsDiffer()", - "EqualToSemverMatcherTest/testMatchWithMetadataShouldReturnTrueWhenVersionsAreEqual()", - "EqualToSemverMatcherTest/testMatchWithPreReleaseShouldReturnFalseWhenVersionsDiffer()", - "EqualToSemverMatcherTest/testMatchWithPreReleaseShouldReturnTrueWhenVersionsAreEqual()", + "EndpointTest\/testHeadersEndpointBuild()", + "EndpointTest\/testPostEndpointBuild()", + "EqualToSemverMatcherTest\/testGeneralUnsuccessfulMatches()", + "EqualToSemverMatcherTest\/testMatchShouldReturnFalseWhenKeyIsNull()", + "EqualToSemverMatcherTest\/testMatchShouldReturnFalseWhenPatchDiffers()", + "EqualToSemverMatcherTest\/testMatchShouldReturnFalseWhenTargetIsNull()", + "EqualToSemverMatcherTest\/testMatchShouldReturnTrueWhenVersionsAreEqual()", + "EqualToSemverMatcherTest\/testMatchWithMetadataShouldReturnFalseWhenVersionsDiffer()", + "EqualToSemverMatcherTest\/testMatchWithMetadataShouldReturnTrueWhenVersionsAreEqual()", + "EqualToSemverMatcherTest\/testMatchWithPreReleaseShouldReturnFalseWhenVersionsDiffer()", + "EqualToSemverMatcherTest\/testMatchWithPreReleaseShouldReturnTrueWhenVersionsAreEqual()", "EvaluatorTests", - "EvaluatorTests/testAlgoLegacy()", - "EvaluatorTests/testAlgoMurmur3()", - "EvaluatorTests/testAlgoNull()", - "EvaluatorTests/testAllocation1Percent()", - "EvaluatorTests/testBrokenSplit()", - "EvaluatorTests/testDefaultRule()", - "EvaluatorTests/testDefaultTreatment()", - "EvaluatorTests/testDefaultTreatmentFacundo()", - "EvaluatorTests/testEqualsToSetConfigTreatment()", - "EvaluatorTests/testEqualsToSetNoConfigTreatment()", - "EvaluatorTests/testInLargeSegmentWhitelist()", - "EvaluatorTests/testInSegmentTestKey()", - "EvaluatorTests/testInSegmentsRule1()", - "EvaluatorTests/testKilledSplit()", - "EvaluatorTests/testMatchesStringNoConfigTreatment()", - "EvaluatorTests/testMissingDefaultRule()", - "EvaluatorTests/testNotInLargeSegmentWhitelist()", - "EvaluatorTests/testNotInSplit()", - "EvaluatorTests/testWhitelisted()", - "EvaluatorTests/testWhitelistedOff()", - "EvaluatorTests/testsTrafficAllocation50DefaultRule50()", + "EvaluatorTests\/testAlgoLegacy()", + "EvaluatorTests\/testAlgoMurmur3()", + "EvaluatorTests\/testAlgoNull()", + "EvaluatorTests\/testAllocation1Percent()", + "EvaluatorTests\/testBrokenSplit()", + "EvaluatorTests\/testDefaultRule()", + "EvaluatorTests\/testDefaultTreatment()", + "EvaluatorTests\/testDefaultTreatmentFacundo()", + "EvaluatorTests\/testEqualsToSetConfigTreatment()", + "EvaluatorTests\/testEqualsToSetNoConfigTreatment()", + "EvaluatorTests\/testInLargeSegmentWhitelist()", + "EvaluatorTests\/testInSegmentTestKey()", + "EvaluatorTests\/testInSegmentsRule1()", + "EvaluatorTests\/testKilledSplit()", + "EvaluatorTests\/testMatchesStringNoConfigTreatment()", + "EvaluatorTests\/testMissingDefaultRule()", + "EvaluatorTests\/testNotInLargeSegmentWhitelist()", + "EvaluatorTests\/testNotInSplit()", + "EvaluatorTests\/testWhitelisted()", + "EvaluatorTests\/testWhitelistedOff()", + "EvaluatorTests\/testsTrafficAllocation50DefaultRule50()", "EventDTOJsonTest", - "EventDTOJsonTest/testBasic()", - "EventDTOJsonTest/testEncode()", - "EventDTOJsonTest/testNonNumber()", - "EventDTOJsonTest/testProperties()", - "EventDTOJsonTest/testPropertiesDecodeEncode()", + "EventDTOJsonTest\/testBasic()", + "EventDTOJsonTest\/testEncode()", + "EventDTOJsonTest\/testNonNumber()", + "EventDTOJsonTest\/testProperties()", + "EventDTOJsonTest\/testPropertiesDecodeEncode()", "EventDaoTest", - "EventDaoTest/testDataIsEncryptedInDb()", - "EventDaoTest/testInsertGetAes128Cbc()", - "EventDaoTest/testInsertGetPlainText()", - "EventDaoTest/testInsertManyGetAes128Cbc()", - "EventDaoTest/testInsertManyGetPlainText()", - "EventDaoTest/testLoadOutdatedAes128Cbc()", - "EventDaoTest/testLoadOutdatedPlainText()", - "EventDaoTest/testUpdateAes128Cbc()", - "EventDaoTest/testUpdatePlainText()", + "EventDaoTest\/testDataIsEncryptedInDb()", + "EventDaoTest\/testInsertGetAes128Cbc()", + "EventDaoTest\/testInsertGetPlainText()", + "EventDaoTest\/testInsertManyGetAes128Cbc()", + "EventDaoTest\/testInsertManyGetPlainText()", + "EventDaoTest\/testLoadOutdatedAes128Cbc()", + "EventDaoTest\/testLoadOutdatedPlainText()", + "EventDaoTest\/testUpdateAes128Cbc()", + "EventDaoTest\/testUpdatePlainText()", "EventStreamParserTest", - "EventStreamParserTest/testParseColon()", - "EventStreamParserTest/testParseEmptyLineNoEnd()", - "EventStreamParserTest/testParseEnd()", - "EventStreamParserTest/testParseErrorMessage()", - "EventStreamParserTest/testParseKeepAlive()", - "EventStreamParserTest/testParseNoColon()", - "EventStreamParserTest/testParseNoFieldName()", + "EventStreamParserTest\/testParseColon()", + "EventStreamParserTest\/testParseEmptyLineNoEnd()", + "EventStreamParserTest\/testParseEnd()", + "EventStreamParserTest\/testParseErrorMessage()", + "EventStreamParserTest\/testParseKeepAlive()", + "EventStreamParserTest\/testParseNoColon()", + "EventStreamParserTest\/testParseNoFieldName()", "EventValidatorTests", - "EventValidatorTests/testEmptyTrafficType()", - "EventValidatorTests/testEmptyType()", - "EventValidatorTests/testLongKey()", - "EventValidatorTests/testNoChachedServerAndUppercasedTrafficType()", - "EventValidatorTests/testNoChachedServerTrafficType()", - "EventValidatorTests/testNullKey()", - "EventValidatorTests/testNullTrafficType()", - "EventValidatorTests/testNullType()", - "EventValidatorTests/testTypeName()", - "EventValidatorTests/testUppercaseCharsInTrafficType()", - "EventValidatorTests/testValidEventAllValues()", - "EventValidatorTests/testValidEventNullValue()", + "EventValidatorTests\/testEmptyTrafficType()", + "EventValidatorTests\/testEmptyType()", + "EventValidatorTests\/testLongKey()", + "EventValidatorTests\/testNoChachedServerAndUppercasedTrafficType()", + "EventValidatorTests\/testNoChachedServerTrafficType()", + "EventValidatorTests\/testNullKey()", + "EventValidatorTests\/testNullTrafficType()", + "EventValidatorTests\/testNullType()", + "EventValidatorTests\/testTypeName()", + "EventValidatorTests\/testUppercaseCharsInTrafficType()", + "EventValidatorTests\/testValidEventAllValues()", + "EventValidatorTests\/testValidEventNullValue()", "EventsRecorderWorkerTests", - "EventsRecorderWorkerTests/testFailToSendSome()", - "EventsRecorderWorkerTests/testSendNoEvents()", - "EventsRecorderWorkerTests/testSendOneEvent()", - "EventsRecorderWorkerTests/testSendSuccess()", + "EventsRecorderWorkerTests\/testFailToSendSome()", + "EventsRecorderWorkerTests\/testSendNoEvents()", + "EventsRecorderWorkerTests\/testSendOneEvent()", + "EventsRecorderWorkerTests\/testSendSuccess()", "EventsStorageTest", - "EventsStorageTest/testClear()", - "EventsStorageTest/testDisablePersistence()", - "EventsStorageTest/testEnablePersistence()", - "EventsStorageTest/testStartDisabledPersistence()", - "EventsStorageTest/testStartEnabledPersistence()", - "EventsSynchronizerTest/testDestroy()", - "EventsSynchronizerTest/testFlush()", - "EventsSynchronizerTest/testPush()", - "EventsSynchronizerTest/testResume()", - "EventsSynchronizerTest/testStart()", - "EventsSynchronizerTest/testStop()", + "EventsStorageTest\/testClear()", + "EventsStorageTest\/testDisablePersistence()", + "EventsStorageTest\/testEnablePersistence()", + "EventsStorageTest\/testStartDisabledPersistence()", + "EventsStorageTest\/testStartEnabledPersistence()", + "EventsSynchronizerTest\/testDestroy()", + "EventsSynchronizerTest\/testFlush()", + "EventsSynchronizerTest\/testPush()", + "EventsSynchronizerTest\/testResume()", + "EventsSynchronizerTest\/testStart()", + "EventsSynchronizerTest\/testStop()", "EventsTrackerTest", - "EventsTrackerTest/testTrackDisabled()", + "EventsTrackerTest\/testTrackDisabled()", "FactoryMonitorTest", "FactoryRegistryTest", - "FactoryRegistryTest/testCountLiveFactories()", + "FactoryRegistryTest\/testCountLiveFactories()", "FeatureFlagsPayloadDecoderTest", - "FeatureFlagsPayloadDecoderTest/testDecodeGzip()", + "FeatureFlagsPayloadDecoderTest\/testDecodeGzip()", "FeatureFlagsSynchronizerTest", - "FeatureFlagsSynchronizerTest/testLoadSplitWhenQuerystringNamesChanges()", - "FeatureFlagsSynchronizerTest/testLoadSplitWhenQuerystringSetsChanges()", - "FeatureFlagsSynchronizerTest/testLoadSplitsClearedOnLoadBecauseNotInFilter()", - "FeatureFlagsSynchronizerTest/testLoadSplitsNoClearedOnLoad()", - "FeatureFlagsSynchronizerTest/testLoadSplitsWhenFlagsSetsAndFilterHasChangedClearsAllFeatureFlags()", - "FeatureFlagsSynchronizerTest/testLoadSplitsWhenFlagsSetsHasChangedClearsAllFeatureFlags()", - "FeatureFlagsSynchronizerTest/testStartPeriodicFetchingSingleModeEnabled()", - "FeatureFlagsSynchronizerTest/testStartPeriodicSync()", - "FeatureFlagsSynchronizerTest/testStop()", - "FeatureFlagsSynchronizerTest/testStopPeriodicSync()", - "FeatureFlagsSynchronizerTest/testSynchronizeSplits()", - "FeatureFlagsSynchronizerTest/testSynchronizeSplitsWithChangeNumber()", - "FeatureFlagsSynchronizerTest/testSynchronizeSplitsWithUriTooLong()", - "FeatureFlagsSynchronizerTest/testUpdateSplitsSingleModeEnabled()", + "FeatureFlagsSynchronizerTest\/testLoadSplitWhenQuerystringNamesChanges()", + "FeatureFlagsSynchronizerTest\/testLoadSplitWhenQuerystringSetsChanges()", + "FeatureFlagsSynchronizerTest\/testLoadSplitsClearedOnLoadBecauseNotInFilter()", + "FeatureFlagsSynchronizerTest\/testLoadSplitsNoClearedOnLoad()", + "FeatureFlagsSynchronizerTest\/testLoadSplitsWhenFlagsSetsAndFilterHasChangedClearsAllFeatureFlags()", + "FeatureFlagsSynchronizerTest\/testLoadSplitsWhenFlagsSetsHasChangedClearsAllFeatureFlags()", + "FeatureFlagsSynchronizerTest\/testStartPeriodicFetchingSingleModeEnabled()", + "FeatureFlagsSynchronizerTest\/testStartPeriodicSync()", + "FeatureFlagsSynchronizerTest\/testStop()", + "FeatureFlagsSynchronizerTest\/testStopPeriodicSync()", + "FeatureFlagsSynchronizerTest\/testSynchronizeSplits()", + "FeatureFlagsSynchronizerTest\/testSynchronizeSplitsWithChangeNumber()", + "FeatureFlagsSynchronizerTest\/testSynchronizeSplitsWithUriTooLong()", + "FeatureFlagsSynchronizerTest\/testUpdateSplitsSingleModeEnabled()", "FetchSpecificSplitsTest", - "FetchSpecificSplitsTest/testBothFilters()", - "FetchSpecificSplitsTest/testByNamesFilter()", - "FetchSpecificSplitsTest/testByPrefixFilter()", + "FetchSpecificSplitsTest\/testBothFilters()", + "FetchSpecificSplitsTest\/testByNamesFilter()", + "FetchSpecificSplitsTest\/testByPrefixFilter()", "FetcherThrottleTests", - "FetcherThrottleTests/testDelayValues()", - "FilterBuilderTest/testBasicByNameQueryString()", - "FilterBuilderTest/testBasicBySetQueryString()", - "FilterBuilderTest/testBySetAndByNameQueryString()", - "FilterBuilderTest/testFilterByNamesValuesDeduptedAndGrouped()", - "FilterBuilderTest/testFilterBySetsValuesDeduptedAndGrouped()", - "FilterBuilderTest/testMaxByNameFilterExceded()", - "FilterBuilderTest/testMaxByPrefixFilterExceded()", - "FilterBuilderTest/testOnlyOneTypeQueryString()", - "FilterBuilderTest/testQueryStringWithSpecialChars1()", - "FilterBuilderTest/testQueryStringWithSpecialChars2()", - "FilterBuilderTest/testQueryStringWithSpecialChars3()", - "FilterBuilderTest/testQueryStringWithSpecialChars4()", + "FetcherThrottleTests\/testDelayValues()", + "FilterBuilderTest\/testBasicByNameQueryString()", + "FilterBuilderTest\/testBasicBySetQueryString()", + "FilterBuilderTest\/testBySetAndByNameQueryString()", + "FilterBuilderTest\/testFilterByNamesValuesDeduptedAndGrouped()", + "FilterBuilderTest\/testFilterBySetsValuesDeduptedAndGrouped()", + "FilterBuilderTest\/testMaxByNameFilterExceded()", + "FilterBuilderTest\/testMaxByPrefixFilterExceded()", + "FilterBuilderTest\/testOnlyOneTypeQueryString()", + "FilterBuilderTest\/testQueryStringWithSpecialChars1()", + "FilterBuilderTest\/testQueryStringWithSpecialChars2()", + "FilterBuilderTest\/testQueryStringWithSpecialChars3()", + "FilterBuilderTest\/testQueryStringWithSpecialChars4()", "FlagSetValidatorTests", - "FlagSetValidatorTests/testCleanAndValidateValues()", - "FlagSetValidatorTests/testValidateOnEvaluationWithFilteredValues()", + "FlagSetValidatorTests\/testCleanAndValidateValues()", + "FlagSetValidatorTests\/testValidateOnEvaluationWithFilteredValues()", "FlagSetsCacheTests", - "FlagSetsCacheTests/testAddToFlagSetsNoFilter()", - "FlagSetsCacheTests/testAddToFlagSetsWithFilter()", - "FlagSetsCacheTests/testRemoveFromFlagSetsNoFilter()", + "FlagSetsCacheTests\/testAddToFlagSetsNoFilter()", + "FlagSetsCacheTests\/testAddToFlagSetsWithFilter()", + "FlagSetsCacheTests\/testRemoveFromFlagSetsNoFilter()", "GeneralInfoStorageTest", - "GeneralInfoStorageTest/testGetRolloutCacheLastClearTimestampGetsValueFromDao()", - "GeneralInfoStorageTest/testGetRolloutCacheLastClearTimestampReturnsZeroIfEntityIsNil()", - "GeneralInfoStorageTest/testGetUpdateTimestampGetsValueFromDao()", - "GeneralInfoStorageTest/testGetUpdateTimestampReturnsZeroIfEntityIsNil()", - "GeneralInfoStorageTest/testSetUpdateTimestampSetsValueOnDao()", + "GeneralInfoStorageTest\/testGetRolloutCacheLastClearTimestampGetsValueFromDao()", + "GeneralInfoStorageTest\/testGetRolloutCacheLastClearTimestampReturnsZeroIfEntityIsNil()", + "GeneralInfoStorageTest\/testGetUpdateTimestampGetsValueFromDao()", + "GeneralInfoStorageTest\/testGetUpdateTimestampReturnsZeroIfEntityIsNil()", + "GeneralInfoStorageTest\/testSetUpdateTimestampSetsValueOnDao()", "GreaterThanOrEqualToSemverMatcherTest", - "GreaterThanOrEqualToSemverMatcherTest/testGeneralUnsuccessfulMatches()", - "GreaterThanOrEqualToSemverMatcherTest/testMatchShouldReturnFalseWhenKeyIsLess()", - "GreaterThanOrEqualToSemverMatcherTest/testMatchShouldReturnFalseWhenKeyIsNull()", - "GreaterThanOrEqualToSemverMatcherTest/testMatchShouldReturnTrueWhenKeyIsEqual()", - "GreaterThanOrEqualToSemverMatcherTest/testMatchShouldReturnTrueWhenKeyIsGreater()", - "GreaterThanOrEqualToSemverMatcherTest/testMatchWithMetadataShouldReturnTrueWhenEqual()", - "GreaterThanOrEqualToSemverMatcherTest/testMatchWithMetadataShouldReturnTrueWhenGreater()", - "GreaterThanOrEqualToSemverMatcherTest/testMatchWithPreReleaseShouldReturnFalseWhenLess()", - "GreaterThanOrEqualToSemverMatcherTest/testMatchWithPreReleaseShouldReturnTrueWhenEqual()", - "GreaterThanOrEqualToSemverMatcherTest/testMatchWithPreReleaseShouldReturnTrueWhenGreater()", + "GreaterThanOrEqualToSemverMatcherTest\/testGeneralUnsuccessfulMatches()", + "GreaterThanOrEqualToSemverMatcherTest\/testMatchShouldReturnFalseWhenKeyIsLess()", + "GreaterThanOrEqualToSemverMatcherTest\/testMatchShouldReturnFalseWhenKeyIsNull()", + "GreaterThanOrEqualToSemverMatcherTest\/testMatchShouldReturnTrueWhenKeyIsEqual()", + "GreaterThanOrEqualToSemverMatcherTest\/testMatchShouldReturnTrueWhenKeyIsGreater()", + "GreaterThanOrEqualToSemverMatcherTest\/testMatchWithMetadataShouldReturnTrueWhenEqual()", + "GreaterThanOrEqualToSemverMatcherTest\/testMatchWithMetadataShouldReturnTrueWhenGreater()", + "GreaterThanOrEqualToSemverMatcherTest\/testMatchWithPreReleaseShouldReturnFalseWhenLess()", + "GreaterThanOrEqualToSemverMatcherTest\/testMatchWithPreReleaseShouldReturnTrueWhenEqual()", + "GreaterThanOrEqualToSemverMatcherTest\/testMatchWithPreReleaseShouldReturnTrueWhenGreater()", "HashedImpressionDaoTest", - "HashedImpressionDaoTest/testDelete()", - "HashedImpressionDaoTest/testGetAll()", - "HashedImpressionDaoTest/testUpdate()", + "HashedImpressionDaoTest\/testDelete()", + "HashedImpressionDaoTest\/testGetAll()", + "HashedImpressionDaoTest\/testUpdate()", "HttpClientTest", - "HttpClientTest/testDataRequest()", - "HttpClientTest/testDataRequestError()", - "HttpClientTest/testDataRequestWithOrder()", - "HttpClientTest/testStreamRequest()", - "HttpClientTest/testUploadRequest()", + "HttpClientTest\/testDataRequest()", + "HttpClientTest\/testDataRequestError()", + "HttpClientTest\/testDataRequestWithOrder()", + "HttpClientTest\/testStreamRequest()", + "HttpClientTest\/testUploadRequest()", "HttpDataRequestTest", - "HttpDataRequestTest/testError()", - "HttpDataRequestTest/testOnResponseCompletedError()", - "HttpDataRequestTest/testOnResponseCompletedOk()", - "HttpDataRequestTest/testRequestCreationWithOrder()", - "HttpDataRequestTest/testRequestEnquedOnSend()", + "HttpDataRequestTest\/testError()", + "HttpDataRequestTest\/testOnResponseCompletedError()", + "HttpDataRequestTest\/testOnResponseCompletedOk()", + "HttpDataRequestTest\/testRequestCreationWithOrder()", + "HttpDataRequestTest\/testRequestEnquedOnSend()", "HttpEventsRecorderTests", - "HttpEventsRecorderTests/testServerNoReachable()", - "HttpEventsRecorderTests/testSuccessSending()", + "HttpEventsRecorderTests\/testServerNoReachable()", + "HttpEventsRecorderTests\/testSuccessSending()", "HttpImpressionsCountRecorderTests", - "HttpImpressionsCountRecorderTests/testServerNoReachable()", - "HttpImpressionsCountRecorderTests/testSuccessSending()", + "HttpImpressionsCountRecorderTests\/testServerNoReachable()", + "HttpImpressionsCountRecorderTests\/testSuccessSending()", "HttpImpressionsRecorderTests", - "HttpImpressionsRecorderTests/testServerNoReachable()", - "HttpMySegmentsFetcherTest/testServerNoReachable()", - "HttpMySegmentsFetcherTest/testSuccessFulFetch()", + "HttpImpressionsRecorderTests\/testServerNoReachable()", + "HttpMySegmentsFetcherTest\/testServerNoReachable()", + "HttpMySegmentsFetcherTest\/testSuccessFulFetch()", "HttpRequestListTest", - "HttpRequestListTest/testAddRequest()", - "HttpRequestListTest/testOverrideSetRequest()", + "HttpRequestListTest\/testAddRequest()", + "HttpRequestListTest\/testOverrideSetRequest()", "HttpRequestManagerTests", - "HttpRequestManagerTests/testPinnedCredentialValidation()", + "HttpRequestManagerTests\/testPinnedCredentialValidation()", "HttpResponseTest", - "HttpResponseTest/testHttp104()", - "HttpResponseTest/testHttp200()", - "HttpResponseTest/testHttp299()", - "HttpResponseTest/testHttp300()", + "HttpResponseTest\/testHttp104()", + "HttpResponseTest\/testHttp200()", + "HttpResponseTest\/testHttp299()", + "HttpResponseTest\/testHttp300()", "HttpSplitFetcherTests", - "HttpSplitFetcherTests/testServerNoReachable()", - "HttpSplitFetcherTests/testSuccessFullFetch()", + "HttpSplitFetcherTests\/testServerNoReachable()", + "HttpSplitFetcherTests\/testSuccessFullFetch()", "HttpStreamRequestTest", - "HttpStreamRequestTest/testErrorResponse()", - "HttpStreamRequestTest/testOnResponseOk()", - "HttpStreamRequestTest/testRequestCreation()", - "HttpStreamRequestTest/testRequestEnquedOnSend()", + "HttpStreamRequestTest\/testErrorResponse()", + "HttpStreamRequestTest\/testOnResponseOk()", + "HttpStreamRequestTest\/testRequestCreation()", + "HttpStreamRequestTest\/testRequestEnquedOnSend()", "HttpTelemetryConfigRecorderTest", - "HttpTelemetryConfigRecorderTest/testServerNoReachable()", - "HttpTelemetryConfigRecorderTest/testSuccessSending()", + "HttpTelemetryConfigRecorderTest\/testServerNoReachable()", + "HttpTelemetryConfigRecorderTest\/testSuccessSending()", "HttpTelemetryStatsRecorderTest", - "HttpTelemetryStatsRecorderTest/testServerNoReachable()", - "HttpTelemetryStatsRecorderTest/testSuccessSending()", + "HttpTelemetryStatsRecorderTest\/testServerNoReachable()", + "HttpTelemetryStatsRecorderTest\/testSuccessSending()", "HttpUniqueKeyRecorderTests", - "HttpUniqueKeyRecorderTests/testServerNoReachable()", - "HttpUniqueKeyRecorderTests/testSuccessSending()", + "HttpUniqueKeyRecorderTests\/testServerNoReachable()", + "HttpUniqueKeyRecorderTests\/testSuccessSending()", "ImpressionDaoTest", - "ImpressionDaoTest/testDataIsEncryptedInDb()", - "ImpressionDaoTest/testInsertGet()", - "ImpressionDaoTest/testInsertGetAes128Cbc()", - "ImpressionDaoTest/testInsertManyGetAes128Cbc()", - "ImpressionDaoTest/testLoadOutdated()", - "ImpressionDaoTest/testLoadOutdatedAes128Cbc()", - "ImpressionDaoTest/testUpdate()", - "ImpressionDaoTest/testUpdateAes128Cbc()", + "ImpressionDaoTest\/testDataIsEncryptedInDb()", + "ImpressionDaoTest\/testInsertGet()", + "ImpressionDaoTest\/testInsertGetAes128Cbc()", + "ImpressionDaoTest\/testInsertManyGetAes128Cbc()", + "ImpressionDaoTest\/testLoadOutdated()", + "ImpressionDaoTest\/testLoadOutdatedAes128Cbc()", + "ImpressionDaoTest\/testUpdate()", + "ImpressionDaoTest\/testUpdateAes128Cbc()", "ImpressionHasherTest", - "ImpressionHasherTest/testDifferentChangeNumber()", - "ImpressionHasherTest/testDifferentFeature()", - "ImpressionHasherTest/testDifferentKey()", - "ImpressionHasherTest/testDifferentLabel()", - "ImpressionHasherTest/testDifferentTreatment()", - "ImpressionHasherTest/testNoCrashWhenSplitNull()", + "ImpressionHasherTest\/testDifferentChangeNumber()", + "ImpressionHasherTest\/testDifferentFeature()", + "ImpressionHasherTest\/testDifferentKey()", + "ImpressionHasherTest\/testDifferentLabel()", + "ImpressionHasherTest\/testDifferentTreatment()", + "ImpressionHasherTest\/testNoCrashWhenSplitNull()", "ImpressionsCountDaoTest", - "ImpressionsCountDaoTest/testDataIsEncryptedInDb()", - "ImpressionsCountDaoTest/testInsertGetAes128Cbc()", - "ImpressionsCountDaoTest/testInsertGetPlainText()", - "ImpressionsCountDaoTest/testLoadOutdatedGetAes128Cbc()", - "ImpressionsCountDaoTest/testLoadOutdatedPlainText()", - "ImpressionsCountDaoTest/testUpdateGetAes128Cbc()", - "ImpressionsCountDaoTest/testUpdatePlainText()", + "ImpressionsCountDaoTest\/testDataIsEncryptedInDb()", + "ImpressionsCountDaoTest\/testInsertGetAes128Cbc()", + "ImpressionsCountDaoTest\/testInsertGetPlainText()", + "ImpressionsCountDaoTest\/testLoadOutdatedGetAes128Cbc()", + "ImpressionsCountDaoTest\/testLoadOutdatedPlainText()", + "ImpressionsCountDaoTest\/testUpdateGetAes128Cbc()", + "ImpressionsCountDaoTest\/testUpdatePlainText()", "ImpressionsCounterTest", - "ImpressionsCounterTest/testBasicUsage()", - "ImpressionsCounterTest/testConcurrency()", - "ImpressionsCounterTest/testTruncateTime()", + "ImpressionsCounterTest\/testBasicUsage()", + "ImpressionsCounterTest\/testConcurrency()", + "ImpressionsCounterTest\/testTruncateTime()", "ImpressionsDedupTest", - "ImpressionsDedupTest/testDebug()", - "ImpressionsModeTypeWrapperTest/testInitdebugValue()", - "ImpressionsModeTypeWrapperTest/testInitnoneValue()", - "ImpressionsModeTypeWrapperTest/testInitoptimizedValue()", - "ImpressionsModeTypeWrapperTest/testInvalidValue()", - "ImpressionsModeTypeWrapperTest/testdebugValue()", - "ImpressionsModeTypeWrapperTest/testnoneValue()", - "ImpressionsModeTypeWrapperTest/testoptimizedValue()", + "ImpressionsDedupTest\/testDebug()", + "ImpressionsModeTypeWrapperTest\/testInitdebugValue()", + "ImpressionsModeTypeWrapperTest\/testInitnoneValue()", + "ImpressionsModeTypeWrapperTest\/testInitoptimizedValue()", + "ImpressionsModeTypeWrapperTest\/testInvalidValue()", + "ImpressionsModeTypeWrapperTest\/testdebugValue()", + "ImpressionsModeTypeWrapperTest\/testnoneValue()", + "ImpressionsModeTypeWrapperTest\/testoptimizedValue()", "ImpressionsNoneTest", - "ImpressionsNoneTest/testCorrectData()", - "ImpressionsNoneTest/testPeriodicRecording()", + "ImpressionsNoneTest\/testCorrectData()", + "ImpressionsNoneTest\/testPeriodicRecording()", "ImpressionsObserverTest", - "ImpressionsObserverTest/testBasicFunctionality()", - "ImpressionsObserverTest/testConcurrencyVsAccuracy()", - "ImpressionsObserverTest/testSave()", + "ImpressionsObserverTest\/testBasicFunctionality()", + "ImpressionsObserverTest\/testConcurrencyVsAccuracy()", + "ImpressionsObserverTest\/testSave()", "ImpressionsRecorderWorkerTests", - "ImpressionsRecorderWorkerTests/testFailToSendSome()", - "ImpressionsRecorderWorkerTests/testSendNoImpressions()", - "ImpressionsRecorderWorkerTests/testSendOneImpression()", - "ImpressionsRecorderWorkerTests/testSendSuccess()", + "ImpressionsRecorderWorkerTests\/testFailToSendSome()", + "ImpressionsRecorderWorkerTests\/testSendNoImpressions()", + "ImpressionsRecorderWorkerTests\/testSendOneImpression()", + "ImpressionsRecorderWorkerTests\/testSendSuccess()", "ImpressionsStorageTest", - "ImpressionsStorageTest/testClear()", - "ImpressionsStorageTest/testEnablePersistence()", - "ImpressionsStorageTest/testStartDisabledPersistence()", - "ImpressionsStorageTest/testStartEnabledPersistence()", + "ImpressionsStorageTest\/testClear()", + "ImpressionsStorageTest\/testEnablePersistence()", + "ImpressionsStorageTest\/testStartDisabledPersistence()", + "ImpressionsStorageTest\/testStartEnabledPersistence()", "ImpressionsToggleTest", - "ImpressionsToggleTest/testManagerContainsProperty()", - "ImpressionsToggleTest/testNoneMode()", - "ImpressionsToggleTest/testOptimizedMode()", + "ImpressionsToggleTest\/testManagerContainsProperty()", + "ImpressionsToggleTest\/testNoneMode()", + "ImpressionsToggleTest\/testOptimizedMode()", "ImpressionsTrackerTest", - "ImpressionsTrackerTest/testDestroyDebug()", - "ImpressionsTrackerTest/testDestroyNone()", - "ImpressionsTrackerTest/testDestroyOptimized()", - "ImpressionsTrackerTest/testFlushDebug()", - "ImpressionsTrackerTest/testFlushNone()", - "ImpressionsTrackerTest/testFlushOptimized()", - "ImpressionsTrackerTest/testImpressionPushNone()", - "ImpressionsTrackerTest/testImpressionPushTrackingDisabledDebug()", - "ImpressionsTrackerTest/testImpressionPushTrackingDisabledNone()", - "ImpressionsTrackerTest/testImpressionPushTrackingDisabledOptimized()", - "ImpressionsTrackerTest/testPauseDebug()", - "ImpressionsTrackerTest/testPauseNone()", - "ImpressionsTrackerTest/testPauseOptimized()", - "ImpressionsTrackerTest/testResume()", - "ImpressionsTrackerTest/testStartDebug()", - "ImpressionsTrackerTest/testStartNone()", - "ImpressionsTrackerTest/testStartOptimized()", - "ImpressionsTrackerTest/testStopDebug()", - "ImpressionsTrackerTest/testStopNone()", - "ImpressionsTrackerTest/testStopOptimized()", + "ImpressionsTrackerTest\/testDestroyDebug()", + "ImpressionsTrackerTest\/testDestroyNone()", + "ImpressionsTrackerTest\/testDestroyOptimized()", + "ImpressionsTrackerTest\/testFlushDebug()", + "ImpressionsTrackerTest\/testFlushNone()", + "ImpressionsTrackerTest\/testFlushOptimized()", + "ImpressionsTrackerTest\/testImpressionPushNone()", + "ImpressionsTrackerTest\/testImpressionPushTrackingDisabledDebug()", + "ImpressionsTrackerTest\/testImpressionPushTrackingDisabledNone()", + "ImpressionsTrackerTest\/testImpressionPushTrackingDisabledOptimized()", + "ImpressionsTrackerTest\/testPauseDebug()", + "ImpressionsTrackerTest\/testPauseNone()", + "ImpressionsTrackerTest\/testPauseOptimized()", + "ImpressionsTrackerTest\/testResume()", + "ImpressionsTrackerTest\/testStartDebug()", + "ImpressionsTrackerTest\/testStartNone()", + "ImpressionsTrackerTest\/testStartOptimized()", + "ImpressionsTrackerTest\/testStopDebug()", + "ImpressionsTrackerTest\/testStopNone()", + "ImpressionsTrackerTest\/testStopOptimized()", "InListSemverMatcherTest", - "InListSemverMatcherTest/testGeneralMatches()", - "InListSemverMatcherTest/testMatchShouldReturnFalseWhenNotInList()", - "InListSemverMatcherTest/testMatchShouldReturnTrueWhenInList()", - "InListSemverMatcherTest/testMatchWithEmptyListShouldReturnFalse()", - "InListSemverMatcherTest/testMatchWithMetadataShouldReturnFalseWhenNotInList()", - "InListSemverMatcherTest/testMatchWithMetadataShouldReturnTrueWhenInList()", - "InListSemverMatcherTest/testMatchWithNullKeyShouldReturnFalse()", - "InListSemverMatcherTest/testMatchWithNullTargetShouldReturnFalse()", - "InListSemverMatcherTest/testMatchWithPreReleaseShouldReturnFalseWhenNotInList()", - "InListSemverMatcherTest/testMatchWithPreReleaseShouldReturnTrueWhenInList()", - "InMemoryTelemetryStorageTest/testActiveFactoriesCount()", - "InMemoryTelemetryStorageTest/testAuthRejections()", - "InMemoryTelemetryStorageTest/testConcurrence()", - "InMemoryTelemetryStorageTest/testEvent()", - "InMemoryTelemetryStorageTest/testExceptions()", - "InMemoryTelemetryStorageTest/testHttpErrors()", - "InMemoryTelemetryStorageTest/testHttpLatencies()", - "InMemoryTelemetryStorageTest/testLastSync()", - "InMemoryTelemetryStorageTest/testLatencies()", - "InMemoryTelemetryStorageTest/testRedundantFactoriesCount()", - "InMemoryTelemetryStorageTest/testSessionLength()", - "InMemoryTelemetryStorageTest/testTags()", - "InMemoryTelemetryStorageTest/testTimeUntilReady()", - "InMemoryTelemetryStorageTest/testTimeUntilReadyFromCache()", - "InMemoryTelemetryStorageTest/testTokenRefreshes()", - "InMemoryTelemetryStorageTest/testUpdatesFromSse()", + "InListSemverMatcherTest\/testGeneralMatches()", + "InListSemverMatcherTest\/testMatchShouldReturnFalseWhenNotInList()", + "InListSemverMatcherTest\/testMatchShouldReturnTrueWhenInList()", + "InListSemverMatcherTest\/testMatchWithEmptyListShouldReturnFalse()", + "InListSemverMatcherTest\/testMatchWithMetadataShouldReturnFalseWhenNotInList()", + "InListSemverMatcherTest\/testMatchWithMetadataShouldReturnTrueWhenInList()", + "InListSemverMatcherTest\/testMatchWithNullKeyShouldReturnFalse()", + "InListSemverMatcherTest\/testMatchWithNullTargetShouldReturnFalse()", + "InListSemverMatcherTest\/testMatchWithPreReleaseShouldReturnFalseWhenNotInList()", + "InListSemverMatcherTest\/testMatchWithPreReleaseShouldReturnTrueWhenInList()", + "InMemoryTelemetryStorageTest\/testActiveFactoriesCount()", + "InMemoryTelemetryStorageTest\/testAuthRejections()", + "InMemoryTelemetryStorageTest\/testConcurrence()", + "InMemoryTelemetryStorageTest\/testEvent()", + "InMemoryTelemetryStorageTest\/testExceptions()", + "InMemoryTelemetryStorageTest\/testHttpErrors()", + "InMemoryTelemetryStorageTest\/testHttpLatencies()", + "InMemoryTelemetryStorageTest\/testLastSync()", + "InMemoryTelemetryStorageTest\/testLatencies()", + "InMemoryTelemetryStorageTest\/testRedundantFactoriesCount()", + "InMemoryTelemetryStorageTest\/testSessionLength()", + "InMemoryTelemetryStorageTest\/testTags()", + "InMemoryTelemetryStorageTest\/testTimeUntilReady()", + "InMemoryTelemetryStorageTest\/testTimeUntilReadyFromCache()", + "InMemoryTelemetryStorageTest\/testTokenRefreshes()", + "InMemoryTelemetryStorageTest\/testUpdatesFromSse()", "InitDbCipherTest", - "InitDbCipherTest/testDecryptDb()", - "InitDbCipherTest/testEncryptDb()", + "InitDbCipherTest\/testDecryptDb()", + "InitDbCipherTest\/testEncryptDb()", "InitialCacheTest", - "InitialCacheTest/testClearChangedSplitFilter()", - "InitialCacheTest/testClearExpiredCache()", - "InitialCacheTest/testExpiredCache()", - "InitialCacheTest/testFlagsSpecChanged()", - "InitialCacheTest/testNoClearNoExpiredCache()", + "InitialCacheTest\/testClearChangedSplitFilter()", + "InitialCacheTest\/testClearExpiredCache()", + "InitialCacheTest\/testExpiredCache()", + "InitialCacheTest\/testFlagsSpecChanged()", + "InitialCacheTest\/testNoClearNoExpiredCache()", "InstantFeatureFlagsUpdateTest", - "InstantFeatureFlagsUpdateTest/testInstantUpdateArchived()", - "InstantFeatureFlagsUpdateTest/testInstantUpdateGzip()", + "InstantFeatureFlagsUpdateTest\/testInstantUpdateArchived()", + "InstantFeatureFlagsUpdateTest\/testInstantUpdateGzip()", "JwtTokenParserTest", - "JwtTokenParserTest/testEmptyToken()", - "JwtTokenParserTest/testOkToken()", - "JwtTokenParserTest/testOnlyChannelsWithSeparator()", - "JwtTokenParserTest/testOnlyHeader()", + "JwtTokenParserTest\/testEmptyToken()", + "JwtTokenParserTest\/testOkToken()", + "JwtTokenParserTest\/testOnlyChannelsWithSeparator()", + "JwtTokenParserTest\/testOnlyHeader()", "KeyGeneratorTest", - "KeyGeneratorTest/testGenerationOk()", + "KeyGeneratorTest\/testGenerationOk()", "KeyValidatorTests", - "KeyValidatorTests/testInvalidEmptyBucketingKey()", - "KeyValidatorTests/testInvalidEmptyMatchingKey()", - "KeyValidatorTests/testInvalidLongBucketingKey()", - "KeyValidatorTests/testInvalidLongMatchingKey()", - "KeyValidatorTests/testNullMatchingKey()", - "KeyValidatorTests/testValidMatchingAndBucketingKey()", - "KeyValidatorTests/testValidMatchingKey()", + "KeyValidatorTests\/testInvalidEmptyBucketingKey()", + "KeyValidatorTests\/testInvalidEmptyMatchingKey()", + "KeyValidatorTests\/testInvalidLongBucketingKey()", + "KeyValidatorTests\/testInvalidLongMatchingKey()", + "KeyValidatorTests\/testNullMatchingKey()", + "KeyValidatorTests\/testValidMatchingAndBucketingKey()", + "KeyValidatorTests\/testValidMatchingKey()", "LRUCacheTest", - "LRUCacheTest/testAddGet()", - "LRUCacheTest/testGetAllPerformance()", - "LRUCacheTest/testInsertPerformance()", + "LRUCacheTest\/testAddGet()", + "LRUCacheTest\/testGetAllPerformance()", + "LRUCacheTest\/testInsertPerformance()", "LatencyCounterTests", - "LatencyCounterTests/testAllBuckets()", - "LatencyCounterTests/testFirstBucket()", - "LatencyCounterTests/testInvalidIndex()", - "LatencyCounterTests/testLastBucket()", + "LatencyCounterTests\/testAllBuckets()", + "LatencyCounterTests\/testFirstBucket()", + "LatencyCounterTests\/testInvalidIndex()", + "LatencyCounterTests\/testLastBucket()", "LegacyHashingTest", - "LegacyHashingTest/testBucket()", - "LegacyHashingTest/testHashing()", + "LegacyHashingTest\/testBucket()", + "LegacyHashingTest\/testHashing()", "LessThanOrEqualToSemverMatcherTest", - "LessThanOrEqualToSemverMatcherTest/testGeneralMatches()", - "LessThanOrEqualToSemverMatcherTest/testMatchShouldReturnFalseWhenGreater()", - "LessThanOrEqualToSemverMatcherTest/testMatchShouldReturnTrueWhenEqual()", - "LessThanOrEqualToSemverMatcherTest/testMatchShouldReturnTrueWhenLess()", - "LessThanOrEqualToSemverMatcherTest/testMatchWithMetadataShouldReturnTrue()", - "LessThanOrEqualToSemverMatcherTest/testMatchWithNullKeyShouldReturnNull()", - "LessThanOrEqualToSemverMatcherTest/testMatchWithNullTargetShouldReturnFalse()", - "LessThanOrEqualToSemverMatcherTest/testMatchWithPreReleaseShouldReturnTrue()", + "LessThanOrEqualToSemverMatcherTest\/testGeneralMatches()", + "LessThanOrEqualToSemverMatcherTest\/testMatchShouldReturnFalseWhenGreater()", + "LessThanOrEqualToSemverMatcherTest\/testMatchShouldReturnTrueWhenEqual()", + "LessThanOrEqualToSemverMatcherTest\/testMatchShouldReturnTrueWhenLess()", + "LessThanOrEqualToSemverMatcherTest\/testMatchWithMetadataShouldReturnTrue()", + "LessThanOrEqualToSemverMatcherTest\/testMatchWithNullKeyShouldReturnNull()", + "LessThanOrEqualToSemverMatcherTest\/testMatchWithNullTargetShouldReturnFalse()", + "LessThanOrEqualToSemverMatcherTest\/testMatchWithPreReleaseShouldReturnTrue()", "LocalhostManagerTests", - "LocalhostManagerTests/testSplitNames()", - "LocalhostManagerTests/testSplits()", - "LocalhostManagerTests/testSplitsByName()", + "LocalhostManagerTests\/testSplitNames()", + "LocalhostManagerTests\/testSplits()", + "LocalhostManagerTests\/testSplitsByName()", "LocalhostParserTests", - "LocalhostParserTests/testIntercomentedLinesFile()", - "LocalhostParserTests/testPerfectFile()", - "LocalhostParserTests/testSpacedLinesFile()", + "LocalhostParserTests\/testIntercomentedLinesFile()", + "LocalhostParserTests\/testPerfectFile()", + "LocalhostParserTests\/testSpacedLinesFile()", "LocalhostSplitClientTests", - "LocalhostSplitClientTests/testNonExistingSplitsTreatment()", - "LocalhostSplitClientTests/testNonExistingSplitsTreatments()", - "LocalhostSplitClientTests/testRightSplitsFileTreatment()", - "LocalhostSplitClientTests/testRightSplitsFileTreatments()", - "LocalhostSplitLoaderTests/testFileUpdate()", - "LocalhostSplitLoaderTests/testFileUpdate2()", - "LocalhostSplitLoaderTests/testInitial()", - "LocalhostSplitLoaderTests/testNonExistingFile()", - "LocalhostSplitLoaderTests/testWrongFormatYml()", - "LocalhostSplitLoaderTests/testWrongYamlFormatUpdate()", + "LocalhostSplitClientTests\/testNonExistingSplitsTreatment()", + "LocalhostSplitClientTests\/testNonExistingSplitsTreatments()", + "LocalhostSplitClientTests\/testRightSplitsFileTreatment()", + "LocalhostSplitClientTests\/testRightSplitsFileTreatments()", + "LocalhostSplitLoaderTests\/testFileUpdate()", + "LocalhostSplitLoaderTests\/testFileUpdate2()", + "LocalhostSplitLoaderTests\/testInitial()", + "LocalhostSplitLoaderTests\/testNonExistingFile()", + "LocalhostSplitLoaderTests\/testWrongFormatYml()", + "LocalhostSplitLoaderTests\/testWrongYamlFormatUpdate()", "LocalhostSplitStorageTests", - "LocalhostSplitStorageTests/testClear()", - "LocalhostSplitStorageTests/testGetCount()", - "LocalhostSplitStorageTests/testGetManySplits()", - "LocalhostSplitStorageTests/testGetSplit()", - "LocalhostSplitStorageTests/testUpdateSplitChange()", + "LocalhostSplitStorageTests\/testClear()", + "LocalhostSplitStorageTests\/testGetCount()", + "LocalhostSplitStorageTests\/testGetManySplits()", + "LocalhostSplitStorageTests\/testGetSplit()", + "LocalhostSplitStorageTests\/testUpdateSplitChange()", "LocalhostSynchronizerTests", - "LocalhostSynchronizerTests/testLoadAndSdkReady()", - "LocalhostSynchronizerTests/testUpdateSplits()", - "LocalhostSynchronizerTests/testUpdateYaml()", + "LocalhostSynchronizerTests\/testLoadAndSdkReady()", + "LocalhostSynchronizerTests\/testUpdateSplits()", + "LocalhostSynchronizerTests\/testUpdateYaml()", "LocalhostTests", - "LocalhostTests/testDefaultFactoryCreation()", - "LocalhostTests/testLoadYml()", - "LocalhostTests/testLocalhostFactoryCreation()", - "LocalhostTests/testUsingYamlFromApi()", + "LocalhostTests\/testDefaultFactoryCreation()", + "LocalhostTests\/testLoadYml()", + "LocalhostTests\/testLocalhostFactoryCreation()", + "LocalhostTests\/testUsingYamlFromApi()", "LocalhostYamlParserTest", - "LocalhostYamlParserTest/testCorrectFile()", - "LocalhostYamlParserTest/testMissingSplitTreatment()", - "LocalhostYamlParserTest/testWrongFormat()", - "LoggerTest/testDebug()", - "LoggerTest/testError()", - "LoggerTest/testInfo()", - "LoggerTest/testVerbose()", - "LoggerTest/testWarning()", + "LocalhostYamlParserTest\/testCorrectFile()", + "LocalhostYamlParserTest\/testMissingSplitTreatment()", + "LocalhostYamlParserTest\/testWrongFormat()", + "LoggerTest\/testDebug()", + "LoggerTest\/testError()", + "LoggerTest\/testInfo()", + "LoggerTest\/testVerbose()", + "LoggerTest\/testWarning()", "MatcherEvalTests", - "MatcherEvalTests/testEval()", + "MatcherEvalTests\/testEval()", "MultiClientEvaluationTest", - "MultiClientEvaluationTest/testEvaluation()", - "MultiClientEvaluationTest/testEvaluationFromCache()", - "MultiClientEvaluationTest/testEvaluationWithAttributes()", - "MultiClientEvaluationTest/testImpressions()", - "MultiClientEvaluationTest/testTrack()", + "MultiClientEvaluationTest\/testEvaluation()", + "MultiClientEvaluationTest\/testEvaluationFromCache()", + "MultiClientEvaluationTest\/testEvaluationWithAttributes()", + "MultiClientEvaluationTest\/testImpressions()", + "MultiClientEvaluationTest\/testTrack()", "MultiClientStreamingResetTest", - "MultiClientStreamingResetTest/testNoStreamingDelay()", - "MultiClientStreamingResetTest/testStress()", - "MultiClientStreamingResetTest/testWithStreamingDelay()", + "MultiClientStreamingResetTest\/testNoStreamingDelay()", + "MultiClientStreamingResetTest\/testStress()", + "MultiClientStreamingResetTest\/testWithStreamingDelay()", "Murmur3HashingTest", - "Murmur3HashingTest/test64x128()", - "Murmur3HashingTest/testBucket()", + "Murmur3HashingTest\/test64x128()", + "Murmur3HashingTest\/testBucket()", "MyLargeSegmentsStorageTests", - "MyLargeSegmentsStorageTests/testChangeNumber()", - "MyLargeSegmentsStorageTests/testClear()", - "MyLargeSegmentsStorageTests/testGetMySegmentsAfterLoad()", - "MyLargeSegmentsStorageTests/testNoLoaded()", - "MyLargeSegmentsStorageTests/testUpdateEmptySegments()", - "MyLargeSegmentsStorageTests/testUpdateSegments()", + "MyLargeSegmentsStorageTests\/testChangeNumber()", + "MyLargeSegmentsStorageTests\/testClear()", + "MyLargeSegmentsStorageTests\/testGetMySegmentsAfterLoad()", + "MyLargeSegmentsStorageTests\/testNoLoaded()", + "MyLargeSegmentsStorageTests\/testUpdateEmptySegments()", + "MyLargeSegmentsStorageTests\/testUpdateSegments()", "MySegmentServerErrorTest", - "MySegmentServerErrorTest/test()", + "MySegmentServerErrorTest\/test()", "MySegmentUpdateTest", - "MySegmentUpdateTest/testMyLargeSegmentsUpdate()", - "MySegmentUpdateTest/testMySegmentsLargeUpdateBounded()", - "MySegmentUpdateTest/testMySegmentsUpdate()", - "MySegmentUpdateTest/testMySegmentsUpdateBounded()", - "MySegmentUpdateTest/testSeveralNotificationAndOneFetch()", + "MySegmentUpdateTest\/testMyLargeSegmentsUpdate()", + "MySegmentUpdateTest\/testMySegmentsLargeUpdateBounded()", + "MySegmentUpdateTest\/testMySegmentsUpdate()", + "MySegmentUpdateTest\/testMySegmentsUpdateBounded()", + "MySegmentUpdateTest\/testSeveralNotificationAndOneFetch()", "MySegmentUpdatedTest", - "MySegmentUpdatedTest/testSegments()", + "MySegmentUpdatedTest\/testSegments()", "MySegmentsBgSyncWorkerTest", - "MySegmentsBgSyncWorkerTest/testNoSuccess()", - "MySegmentsBgSyncWorkerTest/testOneTimeFetchSuccess()", + "MySegmentsBgSyncWorkerTest\/testNoSuccess()", + "MySegmentsBgSyncWorkerTest\/testOneTimeFetchSuccess()", "MySegmentsDaoTest", - "MySegmentsDaoTest/testDataIsEncryptedInDb()", - "MySegmentsDaoTest/testGetInvalidKeyAes128Cbc()", - "MySegmentsDaoTest/testGetInvalidKeyPlainText()", - "MySegmentsDaoTest/testUpdateGetAes128Cbc()", - "MySegmentsDaoTest/testUpdateGetPlainText()", + "MySegmentsDaoTest\/testDataIsEncryptedInDb()", + "MySegmentsDaoTest\/testGetInvalidKeyAes128Cbc()", + "MySegmentsDaoTest\/testGetInvalidKeyPlainText()", + "MySegmentsDaoTest\/testUpdateGetAes128Cbc()", + "MySegmentsDaoTest\/testUpdateGetPlainText()", "MySegmentsPayloadDecoderTest", - "MySegmentsPayloadDecoderTest/testUserKeyHash()", + "MySegmentsPayloadDecoderTest\/testUserKeyHash()", "MySegmentsStorageTests", - "MySegmentsStorageTests/testChangeNumber()", - "MySegmentsStorageTests/testClear()", - "MySegmentsStorageTests/testGetMySegmentsAfterLoad()", - "MySegmentsStorageTests/testNoLoaded()", - "MySegmentsStorageTests/testUpdateEmptySegments()", - "MySegmentsStorageTests/testUpdateSegments()", + "MySegmentsStorageTests\/testChangeNumber()", + "MySegmentsStorageTests\/testClear()", + "MySegmentsStorageTests\/testGetMySegmentsAfterLoad()", + "MySegmentsStorageTests\/testNoLoaded()", + "MySegmentsStorageTests\/testUpdateEmptySegments()", + "MySegmentsStorageTests\/testUpdateSegments()", "MySegmentsSyncWorkerTest", - "MySegmentsSyncWorkerTest/testNoCacheHeader()", - "MySegmentsSyncWorkerTest/testOneTimeFetchSuccess()", - "MySegmentsSyncWorkerTest/testRetryAndSuccess()", - "MySegmentsSyncWorkerTest/testStopNoSuccess()", + "MySegmentsSyncWorkerTest\/testNoCacheHeader()", + "MySegmentsSyncWorkerTest\/testOneTimeFetchSuccess()", + "MySegmentsSyncWorkerTest\/testRetryAndSuccess()", + "MySegmentsSyncWorkerTest\/testStopNoSuccess()", "MySegmentsSynchronizerTest", - "MySegmentsSynchronizerTest/testDestroy()", - "MySegmentsSynchronizerTest/testForceSync()", - "MySegmentsSynchronizerTest/testLoadMySegmentsFromCache()", - "MySegmentsSynchronizerTest/testNoPeriodicSync()", - "MySegmentsSynchronizerTest/testPeriodicStartPauseResumeStop()", - "MySegmentsSynchronizerTest/testPeriodicStartStop()", - "MySegmentsSynchronizerTest/testSynchronize()", + "MySegmentsSynchronizerTest\/testDestroy()", + "MySegmentsSynchronizerTest\/testForceSync()", + "MySegmentsSynchronizerTest\/testLoadMySegmentsFromCache()", + "MySegmentsSynchronizerTest\/testNoPeriodicSync()", + "MySegmentsSynchronizerTest\/testPeriodicStartPauseResumeStop()", + "MySegmentsSynchronizerTest\/testPeriodicStartStop()", + "MySegmentsSynchronizerTest\/testSynchronize()", "MySegmentsV2PayloaDecoderTest", - "MySegmentsV2PayloaDecoderTest/testBoundedZlibPayload()", - "MySegmentsV2PayloaDecoderTest/testKeyListGzipPayload()", + "MySegmentsV2PayloaDecoderTest\/testBoundedZlibPayload()", + "MySegmentsV2PayloaDecoderTest\/testKeyListGzipPayload()", "NotificationManagerKeeperTest", - "NotificationManagerKeeperTest/testIncomingControlStreamingDisabled()", - "NotificationManagerKeeperTest/testIncomingControlStreamingEnabled()", - "NotificationManagerKeeperTest/testIncomingControlStreamingEnabledNoPublishers()", - "NotificationManagerKeeperTest/testIncomingControlStreamingPaused()", - "NotificationManagerKeeperTest/testIncomingControlStreamingReset()", - "NotificationManagerKeeperTest/testNoAvailablePublishers()", - "NotificationManagerKeeperTest/testNoAvailablePublishersInPriButAvailableInSec()", - "NotificationManagerKeeperTest/testNoAvailablePublishersOldTimestamp()", - "NotificationManagerKeeperTest/testSecondaryAvailableNotificationReceivedWhenNoPublishers()", - "NotificationManagerKeeperTest/testSecondaryAvailableNotificationReceivedWhenNoPublishersOldTimestamp()", + "NotificationManagerKeeperTest\/testIncomingControlStreamingDisabled()", + "NotificationManagerKeeperTest\/testIncomingControlStreamingEnabled()", + "NotificationManagerKeeperTest\/testIncomingControlStreamingEnabledNoPublishers()", + "NotificationManagerKeeperTest\/testIncomingControlStreamingPaused()", + "NotificationManagerKeeperTest\/testIncomingControlStreamingReset()", + "NotificationManagerKeeperTest\/testNoAvailablePublishers()", + "NotificationManagerKeeperTest\/testNoAvailablePublishersInPriButAvailableInSec()", + "NotificationManagerKeeperTest\/testNoAvailablePublishersOldTimestamp()", + "NotificationManagerKeeperTest\/testSecondaryAvailableNotificationReceivedWhenNoPublishers()", + "NotificationManagerKeeperTest\/testSecondaryAvailableNotificationReceivedWhenNoPublishersOldTimestamp()", "NotificationParserTest", - "NotificationParserTest/testExtractUserKeyHashFromChannel()", - "NotificationParserTest/testProcessControl()", - "NotificationParserTest/testProcessError()", - "NotificationParserTest/testProcessMyLargeSegmentUpdateAllFields()", - "NotificationParserTest/testProcessMyLargeSegmentUpdateRemoval()", - "NotificationParserTest/testProcessMyLargeSegmentUpdateUnbounded()", - "NotificationParserTest/testProcessMySegmentUpdateAllFields()", - "NotificationParserTest/testProcessMySegmentUpdateRemoval()", - "NotificationParserTest/testProcessMySegmentUpdateUnbounded()", - "NotificationParserTest/testProcessOccupancy()", - "NotificationParserTest/testProcessSplitKill()", - "NotificationParserTest/testProcessSplitUpdate()", + "NotificationParserTest\/testExtractUserKeyHashFromChannel()", + "NotificationParserTest\/testProcessControl()", + "NotificationParserTest\/testProcessError()", + "NotificationParserTest\/testProcessMyLargeSegmentUpdateAllFields()", + "NotificationParserTest\/testProcessMyLargeSegmentUpdateRemoval()", + "NotificationParserTest\/testProcessMyLargeSegmentUpdateUnbounded()", + "NotificationParserTest\/testProcessMySegmentUpdateAllFields()", + "NotificationParserTest\/testProcessMySegmentUpdateRemoval()", + "NotificationParserTest\/testProcessMySegmentUpdateUnbounded()", + "NotificationParserTest\/testProcessOccupancy()", + "NotificationParserTest\/testProcessSplitKill()", + "NotificationParserTest\/testProcessSplitUpdate()", "PeriodicMySegmentsSyncWorkerTest", - "PeriodicMySegmentsSyncWorkerTest/testFetchWhenNoSegmentsReadyYet()", - "PeriodicMySegmentsSyncWorkerTest/testNormalFetchSdkIsReady()", + "PeriodicMySegmentsSyncWorkerTest\/testFetchWhenNoSegmentsReadyYet()", + "PeriodicMySegmentsSyncWorkerTest\/testNormalFetchSdkIsReady()", "PeriodicSplitsSyncWorkerTest", - "PeriodicSplitsSyncWorkerTest/testNoSdkReadyFetch()", - "PeriodicSplitsSyncWorkerTest/testNormalFetch()", + "PeriodicSplitsSyncWorkerTest\/testNoSdkReadyFetch()", + "PeriodicSplitsSyncWorkerTest\/testNormalFetch()", "PersistentAttributesStorageTests", - "PersistentAttributesStorageTests/testAll()", - "PersistentAttributesStorageTests/testClear()", - "PersistentAttributesStorageTests/testSet()", + "PersistentAttributesStorageTests\/testAll()", + "PersistentAttributesStorageTests\/testClear()", + "PersistentAttributesStorageTests\/testSet()", "PersistentImpressionsStorageTest", "PersistentMyLargeSegmentsStorageTests", - "PersistentMyLargeSegmentsStorageTests/testClear()", - "PersistentMyLargeSegmentsStorageTests/testGetSnapshot()", - "PersistentMyLargeSegmentsStorageTests/testSet()", - "PersistentMyLargeSegmentsStorageTests/testSetMultiKey()", + "PersistentMyLargeSegmentsStorageTests\/testClear()", + "PersistentMyLargeSegmentsStorageTests\/testGetSnapshot()", + "PersistentMyLargeSegmentsStorageTests\/testSet()", + "PersistentMyLargeSegmentsStorageTests\/testSetMultiKey()", "PersistentMySegmentsStorageTests", - "PersistentMySegmentsStorageTests/testClear()", - "PersistentMySegmentsStorageTests/testGetSnapshot()", - "PersistentMySegmentsStorageTests/testGetSnapshotMultiKey()", - "PersistentMySegmentsStorageTests/testSet()", - "PersistentMySegmentsStorageTests/testSetMultiKey()", + "PersistentMySegmentsStorageTests\/testClear()", + "PersistentMySegmentsStorageTests\/testGetSnapshot()", + "PersistentMySegmentsStorageTests\/testGetSnapshotMultiKey()", + "PersistentMySegmentsStorageTests\/testSet()", + "PersistentMySegmentsStorageTests\/testSetMultiKey()", "PersistentUniqueKeysStorageTests", - "PersistentUniqueKeysStorageTests/testDelete()", - "PersistentUniqueKeysStorageTests/testPop()", - "PersistentUniqueKeysStorageTests/testPush()", - "PersistentUniqueKeysStorageTests/testSetActive()", + "PersistentUniqueKeysStorageTests\/testDelete()", + "PersistentUniqueKeysStorageTests\/testPop()", + "PersistentUniqueKeysStorageTests\/testPush()", + "PersistentUniqueKeysStorageTests\/testSetActive()", "PushManagerEventBroadcasterTest", - "PushManagerEventBroadcasterTest/testStop()", + "PushManagerEventBroadcasterTest\/testStop()", "PushNotificationManagerTest", - "PushNotificationManagerTest/testResetConnectionOk()", - "PushNotificationManagerTest/testStartAuthReintent()", - "PushNotificationManagerTest/testStartFullConnectionOk()", - "PushNotificationManagerTest/testStartSseReintent()", - "PushNotificationManagerTest/testStop()", - "PushNotificationManagerTest/testStreamingDisabled()", + "PushNotificationManagerTest\/testResetConnectionOk()", + "PushNotificationManagerTest\/testStartAuthReintent()", + "PushNotificationManagerTest\/testStartFullConnectionOk()", + "PushNotificationManagerTest\/testStartSseReintent()", + "PushNotificationManagerTest\/testStop()", + "PushNotificationManagerTest\/testStreamingDisabled()", "ReadyFromCacheTest", - "ReadyFromCacheTest/testExistingSplitsAndConnectionOk()", - "ReadyFromCacheTest/testExistingSplitsAndNoConnection()", - "ReadyFromCacheTest/testLargeSegmentsEnabled()", - "ReadyFromCacheTest/testNotExistingSplitsAndConnectionOk()", - "ReadyFromCacheTest/testPersistentAttributesDisabled()", - "ReadyFromCacheTest/testSplitsAndConnOk_FromNoSplitFilterToFilter()", - "ReadyFromCacheTest/testSplitsAndConnOk_FromSplitFilterToNoFilter()", + "ReadyFromCacheTest\/testExistingSplitsAndConnectionOk()", + "ReadyFromCacheTest\/testExistingSplitsAndNoConnection()", + "ReadyFromCacheTest\/testLargeSegmentsEnabled()", + "ReadyFromCacheTest\/testNotExistingSplitsAndConnectionOk()", + "ReadyFromCacheTest\/testPersistentAttributesDisabled()", + "ReadyFromCacheTest\/testSplitsAndConnOk_FromNoSplitFilterToFilter()", + "ReadyFromCacheTest\/testSplitsAndConnOk_FromSplitFilterToNoFilter()", "ReconnectBackoffCounterTest", - "ReconnectBackoffCounterTest/testBase2()", - "ReconnectBackoffCounterTest/testBase3()", - "ReconnectBackoffCounterTest/testBase8()", + "ReconnectBackoffCounterTest\/testBase2()", + "ReconnectBackoffCounterTest\/testBase3()", + "ReconnectBackoffCounterTest\/testBase8()", "RecorderFlusherCheckerTests", - "RecorderFlusherCheckerTests/testBytesLimit()", + "RecorderFlusherCheckerTests\/testBytesLimit()", "RegexTest", - "RegexTest/testsRegex()", + "RegexTest\/testsRegex()", "RolloutCacheConfigurationTest", - "RolloutCacheConfigurationTest/testClearOnInitIsCorrectlySet()", - "RolloutCacheConfigurationTest/testDefaultValues()", - "RolloutCacheConfigurationTest/testExpirationIsCorrectlySet()", - "RolloutCacheConfigurationTest/testNegativeExpirationIsSetToDefault()", + "RolloutCacheConfigurationTest\/testClearOnInitIsCorrectlySet()", + "RolloutCacheConfigurationTest\/testDefaultValues()", + "RolloutCacheConfigurationTest\/testExpirationIsCorrectlySet()", + "RolloutCacheConfigurationTest\/testNegativeExpirationIsSetToDefault()", "RolloutCacheManagerIntegrationTest", - "RolloutCacheManagerIntegrationTest/testClearOnInitClearCacheOnStartup()", - "RolloutCacheManagerIntegrationTest/testExpirationPeriodIsUsed()", - "RolloutCacheManagerIntegrationTest/testRepeatedInitWithClearOnInitSetToTrueDoesNotClearIfMinDaysHasNotElapsed()", + "RolloutCacheManagerIntegrationTest\/testClearOnInitClearCacheOnStartup()", + "RolloutCacheManagerIntegrationTest\/testExpirationPeriodIsUsed()", + "RolloutCacheManagerIntegrationTest\/testRepeatedInitWithClearOnInitSetToTrueDoesNotClearIfMinDaysHasNotElapsed()", "RolloutCacheManagerTest", - "RolloutCacheManagerTest/testDefaultValueForLastClearTimestampClearsCacheWhenClearOnInitIsTrue()", - "RolloutCacheManagerTest/testDefaultValueForUpdateTimestampDoesNotClearCache()", - "RolloutCacheManagerTest/testValidateCacheCallsClearOnStorageOnlyOnceWhenExecutedConsecutively()", - "RolloutCacheManagerTest/testValidateCacheCallsClearOnStoragesWhenExpirationIsNotSurpassedAndClearOnInitIsTrue()", - "RolloutCacheManagerTest/testValidateCacheCallsClearOnStoragesWhenExpirationIsSurpassed()", - "RolloutCacheManagerTest/testValidateCacheDoesNotCallClearOnStoragesWhenExpirationIsNotSurpassedAndClearOnInitIsFalse()", - "RolloutCacheManagerTest/testValidateCacheDoesNotUpdateLastClearTimestampWhenStoragesAreNotCleared()", - "RolloutCacheManagerTest/testValidateCacheUpdatesLastClearTimestampWhenStoragesAreCleared()", + "RolloutCacheManagerTest\/testDefaultValueForLastClearTimestampClearsCacheWhenClearOnInitIsTrue()", + "RolloutCacheManagerTest\/testDefaultValueForUpdateTimestampDoesNotClearCache()", + "RolloutCacheManagerTest\/testValidateCacheCallsClearOnStorageOnlyOnceWhenExecutedConsecutively()", + "RolloutCacheManagerTest\/testValidateCacheCallsClearOnStoragesWhenExpirationIsNotSurpassedAndClearOnInitIsTrue()", + "RolloutCacheManagerTest\/testValidateCacheCallsClearOnStoragesWhenExpirationIsSurpassed()", + "RolloutCacheManagerTest\/testValidateCacheDoesNotCallClearOnStoragesWhenExpirationIsNotSurpassedAndClearOnInitIsFalse()", + "RolloutCacheManagerTest\/testValidateCacheDoesNotUpdateLastClearTimestampWhenStoragesAreNotCleared()", + "RolloutCacheManagerTest\/testValidateCacheUpdatesLastClearTimestampWhenStoragesAreCleared()", "SdkUpdateStreamingTest", - "SdkUpdateStreamingTest/testReady()", - "SdkUpdateStreamingTest/testSdkUpdateMySegmentsWhenNotificationArrives()", - "SdkUpdateStreamingTest/testSdkUpdateSplitsWhenNotificationArrives()", + "SdkUpdateStreamingTest\/testReady()", + "SdkUpdateStreamingTest\/testSdkUpdateMySegmentsWhenNotificationArrives()", + "SdkUpdateStreamingTest\/testSdkUpdateSplitsWhenNotificationArrives()", "SegmentsSyncHelperTests", - "SegmentsSyncHelperTests/testCdnByPassNoTillChange()", - "SegmentsSyncHelperTests/testCdnByPassNoTillNoChange()", - "SegmentsSyncHelperTests/testCdnByPassTill()", - "SegmentsSyncHelperTests/testDidffGoallCnMs()", - "SegmentsSyncHelperTests/testDiffGoalCnMls()", + "SegmentsSyncHelperTests\/testCdnByPassNoTillChange()", + "SegmentsSyncHelperTests\/testCdnByPassNoTillNoChange()", + "SegmentsSyncHelperTests\/testCdnByPassTill()", + "SegmentsSyncHelperTests\/testDidffGoallCnMs()", + "SegmentsSyncHelperTests\/testDiffGoalCnMls()", "SegmentsUpdateWorkerTests", - "SegmentsUpdateWorkerTests/testLargeSegmentsKeyListAdd()", - "SegmentsUpdateWorkerTests/testLargeSegmentsKeyListNoAction()", - "SegmentsUpdateWorkerTests/testLargeSegmentsKeyListRemoval()", - "SegmentsUpdateWorkerTests/testMyLargeSegmentsNonRemoval()", - "SegmentsUpdateWorkerTests/testMyLargeSegmentsRemoval()", - "SegmentsUpdateWorkerTests/testMySegmentsNonRemoval()", - "SegmentsUpdateWorkerTests/testMySegmentsRemoval()", - "SegmentsUpdateWorkerTests/testSegmentsKeyListAdd()", - "SegmentsUpdateWorkerTests/testSegmentsKeyListNoAction()", - "SegmentsUpdateWorkerTests/testSegmentsKeyListRemove()", - "SegmentsUpdateWorkerTests/testUnbounded()", - "SegmentsUpdateWorkerTests/testUnboundedLarge()", + "SegmentsUpdateWorkerTests\/testLargeSegmentsKeyListAdd()", + "SegmentsUpdateWorkerTests\/testLargeSegmentsKeyListNoAction()", + "SegmentsUpdateWorkerTests\/testLargeSegmentsKeyListRemoval()", + "SegmentsUpdateWorkerTests\/testMyLargeSegmentsNonRemoval()", + "SegmentsUpdateWorkerTests\/testMyLargeSegmentsRemoval()", + "SegmentsUpdateWorkerTests\/testMySegmentsNonRemoval()", + "SegmentsUpdateWorkerTests\/testMySegmentsRemoval()", + "SegmentsUpdateWorkerTests\/testSegmentsKeyListAdd()", + "SegmentsUpdateWorkerTests\/testSegmentsKeyListNoAction()", + "SegmentsUpdateWorkerTests\/testSegmentsKeyListRemove()", + "SegmentsUpdateWorkerTests\/testUnbounded()", + "SegmentsUpdateWorkerTests\/testUnboundedLarge()", "SemverIntegrationTest", - "SemverIntegrationTest/testBetweenSemverMatcher()", - "SemverIntegrationTest/testEqualToSemverMatcher()", - "SemverIntegrationTest/testInListSemverMatcher()", - "SemverIntegrationTest/testLessThanOrEqualToSemverMatcher()", + "SemverIntegrationTest\/testBetweenSemverMatcher()", + "SemverIntegrationTest\/testEqualToSemverMatcher()", + "SemverIntegrationTest\/testInListSemverMatcher()", + "SemverIntegrationTest\/testLessThanOrEqualToSemverMatcher()", "SemverTest", - "SemverTest/testBetween()", - "SemverTest/testEqualTo()", - "SemverTest/testGreaterThanOrEqualTo()", - "SemverTest/testInvalidFormats()", - "SemverTest/testLessThanOrEqualTo()", - "ServiceEndpointsTests/testBuilderValidationAuthError()", - "ServiceEndpointsTests/testBuilderValidationEventsError()", - "ServiceEndpointsTests/testBuilderValidationOk()", - "ServiceEndpointsTests/testBuilderValidationSdkError()", - "ServiceEndpointsTests/testBuilderValidationStreamingError()", - "ServiceEndpointsTests/testBuilderValidationTelemetryError()", - "SingleSyncTest/testSingleSyncEnabledImpressionsDebug()", - "SingleSyncTest/testSingleSyncEnabledImpressionsNone()", - "SingleSyncTest/testSingleSyncEnabledImpressionsOptmized()", + "SemverTest\/testBetween()", + "SemverTest\/testEqualTo()", + "SemverTest\/testGreaterThanOrEqualTo()", + "SemverTest\/testInvalidFormats()", + "SemverTest\/testLessThanOrEqualTo()", + "ServiceEndpointsTests\/testBuilderValidationAuthError()", + "ServiceEndpointsTests\/testBuilderValidationEventsError()", + "ServiceEndpointsTests\/testBuilderValidationOk()", + "ServiceEndpointsTests\/testBuilderValidationSdkError()", + "ServiceEndpointsTests\/testBuilderValidationStreamingError()", + "ServiceEndpointsTests\/testBuilderValidationTelemetryError()", + "SingleSyncTest\/testSingleSyncEnabledImpressionsDebug()", + "SingleSyncTest\/testSingleSyncEnabledImpressionsNone()", + "SingleSyncTest\/testSingleSyncEnabledImpressionsOptmized()", "SplitChangeProcessorTests", - "SplitChangeProcessorTests/testProcessNoSets()", + "SplitChangeProcessorTests\/testProcessNoSets()", "SplitChangesServerErrorTest", - "SplitChangesServerErrorTest/testChangesError()", + "SplitChangesServerErrorTest\/testChangesError()", "SplitChangesTest", - "SplitChangesTest/test()", + "SplitChangesTest\/test()", "SplitClientManagerTest", - "SplitClientManagerTest/testAddClient()", - "SplitClientManagerTest/testDestroyForKey()", - "SplitClientManagerTest/testDestroyLastKey()", - "SplitClientManagerTest/testFlush()", - "SplitClientManagerTest/testInit()", + "SplitClientManagerTest\/testAddClient()", + "SplitClientManagerTest\/testDestroyForKey()", + "SplitClientManagerTest\/testDestroyLastKey()", + "SplitClientManagerTest\/testFlush()", + "SplitClientManagerTest\/testInit()", "SplitClientTests", - "SplitClientTests/testOnBg()", - "SplitClientTests/testOnMain()", - "SplitClientTests/testOnQueue()", + "SplitClientTests\/testOnBg()", + "SplitClientTests\/testOnMain()", + "SplitClientTests\/testOnQueue()", "SplitConfigurationsParsingTest", - "SplitConfigurationsParsingTest/testDecodingArrayAndMapConfig()", - "SplitConfigurationsParsingTest/testEncodingAllValueTypesConfig()", - "SplitConfigurationsParsingTest/testEncodingMapArrayConfig()", - "SplitConfigurationsParsingTest/testEncodingMultiTreatmentConfig()", - "SplitConfigurationsParsingTest/testEncodingNestedMultiConfig()", - "SplitConfigurationsParsingTest/testEncodingNullConfig()", - "SplitConfigurationsParsingTest/testEncodingOneBasicConfig()", + "SplitConfigurationsParsingTest\/testDecodingArrayAndMapConfig()", + "SplitConfigurationsParsingTest\/testEncodingAllValueTypesConfig()", + "SplitConfigurationsParsingTest\/testEncodingMapArrayConfig()", + "SplitConfigurationsParsingTest\/testEncodingMultiTreatmentConfig()", + "SplitConfigurationsParsingTest\/testEncodingNestedMultiConfig()", + "SplitConfigurationsParsingTest\/testEncodingNullConfig()", + "SplitConfigurationsParsingTest\/testEncodingOneBasicConfig()", "SplitDaoTest", - "SplitDaoTest/testCreateGetAes128Cbc()", - "SplitDaoTest/testCreateGetPlainText()", - "SplitDaoTest/testDataIsEncryptedInDb()", - "SplitDaoTest/testDeleteAllAes128Cbc()", - "SplitDaoTest/testDeleteAllPlainText()", - "SplitDaoTest/testGetUpdate()", - "SplitDaoTest/testGetUpdateAes128Cbc()", - "SplitDaoTest/testGetUpdateSeveralAes128Cbc()", - "SplitDaoTest/testGetUpdateSeveralPlainText()", + "SplitDaoTest\/testCreateGetAes128Cbc()", + "SplitDaoTest\/testCreateGetPlainText()", + "SplitDaoTest\/testDataIsEncryptedInDb()", + "SplitDaoTest\/testDeleteAllAes128Cbc()", + "SplitDaoTest\/testDeleteAllPlainText()", + "SplitDaoTest\/testGetUpdate()", + "SplitDaoTest\/testGetUpdateAes128Cbc()", + "SplitDaoTest\/testGetUpdateSeveralAes128Cbc()", + "SplitDaoTest\/testGetUpdateSeveralPlainText()", "SplitEventsCoordinatorTest", - "SplitEventsCoordinatorTest/testAddNotifyRemoveManager()", - "SplitEventsCoordinatorTest/testEventsAfterAdd()", - "SplitEventsCoordinatorTest/testEventsBeforeAdd()", - "SplitEventsCoordinatorTest/testStop()", + "SplitEventsCoordinatorTest\/testAddNotifyRemoveManager()", + "SplitEventsCoordinatorTest\/testEventsAfterAdd()", + "SplitEventsCoordinatorTest\/testEventsBeforeAdd()", + "SplitEventsCoordinatorTest\/testStop()", "SplitEventsManagerTest", - "SplitEventsManagerTest/testSdkReady()", - "SplitEventsManagerTest/testSdkReadyAndReadyTimeOut()", - "SplitEventsManagerTest/testSdkReadyFromCacheAndReady()", - "SplitEventsManagerTest/testSdkReadyFromCacheAndReadyTimeout()", - "SplitEventsManagerTest/testSdkReadyTimeOut()", - "SplitEventsManagerTest/testSdkUpdateMySegments()", - "SplitEventsManagerTest/testSdkUpdateSplits()", - "SplitEventsManagerTest/testSplitKilledNoSdkReady()", - "SplitEventsManagerTest/testSplitKilledWhenReady()", + "SplitEventsManagerTest\/testSdkReady()", + "SplitEventsManagerTest\/testSdkReadyAndReadyTimeOut()", + "SplitEventsManagerTest\/testSdkReadyFromCacheAndReady()", + "SplitEventsManagerTest\/testSdkReadyFromCacheAndReadyTimeout()", + "SplitEventsManagerTest\/testSdkReadyTimeOut()", + "SplitEventsManagerTest\/testSdkUpdateMySegments()", + "SplitEventsManagerTest\/testSdkUpdateSplits()", + "SplitEventsManagerTest\/testSplitKilledNoSdkReady()", + "SplitEventsManagerTest\/testSplitKilledWhenReady()", "SplitFactoryBuilderTests", - "SplitFactoryBuilderTests/testEmptyApiKey()", - "SplitFactoryBuilderTests/testKey()", - "SplitFactoryBuilderTests/testLongBucketingKey()", - "SplitFactoryBuilderTests/testLongMatchingKey()", - "SplitFactoryBuilderTests/testMultiFactoryDifferentKey()", - "SplitFactoryBuilderTests/testMultiFactorySameKey()", - "SplitFactoryBuilderTests/testMultiFactoryTwoSameKey()", - "SplitFactoryBuilderTests/testNullApiKey()", - "SplitFactoryBuilderTests/testNullKey()", - "SplitFactoryBuilderTests/testNullMatchingKey()", - "SplitFactoryBuilderTests/testNullValues()", + "SplitFactoryBuilderTests\/testEmptyApiKey()", + "SplitFactoryBuilderTests\/testKey()", + "SplitFactoryBuilderTests\/testLongBucketingKey()", + "SplitFactoryBuilderTests\/testLongMatchingKey()", + "SplitFactoryBuilderTests\/testMultiFactoryDifferentKey()", + "SplitFactoryBuilderTests\/testMultiFactorySameKey()", + "SplitFactoryBuilderTests\/testMultiFactoryTwoSameKey()", + "SplitFactoryBuilderTests\/testNullApiKey()", + "SplitFactoryBuilderTests\/testNullKey()", + "SplitFactoryBuilderTests\/testNullMatchingKey()", + "SplitFactoryBuilderTests\/testNullValues()", "SplitIntegrationTests", - "SplitIntegrationTests/testControlTreatment()", - "SplitIntegrationTests/testImpressionsCount()", - "SplitIntegrationTests/testReadyMyLargeSegmentsEnabledError()", - "SplitIntegrationTests/testReadyMyLargeSegmentsEnabledNoWaitMls()", - "SplitIntegrationTests/testReadyMyLargeSegmentsEnabledWaitMls()", - "SplitIntegrationTests/testReadyNoRef()", + "SplitIntegrationTests\/testControlTreatment()", + "SplitIntegrationTests\/testImpressionsCount()", + "SplitIntegrationTests\/testReadyMyLargeSegmentsEnabledError()", + "SplitIntegrationTests\/testReadyMyLargeSegmentsEnabledNoWaitMls()", + "SplitIntegrationTests\/testReadyMyLargeSegmentsEnabledWaitMls()", + "SplitIntegrationTests\/testReadyNoRef()", "SplitManagerTest", - "SplitManagerTest/testAddOneSplit()", - "SplitManagerTest/testEmptyName()", - "SplitManagerTest/testInitialSplitLoaded()", - "SplitManagerTest/testSplitInfo()", + "SplitManagerTest\/testAddOneSplit()", + "SplitManagerTest\/testEmptyName()", + "SplitManagerTest\/testInitialSplitLoaded()", + "SplitManagerTest\/testSplitInfo()", "SplitSdkUpdatePollingTest", - "SplitSdkUpdatePollingTest/testSdkReadyOnly()", - "SplitSdkUpdatePollingTest/testSdkUpdateMySegments()", - "SplitSdkUpdatePollingTest/testSdkUpdateSplits()", + "SplitSdkUpdatePollingTest\/testSdkReadyOnly()", + "SplitSdkUpdatePollingTest\/testSdkUpdateMySegments()", + "SplitSdkUpdatePollingTest\/testSdkUpdateSplits()", "SplitValidatorTests", - "SplitValidatorTests/testEmptyName()", - "SplitValidatorTests/testExistingSplit()", - "SplitValidatorTests/testLeadingSpacesName()", - "SplitValidatorTests/testNoExistingSplit()", - "SplitValidatorTests/testNullName()", - "SplitValidatorTests/testTrailingSpacesName()", - "SplitValidatorTests/testValidName()", + "SplitValidatorTests\/testEmptyName()", + "SplitValidatorTests\/testExistingSplit()", + "SplitValidatorTests\/testLeadingSpacesName()", + "SplitValidatorTests\/testNoExistingSplit()", + "SplitValidatorTests\/testNullName()", + "SplitValidatorTests\/testTrailingSpacesName()", + "SplitValidatorTests\/testValidName()", "SplitsBgSyncWorkerTest", - "SplitsBgSyncWorkerTest/testClearExpiredCache()", - "SplitsBgSyncWorkerTest/testFetchFail()", - "SplitsBgSyncWorkerTest/testFetchSuccess()", - "SplitsBgSyncWorkerTest/testNoClearNonExpiredCache()", + "SplitsBgSyncWorkerTest\/testClearExpiredCache()", + "SplitsBgSyncWorkerTest\/testFetchFail()", + "SplitsBgSyncWorkerTest\/testFetchSuccess()", + "SplitsBgSyncWorkerTest\/testNoClearNonExpiredCache()", "SplitsChangesCheckerTest", - "SplitsChangesCheckerTest/testSplitsChangesArrived()", - "SplitsChangesCheckerTest/testSplitsNoChangesEqualChangeNumber()", - "SplitsChangesCheckerTest/testSplitsNoChangesMinorChangeNumber()", + "SplitsChangesCheckerTest\/testSplitsChangesArrived()", + "SplitsChangesCheckerTest\/testSplitsNoChangesEqualChangeNumber()", + "SplitsChangesCheckerTest\/testSplitsNoChangesMinorChangeNumber()", "SplitsDecoderTest", - "SplitsDecoderTest/testDecodeMultiThreads()", - "SplitsDecoderTest/testDecodeOneThread()", - "SplitsDecoderTest/testDecodeTwoThreads()", + "SplitsDecoderTest\/testDecodeMultiThreads()", + "SplitsDecoderTest\/testDecodeOneThread()", + "SplitsDecoderTest\/testDecodeTwoThreads()", "SplitsEncoderTest", - "SplitsEncoderTest/testDecodeMultiThread()", - "SplitsEncoderTest/testDecodeOneThread()", - "SplitsEncoderTest/testDecodeTwoThread()", + "SplitsEncoderTest\/testDecodeMultiThread()", + "SplitsEncoderTest\/testDecodeOneThread()", + "SplitsEncoderTest\/testDecodeTwoThread()", "SplitsStorageTrafficTypesTests", - "SplitsStorageTrafficTypesTests/testChangedTrafficTypeForSplit()", - "SplitsStorageTrafficTypesTests/testExistingChangedTrafficTypeForSplit()", - "SplitsStorageTrafficTypesTests/testInitialtrafficTypes()", - "SplitsStorageTrafficTypesTests/testOverflowArchived()", - "SplitsStorageTrafficTypesTests/testRemove2TrafficTypes()", - "SplitsStorageTrafficTypesTests/testSeveralTrafficTypeUpdatesFinalActive()", - "SplitsStorageTrafficTypesTests/testSeveralTrafficTypeUpdatesFinalArchived()", - "SplitsStorageTrafficTypesTests/testUpdatedSplitTrafficType()", + "SplitsStorageTrafficTypesTests\/testChangedTrafficTypeForSplit()", + "SplitsStorageTrafficTypesTests\/testExistingChangedTrafficTypeForSplit()", + "SplitsStorageTrafficTypesTests\/testInitialtrafficTypes()", + "SplitsStorageTrafficTypesTests\/testOverflowArchived()", + "SplitsStorageTrafficTypesTests\/testRemove2TrafficTypes()", + "SplitsStorageTrafficTypesTests\/testSeveralTrafficTypeUpdatesFinalActive()", + "SplitsStorageTrafficTypesTests\/testSeveralTrafficTypeUpdatesFinalArchived()", + "SplitsStorageTrafficTypesTests\/testUpdatedSplitTrafficType()", "SplitsSyncWorkerTest", - "SplitsSyncWorkerTest/testChangedFlagsSpecString()", - "SplitsSyncWorkerTest/testChangedQueryString()", - "SplitsSyncWorkerTest/testClearExpiredCache()", - "SplitsSyncWorkerTest/testClearExpiredCacheAndChangedQs()", - "SplitsSyncWorkerTest/testNoClearNonExpiredCache()", - "SplitsSyncWorkerTest/testRetryAndSuccess()", - "SplitsSyncWorkerTest/testUriTooLongError()", + "SplitsSyncWorkerTest\/testChangedFlagsSpecString()", + "SplitsSyncWorkerTest\/testChangedQueryString()", + "SplitsSyncWorkerTest\/testClearExpiredCache()", + "SplitsSyncWorkerTest\/testClearExpiredCacheAndChangedQs()", + "SplitsSyncWorkerTest\/testNoClearNonExpiredCache()", + "SplitsSyncWorkerTest\/testRetryAndSuccess()", + "SplitsSyncWorkerTest\/testUriTooLongError()", "SplitsUpdateWorkerTest", - "SplitsUpdateWorkerTest/testOldChangeNumber()", - "SplitsUpdateWorkerTest/testOneTimeFetchSuccess()", - "SplitsUpdateWorkerTest/testRetryAndSuccess()", - "SplitsUpdateWorkerTest/testStopNoSuccess()", - "SseAuthenticatorTest/testEmptyTokenResponse()", - "SseAuthenticatorTest/testNoRecoverableError()", - "SseAuthenticatorTest/testRecoverableError()", - "SseAuthenticatorTest/testSuccesfulMultiUserKeyRequest()", - "SseAuthenticatorTest/testSuccesfulRequest()", + "SplitsUpdateWorkerTest\/testOldChangeNumber()", + "SplitsUpdateWorkerTest\/testOneTimeFetchSuccess()", + "SplitsUpdateWorkerTest\/testRetryAndSuccess()", + "SplitsUpdateWorkerTest\/testStopNoSuccess()", + "SseAuthenticatorTest\/testEmptyTokenResponse()", + "SseAuthenticatorTest\/testNoRecoverableError()", + "SseAuthenticatorTest\/testRecoverableError()", + "SseAuthenticatorTest\/testSuccesfulMultiUserKeyRequest()", + "SseAuthenticatorTest\/testSuccesfulRequest()", "SseClientTest", - "SseClientTest/testConnect()", - "SseClientTest/testDisconnect()", - "SseClientTest/testDisconnectFromServer()", - "SseClientTest/testOnErrorAfterConnectionSuccess()", - "SseClientTest/testOnErrorExceptionWhileRequest()", - "SseClientTest/testOnErrorNonRecoverable()", - "SseClientTest/testOnErrorRecoverable()", - "SseClientTest/testOnMessage()", + "SseClientTest\/testConnect()", + "SseClientTest\/testDisconnect()", + "SseClientTest\/testDisconnectFromServer()", + "SseClientTest\/testOnErrorAfterConnectionSuccess()", + "SseClientTest\/testOnErrorExceptionWhileRequest()", + "SseClientTest\/testOnErrorNonRecoverable()", + "SseClientTest\/testOnErrorRecoverable()", + "SseClientTest\/testOnMessage()", "SseHandlerTest", - "SseHandlerTest/testIncomingControlStreaming()", - "SseHandlerTest/testIncomingHightRetryableSseError()", - "SseHandlerTest/testIncomingIgnorableSseErrorTest()", - "SseHandlerTest/testIncomingLowNonRetryableSseError()", - "SseHandlerTest/testIncomingLowRetryableSseError()", - "SseHandlerTest/testIncomingMySegmentsUpdate()", - "SseHandlerTest/testIncomingOccupancy()", - "SseHandlerTest/testIncomingSplitKill()", - "SseHandlerTest/testIncomingSplitUpdate()", - "SseHandlerTest/testNoProcessIncomingWhenStreamingInactive()", + "SseHandlerTest\/testIncomingControlStreaming()", + "SseHandlerTest\/testIncomingHightRetryableSseError()", + "SseHandlerTest\/testIncomingIgnorableSseErrorTest()", + "SseHandlerTest\/testIncomingLowNonRetryableSseError()", + "SseHandlerTest\/testIncomingLowRetryableSseError()", + "SseHandlerTest\/testIncomingMySegmentsUpdate()", + "SseHandlerTest\/testIncomingOccupancy()", + "SseHandlerTest\/testIncomingSplitKill()", + "SseHandlerTest\/testIncomingSplitUpdate()", + "SseHandlerTest\/testNoProcessIncomingWhenStreamingInactive()", "SseNotificationProcessorTest", - "SseNotificationProcessorTest/testProcessMyLargeSegmentsUpdateKeyListRequest()", - "SseNotificationProcessorTest/testProcessMySegmentsUpdateBoundedRequest()", - "SseNotificationProcessorTest/testProcessMySegmentsUpdateKeyListRequest()", - "SseNotificationProcessorTest/testProcessMySegmentsUpdateUnboundedFetchRequest()", - "SseNotificationProcessorTest/testProcessSplitKill()", - "SseNotificationProcessorTest/testProcessSplitKillException()", - "SseNotificationProcessorTest/testProcessSplitKillNullJson()", - "SseNotificationProcessorTest/testProcessSplitUpdate()", - "SseNotificationProcessorTest/testProcessSplitUpdateException()", - "SseNotificationProcessorTest/testProcessSplitUpdateNullJson()", + "SseNotificationProcessorTest\/testProcessMyLargeSegmentsUpdateKeyListRequest()", + "SseNotificationProcessorTest\/testProcessMySegmentsUpdateBoundedRequest()", + "SseNotificationProcessorTest\/testProcessMySegmentsUpdateKeyListRequest()", + "SseNotificationProcessorTest\/testProcessMySegmentsUpdateUnboundedFetchRequest()", + "SseNotificationProcessorTest\/testProcessSplitKill()", + "SseNotificationProcessorTest\/testProcessSplitKillException()", + "SseNotificationProcessorTest\/testProcessSplitKillNullJson()", + "SseNotificationProcessorTest\/testProcessSplitUpdate()", + "SseNotificationProcessorTest\/testProcessSplitUpdateException()", + "SseNotificationProcessorTest\/testProcessSplitUpdateNullJson()", "StreamingAuthFail4xxTest", - "StreamingAuthFail4xxTest/testInit()", + "StreamingAuthFail4xxTest\/testInit()", "StreamingAuthFail5xxTest", - "StreamingAuthFail5xxTest/testInit()", + "StreamingAuthFail5xxTest\/testInit()", "StreamingBgReconnectTest", "StreamingConnFail5xxTest", - "StreamingConnFail5xxTest/testInit()", + "StreamingConnFail5xxTest\/testInit()", "StreamingControlResetTest", - "StreamingControlResetTest/testStreamingReset()", + "StreamingControlResetTest\/testStreamingReset()", "StreamingControlTest", - "StreamingControlTest/testControl()", + "StreamingControlTest\/testControl()", "StreamingDelaytTest", - "StreamingDelaytTest/testDelayOnReconnect()", - "StreamingDelaytTest/testDelayOnReconnectStress()", - "StreamingDelaytTest/testNoStreamingDelay()", - "StreamingDelaytTest/testStreamingDelay()", + "StreamingDelaytTest\/testDelayOnReconnect()", + "StreamingDelaytTest\/testDelayOnReconnectStress()", + "StreamingDelaytTest\/testNoStreamingDelay()", + "StreamingDelaytTest\/testStreamingDelay()", "StreamingDisabledTest", - "StreamingDisabledTest/testOccupancy()", + "StreamingDisabledTest\/testOccupancy()", "StreamingInitTest", - "StreamingInitTest/testInit()", - "StreamingInitTest/testInitWithoutSpec()", + "StreamingInitTest\/testInit()", + "StreamingInitTest\/testInitWithoutSpec()", "StreamingMySegmentsSyncTest", - "StreamingMySegmentsSyncTest/testInit()", + "StreamingMySegmentsSyncTest\/testInit()", "StreamingNoReconectWhenPollingTest", - "StreamingNoReconectWhenPollingTest/testShouldNoReconnect()", - "StreamingNoReconectWhenPollingTest/testShouldReconnect()", + "StreamingNoReconectWhenPollingTest\/testShouldNoReconnect()", + "StreamingNoReconectWhenPollingTest\/testShouldReconnect()", "StreamingOccupancyTest", - "StreamingOccupancyTest/testOccupancy()", + "StreamingOccupancyTest\/testOccupancy()", "StreamingSplitKillTest", - "StreamingSplitKillTest/testSplitKill()", + "StreamingSplitKillTest\/testSplitKill()", "StreamingSplitsSyncTest", - "StreamingSplitsSyncTest/testInit()", + "StreamingSplitsSyncTest\/testInit()", "SyncConfigTest", - "SyncConfigTest/testEmptyFilterValuesDiscarded()", - "SyncConfigTest/testFilterByPrefix()", - "SyncConfigTest/testFilterName()", - "SyncConfigTest/testInvalidFilterValuesDiscarded()", - "SyncConfigTest/testSyncBuilder()", + "SyncConfigTest\/testEmptyFilterValuesDiscarded()", + "SyncConfigTest\/testFilterByPrefix()", + "SyncConfigTest\/testFilterName()", + "SyncConfigTest\/testInvalidFilterValuesDiscarded()", + "SyncConfigTest\/testSyncBuilder()", "SyncDictionaryCollectionWrapperTest", - "SyncDictionaryCollectionWrapperTest/testAllValues()", - "SyncDictionaryCollectionWrapperTest/testInitialAppend()", - "SyncDictionaryCollectionWrapperTest/testRemoveValue()", + "SyncDictionaryCollectionWrapperTest\/testAllValues()", + "SyncDictionaryCollectionWrapperTest\/testInitialAppend()", + "SyncDictionaryCollectionWrapperTest\/testRemoveValue()", "SyncDictionarySingleWrapperTest", - "SyncDictionarySingleWrapperTest/testAllValues()", - "SyncDictionarySingleWrapperTest/testInitialSetup()", - "SyncDictionarySingleWrapperTest/testRemoveAllValues()", - "SyncDictionarySingleWrapperTest/testRemoveValue()", + "SyncDictionarySingleWrapperTest\/testAllValues()", + "SyncDictionarySingleWrapperTest\/testInitialSetup()", + "SyncDictionarySingleWrapperTest\/testRemoveAllValues()", + "SyncDictionarySingleWrapperTest\/testRemoveValue()", "SyncGuardianTest", - "SyncGuardianTest/testIncreaseAndDecreasePeriodValidation()", - "SyncGuardianTest/testMinPeriodValidation()", - "SyncGuardianTest/testMustNoSyncDisabled()", - "SyncGuardianTest/testMustNoSyncStreamingDisabled()", - "SyncGuardianTest/testMustNotSyncWhenTimeDoesNotExceed()", - "SyncGuardianTest/testMustSyncWhenTimeExceeds()", - "SyncGuardianTest/testUpdatePeriodValidation()", + "SyncGuardianTest\/testIncreaseAndDecreasePeriodValidation()", + "SyncGuardianTest\/testMinPeriodValidation()", + "SyncGuardianTest\/testMustNoSyncDisabled()", + "SyncGuardianTest\/testMustNoSyncStreamingDisabled()", + "SyncGuardianTest\/testMustNotSyncWhenTimeDoesNotExceed()", + "SyncGuardianTest\/testMustSyncWhenTimeExceeds()", + "SyncGuardianTest\/testUpdatePeriodValidation()", "SyncManagerTest", - "SyncManagerTest/testCredentialPinnedFailNotification()", - "SyncManagerTest/testPauseResume()", - "SyncManagerTest/testPushDelayReceived()", - "SyncManagerTest/testPushNonRetryableError()", - "SyncManagerTest/testPushReset()", - "SyncManagerTest/testPushRetryableError()", - "SyncManagerTest/testPushSubsystemDownReceived()", - "SyncManagerTest/testPushSubsystemUpReceived()", - "SyncManagerTest/testStartSingleModeStreamingDisabled()", - "SyncManagerTest/testStartSingleModeStreamingEnabled()", - "SyncManagerTest/testStartStreamingDisabled()", - "SyncManagerTest/testStartStreamingEnabled()", - "SyncManagerTest/testStop()", - "SyncManagerTest/testSyncExecutedReceived()", + "SyncManagerTest\/testCredentialPinnedFailNotification()", + "SyncManagerTest\/testPauseResume()", + "SyncManagerTest\/testPushDelayReceived()", + "SyncManagerTest\/testPushNonRetryableError()", + "SyncManagerTest\/testPushReset()", + "SyncManagerTest\/testPushRetryableError()", + "SyncManagerTest\/testPushSubsystemDownReceived()", + "SyncManagerTest\/testPushSubsystemUpReceived()", + "SyncManagerTest\/testStartSingleModeStreamingDisabled()", + "SyncManagerTest\/testStartSingleModeStreamingEnabled()", + "SyncManagerTest\/testStartStreamingDisabled()", + "SyncManagerTest\/testStartStreamingEnabled()", + "SyncManagerTest\/testStop()", + "SyncManagerTest\/testSyncExecutedReceived()", "SyncPostBgTest", - "SyncPostBgTest/testSync()", + "SyncPostBgTest\/testSync()", "SyncUpdateWorkerTest", - "SyncUpdateWorkerTest/testSplitKillWorker()", - "SyncUpdateWorkerTest/testSplitUpdateWorkerNoPayload()", - "SyncUpdateWorkerTest/testSplitUpdateWorkerWithPayloadChangeNumberBigger()", - "SyncUpdateWorkerTest/testSplitUpdateWorkerWithPayloadChangeNumberSmaller()", + "SyncUpdateWorkerTest\/testSplitKillWorker()", + "SyncUpdateWorkerTest\/testSplitUpdateWorkerNoPayload()", + "SyncUpdateWorkerTest\/testSplitUpdateWorkerWithPayloadChangeNumberBigger()", + "SyncUpdateWorkerTest\/testSplitUpdateWorkerWithPayloadChangeNumberSmaller()", "SynchronizerTest", - "SynchronizerTest/testDestroy()", - "SynchronizerTest/testDisableEventsEndpoint()", - "SynchronizerTest/testDisableSdkEndpoint()", - "SynchronizerTest/testDisableTelemetry()", - "SynchronizerTest/testEventPush()", - "SynchronizerTest/testFlush()", - "SynchronizerTest/testForceMySegmentsSyncSingleModeEnabled()", - "SynchronizerTest/testForceSynchronizeMySegments()", - "SynchronizerTest/testLoadSplits()", - "SynchronizerTest/testStartByKey()", - "SynchronizerTest/testStartPeriodicFetching()", - "SynchronizerTest/testStartPeriodicFetchingSingleModeEnabled()", - "SynchronizerTest/testStartPeriodicRecordingUserData()", - "SynchronizerTest/testStartRecordingTelemetry()", - "SynchronizerTest/testStopPeriodicFetching()", - "SynchronizerTest/testStopRecordingTelemetry()", - "SynchronizerTest/testStopRecordingUserData()", - "SynchronizerTest/testSynchronizeMySegments()", - "SynchronizerTest/testSynchronizeSplitsWithChangeNumber()", - "SynchronizerTest/testUpdateSplitsSingleModeEnabled()", + "SynchronizerTest\/testDestroy()", + "SynchronizerTest\/testDisableEventsEndpoint()", + "SynchronizerTest\/testDisableSdkEndpoint()", + "SynchronizerTest\/testDisableTelemetry()", + "SynchronizerTest\/testEventPush()", + "SynchronizerTest\/testFlush()", + "SynchronizerTest\/testForceMySegmentsSyncSingleModeEnabled()", + "SynchronizerTest\/testForceSynchronizeMySegments()", + "SynchronizerTest\/testLoadSplits()", + "SynchronizerTest\/testStartByKey()", + "SynchronizerTest\/testStartPeriodicFetching()", + "SynchronizerTest\/testStartPeriodicFetchingSingleModeEnabled()", + "SynchronizerTest\/testStartPeriodicRecordingUserData()", + "SynchronizerTest\/testStartRecordingTelemetry()", + "SynchronizerTest\/testStopPeriodicFetching()", + "SynchronizerTest\/testStopRecordingTelemetry()", + "SynchronizerTest\/testStopRecordingUserData()", + "SynchronizerTest\/testSynchronizeMySegments()", + "SynchronizerTest\/testSynchronizeSplitsWithChangeNumber()", + "SynchronizerTest\/testUpdateSplitsSingleModeEnabled()", "TelemetryConfigRecorderWorkerTests", - "TelemetryConfigRecorderWorkerTests/testFailedAttemptLimit()", - "TelemetryConfigRecorderWorkerTests/testFailedAttemptLimitExceded()", - "TelemetryConfigRecorderWorkerTests/testSendSuccess()", + "TelemetryConfigRecorderWorkerTests\/testFailedAttemptLimit()", + "TelemetryConfigRecorderWorkerTests\/testFailedAttemptLimitExceded()", + "TelemetryConfigRecorderWorkerTests\/testSendSuccess()", "TelemetryIntegrationTest", - "TelemetryIntegrationTest/testConfig()", - "TelemetryIntegrationTest/testConfigTelemetry()", - "TelemetryIntegrationTest/testHttpError()", - "TelemetryIntegrationTest/testRuntimeAndSyncTelemetry()", - "TelemetryIntegrationTest/testStreamingAblyErrorAndSessionLength()", - "TelemetryIntegrationTest/testStreamingTelemetry()", + "TelemetryIntegrationTest\/testConfig()", + "TelemetryIntegrationTest\/testConfigTelemetry()", + "TelemetryIntegrationTest\/testHttpError()", + "TelemetryIntegrationTest\/testRuntimeAndSyncTelemetry()", + "TelemetryIntegrationTest\/testStreamingAblyErrorAndSessionLength()", + "TelemetryIntegrationTest\/testStreamingTelemetry()", "TelemetryStatsRecorderWorkerTests", - "TelemetryStatsRecorderWorkerTests/testConcurrentFlush()", - "TelemetryStatsRecorderWorkerTests/testEndpointNotReachable()", - "TelemetryStatsRecorderWorkerTests/testFailedAttemptLimit()", - "TelemetryStatsRecorderWorkerTests/testFailedAttemptLimitExceded()", - "TelemetryStatsRecorderWorkerTests/testSendSuccess()", + "TelemetryStatsRecorderWorkerTests\/testConcurrentFlush()", + "TelemetryStatsRecorderWorkerTests\/testEndpointNotReachable()", + "TelemetryStatsRecorderWorkerTests\/testFailedAttemptLimit()", + "TelemetryStatsRecorderWorkerTests\/testFailedAttemptLimitExceded()", + "TelemetryStatsRecorderWorkerTests\/testSendSuccess()", "TelemetryTest", - "TelemetryTest/testEvaluationRecording()", - "TelemetryTest/testNonReadyEvaluation()", - "TelemetryTest/testOnlyReadyTime()", - "TelemetryTest/testReadyTime()", + "TelemetryTest\/testEvaluationRecording()", + "TelemetryTest\/testNonReadyEvaluation()", + "TelemetryTest\/testOnlyReadyTime()", + "TelemetryTest\/testReadyTime()", "TelmetrySynchronizerTest", - "TelmetrySynchronizerTest/testStart()", - "TelmetrySynchronizerTest/testStop()", - "TelmetrySynchronizerTest/testSyncConfig()", - "TelmetrySynchronizerTest/testSyncStats()", + "TelmetrySynchronizerTest\/testStart()", + "TelmetrySynchronizerTest\/testStop()", + "TelmetrySynchronizerTest\/testSyncConfig()", + "TelmetrySynchronizerTest\/testSyncStats()", "TimersManagerTest", - "TimersManagerTest/testAddAndCancelTimer()", - "TimersManagerTest/testAddTimer()", + "TimersManagerTest\/testAddAndCancelTimer()", + "TimersManagerTest\/testAddTimer()", "TlsPinningCheckerTests", - "TlsPinningCheckerTests/testAppleEcSpki()", - "TlsPinningCheckerTests/testEc256r1Spki()", - "TlsPinningCheckerTests/testEc384r1Spki()", - "TlsPinningCheckerTests/testEc521r1Spki()", - "TlsPinningCheckerTests/testInvalidChallengeMethod()", - "TlsPinningCheckerTests/testInvalidChallengeNoSecTrust()", - "TlsPinningCheckerTests/testRsa3072Spki()", - "TlsPinningCheckerTests/testRsa4096Spki()", - "TlsPinningCheckerTests/testUntrustedCertificate()", + "TlsPinningCheckerTests\/testAppleEcSpki()", + "TlsPinningCheckerTests\/testEc256r1Spki()", + "TlsPinningCheckerTests\/testEc384r1Spki()", + "TlsPinningCheckerTests\/testEc521r1Spki()", + "TlsPinningCheckerTests\/testInvalidChallengeMethod()", + "TlsPinningCheckerTests\/testInvalidChallengeNoSecTrust()", + "TlsPinningCheckerTests\/testRsa3072Spki()", + "TlsPinningCheckerTests\/testRsa4096Spki()", + "TlsPinningCheckerTests\/testUntrustedCertificate()", "TrackTest", - "TrackTest/test()", + "TrackTest\/test()", "TreatmentManagerTest", - "TreatmentManagerTest/testBasicEvaluationNoConfig()", - "TreatmentManagerTest/testBasicEvaluationWithConfig()", - "TreatmentManagerTest/testBasicEvaluations()", - "TreatmentManagerTest/testBasicEvaluationsBySets()", - "TreatmentManagerTest/testEmptyKey()", - "TreatmentManagerTest/testEmptySplits()", - "TreatmentManagerTest/testKilledSplitWithConfig()", - "TreatmentManagerTest/testLongKey()", - "TreatmentManagerTest/testMergedAttributes()", - "TreatmentManagerTest/testNoStoredAttributes()", - "TreatmentManagerTest/testOnlyStoredAttributes()", - "TreatmentManagerTest/testRuntimeProducers()", + "TreatmentManagerTest\/testBasicEvaluationNoConfig()", + "TreatmentManagerTest\/testBasicEvaluationWithConfig()", + "TreatmentManagerTest\/testBasicEvaluations()", + "TreatmentManagerTest\/testBasicEvaluationsBySets()", + "TreatmentManagerTest\/testEmptyKey()", + "TreatmentManagerTest\/testEmptySplits()", + "TreatmentManagerTest\/testKilledSplitWithConfig()", + "TreatmentManagerTest\/testLongKey()", + "TreatmentManagerTest\/testMergedAttributes()", + "TreatmentManagerTest\/testNoStoredAttributes()", + "TreatmentManagerTest\/testOnlyStoredAttributes()", + "TreatmentManagerTest\/testRuntimeProducers()", "UniqueKeyDaoTest", - "UniqueKeyDaoTest/testDataIsEncryptedInDb()", - "UniqueKeyDaoTest/testInsertGetAes128Cbc()", - "UniqueKeyDaoTest/testInsertGetPlainText()", - "UniqueKeyDaoTest/testLoadOutdatedAes128Cbc()", - "UniqueKeyDaoTest/testLoadOutdatedPlainText()", - "UniqueKeyDaoTest/testUpdateAes128Cbc()", - "UniqueKeyDaoTest/testUpdateAndIncrementCountAes128Cbc()", - "UniqueKeyDaoTest/testUpdateAndIncrementCountPlainText()", - "UniqueKeyDaoTest/testUpdatePlainText()", + "UniqueKeyDaoTest\/testDataIsEncryptedInDb()", + "UniqueKeyDaoTest\/testInsertGetAes128Cbc()", + "UniqueKeyDaoTest\/testInsertGetPlainText()", + "UniqueKeyDaoTest\/testLoadOutdatedAes128Cbc()", + "UniqueKeyDaoTest\/testLoadOutdatedPlainText()", + "UniqueKeyDaoTest\/testUpdateAes128Cbc()", + "UniqueKeyDaoTest\/testUpdateAndIncrementCountAes128Cbc()", + "UniqueKeyDaoTest\/testUpdateAndIncrementCountPlainText()", + "UniqueKeyDaoTest\/testUpdatePlainText()", "UniqueKeyTrackerTest", - "UniqueKeyTrackerTest/testTrackAndSave()", - "UniqueKeysRecorderCountWorkerTests/testFailToSendSome()", - "UniqueKeysRecorderCountWorkerTests/testSendOneImpression()", - "UniqueKeysRecorderCountWorkerTests/testSendSuccess()", + "UniqueKeyTrackerTest\/testTrackAndSave()", + "UniqueKeysRecorderCountWorkerTests\/testFailToSendSome()", + "UniqueKeysRecorderCountWorkerTests\/testSendOneImpression()", + "UniqueKeysRecorderCountWorkerTests\/testSendSuccess()", "UnsupportedMatcherIntegrationTest", - "UnsupportedMatcherIntegrationTest/testFeatureFlagWithUnsupportedMatcherIsPresentInManager()", - "UnsupportedMatcherIntegrationTest/testGetTreatmentForUnsupportedMatcherFeatureFlagReturnsControl()", - "UnsupportedMatcherIntegrationTest/testGetTreatmentWithConfigForUnsupportedMatcherFeatureFlagReturnsControl()", - "UnsupportedMatcherIntegrationTest/testImpressionInListenerHasUnsupportedLabel()", - "UnsupportedMatcherIntegrationTest/testStoredImpressionHasUnsupportedLabel()", + "UnsupportedMatcherIntegrationTest\/testFeatureFlagWithUnsupportedMatcherIsPresentInManager()", + "UnsupportedMatcherIntegrationTest\/testGetTreatmentForUnsupportedMatcherFeatureFlagReturnsControl()", + "UnsupportedMatcherIntegrationTest\/testGetTreatmentWithConfigForUnsupportedMatcherFeatureFlagReturnsControl()", + "UnsupportedMatcherIntegrationTest\/testImpressionInListenerHasUnsupportedLabel()", + "UnsupportedMatcherIntegrationTest\/testStoredImpressionHasUnsupportedLabel()", "UserConsentManagerTest", - "UserConsentManagerTest/testSetDeclined()", - "UserConsentManagerTest/testSetGranted()", - "UserConsentManagerTest/testSetUnknown()", + "UserConsentManagerTest\/testSetDeclined()", + "UserConsentManagerTest\/testSetGranted()", + "UserConsentManagerTest\/testSetUnknown()", "UserConsentModeDebugTest", - "UserConsentModeDebugTest/testUserConsentDeclined()", - "UserConsentModeDebugTest/testUserConsentUnknownThenDeclined()", - "UserConsentModeDebugTest/testUserConsentUnknownThenGranted()", + "UserConsentModeDebugTest\/testUserConsentDeclined()", + "UserConsentModeDebugTest\/testUserConsentUnknownThenDeclined()", + "UserConsentModeDebugTest\/testUserConsentUnknownThenGranted()", "UserConsentModeNoneTest", - "UserConsentModeNoneTest/testUserConsentDeclined()", - "UserConsentModeNoneTest/testUserConsentGranted()", - "UserConsentModeNoneTest/testUserConsentUnknownThenDeclined()", - "UserConsentModeNoneTest/testUserConsentUnknownThenGranted()", + "UserConsentModeNoneTest\/testUserConsentDeclined()", + "UserConsentModeNoneTest\/testUserConsentGranted()", + "UserConsentModeNoneTest\/testUserConsentUnknownThenDeclined()", + "UserConsentModeNoneTest\/testUserConsentUnknownThenGranted()", "UserConsentModeOptimizedTest", - "UserConsentModeOptimizedTest/testUserConsentDeclined()", - "UserConsentModeOptimizedTest/testUserConsentGranted()", - "UserConsentModeOptimizedTest/testUserConsentUnknownThenGranted()", + "UserConsentModeOptimizedTest\/testUserConsentDeclined()", + "UserConsentModeOptimizedTest\/testUserConsentGranted()", + "UserConsentModeOptimizedTest\/testUserConsentUnknownThenGranted()", "UserKeyEncondingTest", - "UserKeyEncondingTest/testKey()", + "UserKeyEncondingTest\/testKey()", "VersionTest", - "VersionTest/testFactoryVersion()", - "VersionTest/testSemanticVersion()" - ] + "VersionTest\/testFactoryVersion()", + "VersionTest\/testSemanticVersion()" + ], + "target" : { + "containerPath" : "container:Split.xcodeproj", + "identifier" : "592C6AA4211B6C99002D120C", + "name" : "SplitTests" + } } ], - "version": 1 -} \ No newline at end of file + "version" : 1 +} diff --git a/SplitTests/Resources/splits.json b/SplitTests/Resources/splits.json index 113ecb633..67500d653 100644 --- a/SplitTests/Resources/splits.json +++ b/SplitTests/Resources/splits.json @@ -1,6 +1,10 @@ [{ "trafficTypeName": "custom", "name": "SAMPLE_FEATURE0", + "prerequisites": [ + { "n": "flag1", "ts": ["on","v1"] }, + { "n": "flag2", "ts": ["off"] } + ], "trafficAllocation": 100, "trafficAllocationSeed": 1595297106, "seed": -1332540447, diff --git a/SplitTests/SplitManagerTest.swift b/SplitTests/SplitManagerTest.swift index 196388d69..cbb67d60a 100644 --- a/SplitTests/SplitManagerTest.swift +++ b/SplitTests/SplitManagerTest.swift @@ -4,7 +4,6 @@ // // Created by Sebastian Arrubia on 4/24/18. // Copyright © 2018 CocoaPods. All rights reserved. -// import Foundation import XCTest @@ -28,9 +27,6 @@ class SplitManagerTest: XCTestCase { manager = DefaultSplitManager(splitsStorage: splitsStorage) } - override func tearDown() { - } - func testInitialSplitLoaded() { let splits = manager.splitNames @@ -55,7 +51,7 @@ class SplitManagerTest: XCTestCase { XCTAssertNil(splitNotExistent, "Non existent feature flag should be nil") } - func testSplitInfo(){ + func testSplitInfo() { let split0 = manager.split(featureName: "sample_feature0") let treatments0 = split0?.treatments @@ -69,7 +65,8 @@ class SplitManagerTest: XCTestCase { XCTAssertEqual(split0?.sets?.sorted(), ["set1", "set2"]) XCTAssertNotNil(split0?.configs) XCTAssertTrue(split0?.impressionsDisabled ?? false, "Split0 track impressions") - + XCTAssertEqual(split0?.prerequisites?.first?.n, "flag1") + XCTAssertEqual(split0?.prerequisites?.first?.ts?[1], "v1") XCTAssertEqual(treatments0?.count, 6, "Split0 treatment count") XCTAssertEqual(treatments0?.sorted().joined(separator: ",").lowercased(), "t1_0,t2_0,t3_0,t4_0,t5_0,t6_0", "Split0 treatment names") From a85f547b77a18cf1e7ea380ea4a2c87ceceeed55 Mon Sep 17 00:00:00 2001 From: Martin Cardozo Date: Thu, 22 May 2025 16:50:09 -0300 Subject: [PATCH 08/46] Test added --- SplitTests/SplitManagerTest.swift | 1 + 1 file changed, 1 insertion(+) diff --git a/SplitTests/SplitManagerTest.swift b/SplitTests/SplitManagerTest.swift index cbb67d60a..86f0b8444 100644 --- a/SplitTests/SplitManagerTest.swift +++ b/SplitTests/SplitManagerTest.swift @@ -78,6 +78,7 @@ class SplitManagerTest: XCTestCase { XCTAssertTrue(split1?.killed ?? false, "Split1 killed") XCTAssertEqual(split1?.trafficType, "custom1", "Split1 traffic type") XCTAssertEqual(split1?.defaultTreatment, "off", "Split1 traffic type") + XCTAssertEqual(split1?.prerequisites, []) XCTAssertNotNil(split1?.configs) XCTAssertEqual(0, split1?.configs?.count) XCTAssertEqual(treatments1?.count, 6, "Split1 treatment count") From c2fcd9754a029066879067215c6f5c7def1f7c21 Mon Sep 17 00:00:00 2001 From: Martin Cardozo Date: Fri, 23 May 2025 01:51:02 -0300 Subject: [PATCH 09/46] Improved tests --- SplitTests/SplitDTOTests.swift | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/SplitTests/SplitDTOTests.swift b/SplitTests/SplitDTOTests.swift index b8130dcb6..37795eb1d 100644 --- a/SplitTests/SplitDTOTests.swift +++ b/SplitTests/SplitDTOTests.swift @@ -6,18 +6,28 @@ import XCTest class SplitDTOTests: XCTestCase { + func testPrerequisitesField() { + + let expectedFlags = ["flag1","flag2"] + let expectedTreatments = [["on","v1"],["off"]] + // Happy case var data = json.data(using: .utf8)! var result = try? TargetingRulesChangeDecoder.decode(from: data) - XCTAssertEqual(result?.featureFlags.splits.first?.prerequisites?.first?.n, "flag1") - XCTAssertEqual(result?.featureFlags.splits.first?.prerequisites?.first?.ts?[1], "v1") + + for i in 0.. Date: Fri, 23 May 2025 01:56:40 -0300 Subject: [PATCH 10/46] Improved tests --- SplitTests/SplitDTOTests.swift | 31 +++++++++++++++---------------- 1 file changed, 15 insertions(+), 16 deletions(-) diff --git a/SplitTests/SplitDTOTests.swift b/SplitTests/SplitDTOTests.swift index 37795eb1d..9e067448c 100644 --- a/SplitTests/SplitDTOTests.swift +++ b/SplitTests/SplitDTOTests.swift @@ -6,35 +6,34 @@ import XCTest class SplitDTOTests: XCTestCase { + let expectedFlags = ["flag1","flag2"] + let expectedTreatments = [["on","v1"],["off"]] func testPrerequisitesField() { - let expectedFlags = ["flag1","flag2"] - let expectedTreatments = [["on","v1"],["off"]] - // Happy case - var data = json.data(using: .utf8)! - var result = try? TargetingRulesChangeDecoder.decode(from: data) + var data = correctJson + var decoded = try? TargetingRulesChangeDecoder.decode(from: data.data(using: .utf8)!) - for i in 0.. Date: Fri, 23 May 2025 10:42:25 -0300 Subject: [PATCH 11/46] Split tests --- SplitTests/SplitDTOTests.swift | 29 +++++++++++++++-------------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/SplitTests/SplitDTOTests.swift b/SplitTests/SplitDTOTests.swift index 9e067448c..8f7ae9528 100644 --- a/SplitTests/SplitDTOTests.swift +++ b/SplitTests/SplitDTOTests.swift @@ -6,30 +6,31 @@ import XCTest class SplitDTOTests: XCTestCase { - let expectedFlags = ["flag1","flag2"] - let expectedTreatments = [["on","v1"],["off"]] - - func testPrerequisitesField() { + func testCorrectPrerequisites() { + + let expectedFlags = ["flag1","flag2"] + let expectedTreatments = [["on","v1"],["off"]] - // Happy case - var data = correctJson - var decoded = try? TargetingRulesChangeDecoder.decode(from: data.data(using: .utf8)!) + let data = correctJson + let decoded = try? TargetingRulesChangeDecoder.decode(from: data.data(using: .utf8)!) for i in 0.. Date: Fri, 23 May 2025 10:47:02 -0300 Subject: [PATCH 12/46] Cleanup --- SplitTests/Resources/splitchanges_1.json | 1 - 1 file changed, 1 deletion(-) diff --git a/SplitTests/Resources/splitchanges_1.json b/SplitTests/Resources/splitchanges_1.json index 9312ba6f0..420ef8e70 100644 --- a/SplitTests/Resources/splitchanges_1.json +++ b/SplitTests/Resources/splitchanges_1.json @@ -2533,7 +2533,6 @@ { "trafficTypeName":"account", "name":"TEST_SETS_1", - "preQER":[], "trafficAllocation":59, "trafficAllocationSeed":-2108186082, "seed":-1947050785, From 9d7e0cb8c719bc3f7c5de29fd6460bfcd564c7f4 Mon Sep 17 00:00:00 2001 From: Martin Cardozo Date: Fri, 23 May 2025 10:55:39 -0300 Subject: [PATCH 13/46] Fixed test --- SplitTests/SplitManagerTest.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SplitTests/SplitManagerTest.swift b/SplitTests/SplitManagerTest.swift index 86f0b8444..ef1e3e4cd 100644 --- a/SplitTests/SplitManagerTest.swift +++ b/SplitTests/SplitManagerTest.swift @@ -66,7 +66,7 @@ class SplitManagerTest: XCTestCase { XCTAssertNotNil(split0?.configs) XCTAssertTrue(split0?.impressionsDisabled ?? false, "Split0 track impressions") XCTAssertEqual(split0?.prerequisites?.first?.n, "flag1") - XCTAssertEqual(split0?.prerequisites?.first?.ts?[1], "v1") + XCTAssertEqual(split0?.prerequisites?.first?.ts?.sorted(), ["on", "v1"]) XCTAssertEqual(treatments0?.count, 6, "Split0 treatment count") XCTAssertEqual(treatments0?.sorted().joined(separator: ",").lowercased(), "t1_0,t2_0,t3_0,t4_0,t5_0,t6_0", "Split0 treatment names") From 57a5bf8479112c3d0dc42c8399337cfedf5a1304 Mon Sep 17 00:00:00 2001 From: Martin Cardozo Date: Fri, 23 May 2025 10:58:02 -0300 Subject: [PATCH 14/46] New Matcher --- Split.xcodeproj/project.pbxproj | 7 ++++++- Split/Matchers/PrerequisitesMatcher.swift | 14 ++++++++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) create mode 100644 Split/Matchers/PrerequisitesMatcher.swift diff --git a/Split.xcodeproj/project.pbxproj b/Split.xcodeproj/project.pbxproj index 422681e92..b9cc7606c 100644 --- a/Split.xcodeproj/project.pbxproj +++ b/Split.xcodeproj/project.pbxproj @@ -354,7 +354,8 @@ 59FB7C3C2203795F00ECC96A /* LocalhostSplitsParser.swift in Sources */ = {isa = PBXBuildFile; fileRef = 59FB7C3B2203795F00ECC96A /* LocalhostSplitsParser.swift */; }; 59FB7C3E22037B9400ECC96A /* SpaceDelimitedLocalhostSplitsParser.swift in Sources */ = {isa = PBXBuildFile; fileRef = 59FB7C3D22037B9400ECC96A /* SpaceDelimitedLocalhostSplitsParser.swift */; }; 5B91B8392DDE4A3B000510F0 /* SplitDTOTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B91B8382DDE4A30000510F0 /* SplitDTOTests.swift */; }; - 5BF52DEF2DDF884D00FEDAFE /* RuleBasedSegment.swift in Sources */ = {isa = PBXBuildFile; fileRef = C539CAB52D88C1F10050C732 /* RuleBasedSegment.swift */; }; + 5BF52DF62DE0B60700FEDAFE /* PrerequisitesMatcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BF52DF52DE0B60300FEDAFE /* PrerequisitesMatcher.swift */; }; + 5BF52DF72DE0B60700FEDAFE /* PrerequisitesMatcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BF52DF52DE0B60300FEDAFE /* PrerequisitesMatcher.swift */; }; 9500D9922C56F9BA00383593 /* HostDomainFilterTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9500D9912C56F9BA00383593 /* HostDomainFilterTests.swift */; }; 9500D9A92C59297400383593 /* HostDomainFilter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9500D9A82C59297400383593 /* HostDomainFilter.swift */; }; 9500D9AA2C59382000383593 /* HostDomainFilter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9500D9A82C59297400383593 /* HostDomainFilter.swift */; }; @@ -1555,6 +1556,7 @@ 59FB7C3B2203795F00ECC96A /* LocalhostSplitsParser.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocalhostSplitsParser.swift; sourceTree = ""; }; 59FB7C3D22037B9400ECC96A /* SpaceDelimitedLocalhostSplitsParser.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SpaceDelimitedLocalhostSplitsParser.swift; sourceTree = ""; }; 5B91B8382DDE4A30000510F0 /* SplitDTOTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SplitDTOTests.swift; sourceTree = ""; }; + 5BF52DF52DE0B60300FEDAFE /* PrerequisitesMatcher.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PrerequisitesMatcher.swift; sourceTree = ""; }; 9500D9912C56F9BA00383593 /* HostDomainFilterTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HostDomainFilterTests.swift; sourceTree = ""; }; 9500D9A82C59297400383593 /* HostDomainFilter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HostDomainFilter.swift; sourceTree = ""; }; 9500D9AC2C5A918300383593 /* split_cache_v5.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = split_cache_v5.xcdatamodel; sourceTree = ""; }; @@ -2423,6 +2425,7 @@ 3B6DEECB20EA6AE30067435E /* Matchers */ = { isa = PBXGroup; children = ( + 5BF52DF52DE0B60300FEDAFE /* PrerequisitesMatcher.swift */, C539CACD2D8A08C00050C732 /* InRuleBasedSegmentMatcher.swift */, C5A3F5222BED4EC5009FACD3 /* Semver */, 3B6DEECC20EA6AE30067435E /* AllKeysMatcher.swift */, @@ -4233,6 +4236,7 @@ 95C1600D27D28CF4008562E3 /* PersistentAttributesStorage.swift in Sources */, 95726075262F548500350CCA /* SplitBgSynchronizer.swift in Sources */, 95C1600B27D28CB8008562E3 /* OneKeyPersistentAttributesStorage.swift in Sources */, + 5BF52DF72DE0B60700FEDAFE /* PrerequisitesMatcher.swift in Sources */, 9519A91127D6935700278AEC /* ByKeyAttributesStorage.swift in Sources */, 59D84BD622158A24003DA248 /* LocalhostSplitFactory.swift in Sources */, 3B6DEF4C20EA6AE50067435E /* SplitBase.swift in Sources */, @@ -4824,6 +4828,7 @@ 95B02CC628D0BDC10030EC8B /* CompressionUtil.swift in Sources */, 95B02CC728D0BDC10030EC8B /* Stopwatch.swift in Sources */, 95B02CC828D0BDC10030EC8B /* ConcurrentArrayQueue.swift in Sources */, + 5BF52DF62DE0B60700FEDAFE /* PrerequisitesMatcher.swift in Sources */, 95B02CC928D0BDC10030EC8B /* SynchronizedList.swift in Sources */, 95B02CCA28D0BDC10030EC8B /* ConcurrentSet.swift in Sources */, C539CADC2D93229D0050C732 /* EvaluationOptions.swift in Sources */, diff --git a/Split/Matchers/PrerequisitesMatcher.swift b/Split/Matchers/PrerequisitesMatcher.swift new file mode 100644 index 000000000..f2cd06d7a --- /dev/null +++ b/Split/Matchers/PrerequisitesMatcher.swift @@ -0,0 +1,14 @@ +// Created by Martin Cardozo on 22/05/2025. +// Copyright © 2025 Split. All rights reserved. + +import Foundation + +class PrerequisitesMatcher: BaseMatcher, MatcherProtocol { + + private var prerequisites: [Prerequisite]? + //private var storage: Storage? + + func evaluate(values: EvalValues, context: EvalContext?) -> Bool { + + } +} From af86270e2565d9c421ff987e779a3b72ab885557 Mon Sep 17 00:00:00 2001 From: Martin Cardozo Date: Mon, 26 May 2025 11:48:05 -0300 Subject: [PATCH 15/46] Spliting branch --- Split/Engine/Constants/SplitConstants.swift | 4 +- Split/Engine/Evaluator.swift | 171 ++- Split/Impressions/ImpressionsConstants.swift | 1 + Split/Matchers/BaseMatcher.swift | 13 +- Split/Matchers/PrerequisitesMatcher.swift | 25 +- SplitTests/Murmur3HashingTest.swift | 2 +- SplitiOSHalf_1.xctestplan | 1096 +++++++++--------- 7 files changed, 662 insertions(+), 650 deletions(-) diff --git a/Split/Engine/Constants/SplitConstants.swift b/Split/Engine/Constants/SplitConstants.swift index e92b25e0b..910ef121d 100644 --- a/Split/Engine/Constants/SplitConstants.swift +++ b/Split/Engine/Constants/SplitConstants.swift @@ -7,6 +7,6 @@ import Foundation -struct SplitConstants { - static let control: String = "control" +struct SplitConstants { + static let control = "control" } diff --git a/Split/Engine/Evaluator.swift b/Split/Engine/Evaluator.swift index be5e8393c..6eb1f2a8a 100644 --- a/Split/Engine/Evaluator.swift +++ b/Split/Engine/Evaluator.swift @@ -3,113 +3,74 @@ // Split // // Created by Natalia Stele on 11/14/17. -// import Foundation -// swiftlint:disable function_body_length -struct EvaluationResult { - var treatment: String - var label: String - var changeNumber: Int64? - var configuration: String? - var impressionsDisabled: Bool - - init(treatment: String, label: String, changeNumber: Int64? = nil, configuration: String? = nil, - impressionsDisabled: Bool = false) { - self.treatment = treatment - self.label = label - self.changeNumber = changeNumber - self.configuration = configuration - self.impressionsDisabled = impressionsDisabled - } -} - -struct EvalValues { - let matchValue: Any? - let matchingKey: String - let bucketingKey: String? - let attributes: [String: Any]? - - init(matchValue: Any?, matchingKey: String, bucketingKey: String? = nil, attributes: [String: Any]? = nil) { - self.matchValue = matchValue - self.matchingKey = matchingKey - self.bucketingKey = bucketingKey - self.attributes = attributes - } -} - -// Components needed -struct EvalContext { - let evaluator: Evaluator? - let mySegmentsStorage: MySegmentsStorage? - let myLargeSegmentsStorage: MySegmentsStorage? - let ruleBasedSegmentsStorage: RuleBasedSegmentsStorage? -} protocol Evaluator { - func evalTreatment(matchingKey: String, bucketingKey: String?, - splitName: String, attributes: [String: Any]?) throws -> EvaluationResult + func evalTreatment(matchingKey: String, bucketingKey: String?, splitName: String, attributes: [String: Any]?) throws -> EvaluationResult } class DefaultEvaluator: Evaluator { + // Internal for testing purposes var splitter: SplitterProtocol = Splitter.shared + private let splitsStorage: SplitsStorage private let mySegmentsStorage: MySegmentsStorage private let myLargeSegmentsStorage: MySegmentsStorage? private let ruleBasedSegmentsStorage: RuleBasedSegmentsStorage? - init(splitsStorage: SplitsStorage, - mySegmentsStorage: MySegmentsStorage, - myLargeSegmentsStorage: MySegmentsStorage? = nil, - ruleBasedSegmentsStorage: RuleBasedSegmentsStorage? = nil) { + init(splitsStorage: SplitsStorage, mySegmentsStorage: MySegmentsStorage, myLargeSegmentsStorage: MySegmentsStorage? = nil, ruleBasedSegmentsStorage: RuleBasedSegmentsStorage? = nil) { self.splitsStorage = splitsStorage self.mySegmentsStorage = mySegmentsStorage self.myLargeSegmentsStorage = myLargeSegmentsStorage self.ruleBasedSegmentsStorage = ruleBasedSegmentsStorage } - func evalTreatment(matchingKey: String, bucketingKey: String?, - splitName: String, attributes: [String: Any]?) throws -> EvaluationResult { + func evalTreatment(matchingKey: String, bucketingKey: String?, splitName: String, attributes: [String: Any]?) throws -> EvaluationResult { - guard let split = splitsStorage.get(name: splitName), - split.status != .archived else { - Logger.w("The feature flag definition for '\(splitName)' has not been found") - return EvaluationResult(treatment: SplitConstants.control, label: ImpressionsConstants.splitNotFound) + // 1. Guarantee Split exists & is active + guard let split = splitsStorage.get(name: splitName), split.status != .archived else { + Logger.w("The feature flag definition for '\(splitName)' not found") + return EvaluationResult(treatment: SplitConstants.control, label: ImpressionsConstants.splitNotFound) } - + + // 2. Extract neccesary info let changeNumber = split.changeNumber ?? -1 let defaultTreatment = split.defaultTreatment ?? SplitConstants.control - if let killed = split.killed, killed { + let bucketKey = selectBucketKey(matchingKey: matchingKey, bucketingKey: bucketingKey) + let values = EvalValues(matchValue: matchingKey, matchingKey: matchingKey, bucketingKey: bucketKey, attributes: attributes) + + // 3. Guarantee is not killed + guard let killed = split.killed, !killed else { + return EvaluationResult(treatment: defaultTreatment, label: ImpressionsConstants.killed, changeNumber: changeNumber, + configuration: split.configurations?[defaultTreatment], impressionsDisabled: split.isImpressionsDisabled()) + } + + // 4. Evaluate Prerequisites + if !PrerequisitesMatcher().evaluate(values: values, context: getContext()) { return EvaluationResult(treatment: defaultTreatment, - label: ImpressionsConstants.killed, + label: ImpressionsConstants.prerequisitesNotMet, changeNumber: changeNumber, configuration: split.configurations?[defaultTreatment], impressionsDisabled: split.isImpressionsDisabled()) } - var inRollOut: Bool = false - var splitAlgo: Algorithm = Algorithm.legacy - - if let rawAlgo = split.algo, let algo = Algorithm.init(rawValue: rawAlgo) { - splitAlgo = algo + + // 5. Evaluate core conditions + guard let conditions = split.conditions, let trafficAllocationSeed = split.trafficAllocationSeed, let seed = split.seed else { + return EvaluationResult(treatment: SplitConstants.control, label: ImpressionsConstants.exception) } - - let bucketKey = selectBucketKey(matchingKey: matchingKey, bucketingKey: bucketingKey) - - guard let conditions: [Condition] = split.conditions, - let trafficAllocationSeed = split.trafficAllocationSeed, - let seed = split.seed else { - return EvaluationResult(treatment: SplitConstants.control, label: ImpressionsConstants.exception) - } - + var splitAlgo: Algorithm = Algorithm.legacy + if let rawAlgo = split.algo, let algo = Algorithm.init(rawValue: rawAlgo) { splitAlgo = algo } do { + var inRollOut: Bool = false for condition in conditions { + + // Traffic Allocation if !inRollOut && condition.conditionType == ConditionType.rollout { if let trafficAllocation = split.trafficAllocation, trafficAllocation < 100 { - let bucket: Int64 = splitter.getBucket(seed: trafficAllocationSeed, - key: bucketKey, - algo: splitAlgo) + let bucket: Int64 = splitter.getBucket(seed: trafficAllocationSeed, key: bucketKey, algo: splitAlgo) if bucket > trafficAllocation { return EvaluationResult(treatment: defaultTreatment, label: ImpressionsConstants.notInSplit, @@ -121,49 +82,79 @@ class DefaultEvaluator: Evaluator { } } - // Returns the first condition that match. - let values = EvalValues(matchValue: matchingKey, matchingKey: matchingKey, - bucketingKey: bucketKey, attributes: attributes) + // Core conditions (returns the first one that match) if try condition.match(values: values, context: getContext()) { let key: Key = Key(matchingKey: matchingKey, bucketingKey: bucketKey) - let treatment = splitter.getTreatment(key: key, seed: seed, attributes: attributes, - partions: condition.partitions, algo: splitAlgo) + let treatment = splitter.getTreatment(key: key, seed: seed, attributes: attributes, partions: condition.partitions, algo: splitAlgo) + return EvaluationResult(treatment: treatment, label: condition.label!, changeNumber: changeNumber, configuration: split.configurations?[treatment], impressionsDisabled: split.isImpressionsDisabled()) } } - let result = EvaluationResult(treatment: defaultTreatment, + return EvaluationResult(treatment: defaultTreatment, label: ImpressionsConstants.noConditionMatched, changeNumber: changeNumber, configuration: split.configurations?[defaultTreatment], impressionsDisabled: split.isImpressionsDisabled()) - return result } catch EvaluatorError.matcherNotFound { - Logger.e("The matcher has not been found") - return EvaluationResult(treatment: SplitConstants.control, label: ImpressionsConstants.matcherNotFound, - changeNumber: changeNumber, impressionsDisabled: split.isImpressionsDisabled()) + Logger.e("Matcher not found") + return EvaluationResult(treatment: SplitConstants.control, label: ImpressionsConstants.matcherNotFound, changeNumber: changeNumber, impressionsDisabled: split.isImpressionsDisabled()) } } private func getContext() -> EvalContext { - return EvalContext(evaluator: self, - mySegmentsStorage: mySegmentsStorage, - myLargeSegmentsStorage: myLargeSegmentsStorage, - ruleBasedSegmentsStorage: ruleBasedSegmentsStorage) + EvalContext(evaluator: self, mySegmentsStorage: mySegmentsStorage,myLargeSegmentsStorage: myLargeSegmentsStorage, ruleBasedSegmentsStorage: ruleBasedSegmentsStorage) } private func selectBucketKey(matchingKey: String, bucketingKey: String?) -> String { - if let key = bucketingKey, !key.isEmpty() { - return key - } + if let bucketingKey = bucketingKey { return bucketingKey } + return matchingKey } } private extension Split { func isImpressionsDisabled() -> Bool { - return self.impressionsDisabled ?? false + impressionsDisabled ?? false + } +} + +// MARK: Components needed +struct EvalValues { + let matchValue: Any? + let matchingKey: String + let bucketingKey: String? + let attributes: [String: Any]? + + init(matchValue: Any?, matchingKey: String, bucketingKey: String? = nil, attributes: [String: Any]? = nil) { + self.matchValue = matchValue + self.matchingKey = matchingKey + self.bucketingKey = bucketingKey + self.attributes = attributes + } +} + +struct EvalContext { + let evaluator: Evaluator? + let mySegmentsStorage: MySegmentsStorage? + let myLargeSegmentsStorage: MySegmentsStorage? + let ruleBasedSegmentsStorage: RuleBasedSegmentsStorage? +} + +struct EvaluationResult { + var treatment: String + var label: String + var changeNumber: Int64? + var configuration: String? + var impressionsDisabled: Bool + + init(treatment: String, label: String, changeNumber: Int64? = nil, configuration: String? = nil, impressionsDisabled: Bool = false) { + self.treatment = treatment + self.label = label + self.changeNumber = changeNumber + self.configuration = configuration + self.impressionsDisabled = impressionsDisabled } } diff --git a/Split/Impressions/ImpressionsConstants.swift b/Split/Impressions/ImpressionsConstants.swift index 608de4053..1400466f5 100644 --- a/Split/Impressions/ImpressionsConstants.swift +++ b/Split/Impressions/ImpressionsConstants.swift @@ -16,4 +16,5 @@ struct ImpressionsConstants { static let exception: String = "exception" static let notReady: String = "not ready" static let unsupportedMatcherType: String = "targeting rule type unsupported by sdk" + static let prerequisitesNotMet = "prerequisites not met" } diff --git a/Split/Matchers/BaseMatcher.swift b/Split/Matchers/BaseMatcher.swift index c41a2b634..ae8788ddc 100644 --- a/Split/Matchers/BaseMatcher.swift +++ b/Split/Matchers/BaseMatcher.swift @@ -3,7 +3,6 @@ // Split // // Created by Natalia Stele on 11/5/17. -// import Foundation @@ -13,27 +12,25 @@ class BaseMatcher: NSObject { var attribute: String? var type: MatcherType? - init(negate: Bool? = nil, - attribute: String? = nil, type: MatcherType? = nil) { - + init(negate: Bool? = nil, attribute: String? = nil, type: MatcherType? = nil) { self.negate = negate self.attribute = attribute self.type = type } func isNegate() -> Bool { - return self.negate ?? false + negate ?? false } func getAttribute() -> String? { - return self.attribute + attribute } func getMatcherType() -> MatcherType { - return self.type! + type! } func matcherHasAttribute() -> Bool { - return self.attribute != nil + attribute != nil } } diff --git a/Split/Matchers/PrerequisitesMatcher.swift b/Split/Matchers/PrerequisitesMatcher.swift index f2cd06d7a..93171bd77 100644 --- a/Split/Matchers/PrerequisitesMatcher.swift +++ b/Split/Matchers/PrerequisitesMatcher.swift @@ -6,9 +6,30 @@ import Foundation class PrerequisitesMatcher: BaseMatcher, MatcherProtocol { private var prerequisites: [Prerequisite]? - //private var storage: Storage? + + init(prerequisites: [Prerequisite]? = nil) { + self.prerequisites = prerequisites + } + // This evaluation passes JUST if -all- prerequisite are met func evaluate(values: EvalValues, context: EvalContext?) -> Bool { - + guard let prerequisites = prerequisites, !prerequisites.isEmpty, let evaluator = context?.evaluator else { return true } + + for prerequisite in prerequisites { + guard let splitName = prerequisite.n, let treaments = prerequisite.ts, !treaments.isEmpty else { continue } + + do { + let eval = try evaluator.evalTreatment(matchingKey: values.matchingKey, bucketingKey: values.bucketingKey, splitName: splitName, attributes: nil) + + if treaments.contains(values.matchValue as? String ?? "") { + return false + } + } catch { + Logger.e("Error evaluating condition in PrerequisitesMatcher: \(error)") + return false + } + } + + return true } } diff --git a/SplitTests/Murmur3HashingTest.swift b/SplitTests/Murmur3HashingTest.swift index 314fcb1fe..b2ad52c6b 100644 --- a/SplitTests/Murmur3HashingTest.swift +++ b/SplitTests/Murmur3HashingTest.swift @@ -17,7 +17,7 @@ class Murmur3HashingTest: XCTestCase { "murmur3-sample-v4", "murmur3-sample-v3", "murmur3-sample-double-treatment-users"] - for file in files { + for file in files { var data = CsvHelper.readDataFromCSV(sourceClass: self, fileName: file) data = CsvHelper.cleanRows(file: data!) let csvRows = CsvHelper.csv(data: data!) diff --git a/SplitiOSHalf_1.xctestplan b/SplitiOSHalf_1.xctestplan index c552e5d97..7dbdc75e3 100644 --- a/SplitiOSHalf_1.xctestplan +++ b/SplitiOSHalf_1.xctestplan @@ -1,667 +1,669 @@ { - "configurations": [ + "configurations" : [ { - "id": "4F2A5094-0E71-4794-9717-92482220A968", - "name": "Common", - "options": {} + "id" : "4F2A5094-0E71-4794-9717-92482220A968", + "name" : "Common", + "options" : { + + } }, { - "enabled": false, - "id": "00529E1C-25A9-4843-8676-E48C4769483F", - "name": "WatchOS", - "options": { - "targetForVariableExpansion": { - "containerPath": "container:Split.xcodeproj", - "identifier": "95B02CA328D0BD790030EC8B", - "name": "WatchOS" + "enabled" : false, + "id" : "00529E1C-25A9-4843-8676-E48C4769483F", + "name" : "WatchOS", + "options" : { + "targetForVariableExpansion" : { + "containerPath" : "container:Split.xcodeproj", + "identifier" : "95B02CA328D0BD790030EC8B", + "name" : "WatchOS" } } } ], - "defaultOptions": { - "codeCoverage": false, - "environmentVariableEntries": [ + "defaultOptions" : { + "codeCoverage" : false, + "environmentVariableEntries" : [ { - "key": "com.apple.CoreData.ConcurrencyDebug", - "value": "1" + "key" : "com.apple.CoreData.ConcurrencyDebug", + "value" : "1" } ], - "targetForVariableExpansion": { - "containerPath": "container:Split.xcodeproj", - "identifier": "3B6DEE5920EA6A4E0067435E", - "name": "Split" + "targetForVariableExpansion" : { + "containerPath" : "container:Split.xcodeproj", + "identifier" : "3B6DEE5920EA6A4E0067435E", + "name" : "Split" } }, - "testTargets": [ + "testTargets" : [ { - "skippedTests": [ + "skippedTests" : [ "AnyValueValidatorTests", - "AnyValueValidatorTests/testInvalidListValues()", - "AnyValueValidatorTests/testInvalidPrimitiveValues()", - "AnyValueValidatorTests/testValidListValues()", - "AnyValueValidatorTests/testValidPrimitiveValues()", + "AnyValueValidatorTests\/testInvalidListValues()", + "AnyValueValidatorTests\/testInvalidPrimitiveValues()", + "AnyValueValidatorTests\/testValidListValues()", + "AnyValueValidatorTests\/testValidPrimitiveValues()", "ApiKeyValidatorTests", - "ApiKeyValidatorTests/testEmptyKey()", - "ApiKeyValidatorTests/testNull()", - "ApiKeyValidatorTests/testValid()", + "ApiKeyValidatorTests\/testEmptyKey()", + "ApiKeyValidatorTests\/testNull()", + "ApiKeyValidatorTests\/testValid()", "AttributesDaoTest", - "AttributesDaoTest/testDataIsEncryptedInDb()", - "AttributesDaoTest/testGetInvalidKeyAes128Cbc()", - "AttributesDaoTest/testGetInvalidKeyPlainText()", - "AttributesDaoTest/testRemoveAllAes128Cbc()", - "AttributesDaoTest/testRemoveAllPlainText()", - "AttributesDaoTest/testUpdateGetAes128Cbc()", - "AttributesDaoTest/testUpdateGetPlainText()", + "AttributesDaoTest\/testDataIsEncryptedInDb()", + "AttributesDaoTest\/testGetInvalidKeyAes128Cbc()", + "AttributesDaoTest\/testGetInvalidKeyPlainText()", + "AttributesDaoTest\/testRemoveAllAes128Cbc()", + "AttributesDaoTest\/testRemoveAllPlainText()", + "AttributesDaoTest\/testUpdateGetAes128Cbc()", + "AttributesDaoTest\/testUpdateGetPlainText()", "AttributesEvaluationTest", - "AttributesEvaluationTest/testAttributesPersistentedCorrectly()", - "AttributesEvaluationTest/testEvaluationPrecedence()", - "AttributesEvaluationTest/testPersistenceDisabled()", - "AttributesEvaluationTest/testPersistentEvalNoAttributesSeveralOperations()", + "AttributesEvaluationTest\/testAttributesPersistentedCorrectly()", + "AttributesEvaluationTest\/testEvaluationPrecedence()", + "AttributesEvaluationTest\/testPersistenceDisabled()", + "AttributesEvaluationTest\/testPersistentEvalNoAttributesSeveralOperations()", "AttributesStorageTests", - "AttributesStorageTests/testClear()", - "AttributesStorageTests/testGetAttributesAfterLoad()", - "AttributesStorageTests/testNoLoaded()", - "AttributesStorageTests/testRemove()", - "AttributesStorageTests/testUpdateAttributes()", - "AttributesStorageTests/testUpdateEmptyAttributes()", + "AttributesStorageTests\/testClear()", + "AttributesStorageTests\/testGetAttributesAfterLoad()", + "AttributesStorageTests\/testNoLoaded()", + "AttributesStorageTests\/testRemove()", + "AttributesStorageTests\/testUpdateAttributes()", + "AttributesStorageTests\/testUpdateEmptyAttributes()", "Base64UtilsTest", - "Base64UtilsTest/testBasicUrlEncoded()", - "Base64UtilsTest/testEmpty()", - "Base64UtilsTest/testNil()", - "Base64UtilsTest/testRealToken()", + "Base64UtilsTest\/testBasicUrlEncoded()", + "Base64UtilsTest\/testEmpty()", + "Base64UtilsTest\/testNil()", + "Base64UtilsTest\/testRealToken()", "BetweenSemverMatcherTest", - "BetweenSemverMatcherTest/testGeneralMatches()", - "BetweenSemverMatcherTest/testMatchShouldReturnFalseWhenGreater()", - "BetweenSemverMatcherTest/testMatchShouldReturnFalseWhenLess()", - "BetweenSemverMatcherTest/testMatchShouldReturnTrueWhenBetween()", - "BetweenSemverMatcherTest/testMatchWithMetadataShouldReturnFalseWhenGreater()", - "BetweenSemverMatcherTest/testMatchWithMetadataShouldReturnFalseWhenLess()", - "BetweenSemverMatcherTest/testMatchWithMetadataShouldReturnTrueWhenBetween()", - "BetweenSemverMatcherTest/testMatchWithNullEndTargetShouldReturnFalse()", - "BetweenSemverMatcherTest/testMatchWithNullKeyShouldReturnFalse()", - "BetweenSemverMatcherTest/testMatchWithNullStartTargetShouldReturnFalse()", - "BetweenSemverMatcherTest/testMatchWithPreReleaseShouldReturnFalseWhenGreater()", - "BetweenSemverMatcherTest/testMatchWithPreReleaseShouldReturnFalseWhenLess()", - "BetweenSemverMatcherTest/testMatchWithPreReleaseShouldReturnTrueWhenBetween()", + "BetweenSemverMatcherTest\/testGeneralMatches()", + "BetweenSemverMatcherTest\/testMatchShouldReturnFalseWhenGreater()", + "BetweenSemverMatcherTest\/testMatchShouldReturnFalseWhenLess()", + "BetweenSemverMatcherTest\/testMatchShouldReturnTrueWhenBetween()", + "BetweenSemverMatcherTest\/testMatchWithMetadataShouldReturnFalseWhenGreater()", + "BetweenSemverMatcherTest\/testMatchWithMetadataShouldReturnFalseWhenLess()", + "BetweenSemverMatcherTest\/testMatchWithMetadataShouldReturnTrueWhenBetween()", + "BetweenSemverMatcherTest\/testMatchWithNullEndTargetShouldReturnFalse()", + "BetweenSemverMatcherTest\/testMatchWithNullKeyShouldReturnFalse()", + "BetweenSemverMatcherTest\/testMatchWithNullStartTargetShouldReturnFalse()", + "BetweenSemverMatcherTest\/testMatchWithPreReleaseShouldReturnFalseWhenGreater()", + "BetweenSemverMatcherTest\/testMatchWithPreReleaseShouldReturnFalseWhenLess()", + "BetweenSemverMatcherTest\/testMatchWithPreReleaseShouldReturnTrueWhenBetween()", "BlockingQueueTest", - "BlockingQueueTest/testAddAndTake()", - "BlockingQueueTest/testInterrupt()", + "BlockingQueueTest\/testAddAndTake()", + "BlockingQueueTest\/testInterrupt()", "BucketSplitTest", - "BucketSplitTest/testMultiClientBuckets()", + "BucketSplitTest\/testMultiClientBuckets()", "ByKeyAttributesStorageTests", - "ByKeyAttributesStorageTests/testClear()", - "ByKeyAttributesStorageTests/testGetAttributesAfterLoad()", - "ByKeyAttributesStorageTests/testNoLoaded()", - "ByKeyAttributesStorageTests/testRemove()", - "ByKeyAttributesStorageTests/testUpdateAttributes()", - "ByKeyAttributesStorageTests/testUpdateEmptyAttributes()", + "ByKeyAttributesStorageTests\/testClear()", + "ByKeyAttributesStorageTests\/testGetAttributesAfterLoad()", + "ByKeyAttributesStorageTests\/testNoLoaded()", + "ByKeyAttributesStorageTests\/testRemove()", + "ByKeyAttributesStorageTests\/testUpdateAttributes()", + "ByKeyAttributesStorageTests\/testUpdateEmptyAttributes()", "ByKeyFacadeTest", - "ByKeyFacadeTest/testAppendRemove()", - "ByKeyFacadeTest/testDestroy()", - "ByKeyFacadeTest/testForceSync()", - "ByKeyFacadeTest/testLoadDataFromCache()", - "ByKeyFacadeTest/testNoPeriodicSync()", - "ByKeyFacadeTest/testPeriodicStartPauseResumeStop()", - "ByKeyFacadeTest/testPeriodicStartStop()", - "ByKeyFacadeTest/testStartSyncForKey()", - "ByKeyFacadeTest/testStartSyncForKeyPolling()", - "ByKeyFacadeTest/testSynchronize()", + "ByKeyFacadeTest\/testAppendRemove()", + "ByKeyFacadeTest\/testDestroy()", + "ByKeyFacadeTest\/testForceSync()", + "ByKeyFacadeTest\/testLoadDataFromCache()", + "ByKeyFacadeTest\/testNoPeriodicSync()", + "ByKeyFacadeTest\/testPeriodicStartPauseResumeStop()", + "ByKeyFacadeTest\/testPeriodicStartStop()", + "ByKeyFacadeTest\/testStartSyncForKey()", + "ByKeyFacadeTest\/testStartSyncForKeyPolling()", + "ByKeyFacadeTest\/testSynchronize()", "ByKeyMySegmentsStorageTests", - "ByKeyMySegmentsStorageTests/testGetMySegmentsAfterLoad()", - "ByKeyMySegmentsStorageTests/testNoLoaded()", - "ByKeyMySegmentsStorageTests/testUpdateEmptySegments()", - "ByKeyMySegmentsStorageTests/testUpdateSegments()", + "ByKeyMySegmentsStorageTests\/testGetMySegmentsAfterLoad()", + "ByKeyMySegmentsStorageTests\/testNoLoaded()", + "ByKeyMySegmentsStorageTests\/testUpdateEmptySegments()", + "ByKeyMySegmentsStorageTests\/testUpdateSegments()", "CdnByPassTest", - "CdnByPassTest/testInit()", - "CdnByPassTest/testInitWithoutSpec()", + "CdnByPassTest\/testInit()", + "CdnByPassTest\/testInitWithoutSpec()", "CertificatePinningConfigTests", - "CertificatePinningConfigTests/testAddCertificateAndHashes()", - "CertificatePinningConfigTests/testAddWrongCertificate()", - "CertificatePinningConfigTests/testAddWrongSpkiCertificate()", - "CertificatePinningConfigTests/testAddWrongStringHash()", - "CertificatePinningConfigTests/testAddWrongStringHashFormatHash()", - "CertificatePinningConfigTests/testAddWrongStringHashType()", + "CertificatePinningConfigTests\/testAddCertificateAndHashes()", + "CertificatePinningConfigTests\/testAddWrongCertificate()", + "CertificatePinningConfigTests\/testAddWrongSpkiCertificate()", + "CertificatePinningConfigTests\/testAddWrongStringHash()", + "CertificatePinningConfigTests\/testAddWrongStringHashFormatHash()", + "CertificatePinningConfigTests\/testAddWrongStringHashType()", "CipherTest", - "CipherTest/testBasicEncryptDecrypt()", - "CipherTest/testEventEncryptDecrypt()", - "CipherTest/testImpressionEncryptDecrypt()", - "CipherTest/testJsonSplitEncryptDecrypt()", - "CipherTest/testMySegmentsEncryptDecrypt()", - "CipherTest/testVeryLongTextEncryptDecrypt()", + "CipherTest\/testBasicEncryptDecrypt()", + "CipherTest\/testEventEncryptDecrypt()", + "CipherTest\/testImpressionEncryptDecrypt()", + "CipherTest\/testJsonSplitEncryptDecrypt()", + "CipherTest\/testMySegmentsEncryptDecrypt()", + "CipherTest\/testVeryLongTextEncryptDecrypt()", "ComputeProcessTest", - "ComputeProcessTest/testMultiProcess()", - "ComputeProcessTest/testOneLessThanMinProcess()", - "ComputeProcessTest/testOneProcessEqualsMin()", - "ComputeProcessTest/testTwoProcess()", - "ComputeProcessTest/testTwoProcessEdge()", + "ComputeProcessTest\/testMultiProcess()", + "ComputeProcessTest\/testOneLessThanMinProcess()", + "ComputeProcessTest\/testOneProcessEqualsMin()", + "ComputeProcessTest\/testTwoProcess()", + "ComputeProcessTest\/testTwoProcessEdge()", "ConcurrentSetTests", - "ConcurrentSetTests/testDeleteAll()", - "ConcurrentSetTests/testInsert()", - "ConcurrentSetTests/testSet()", - "ConcurrentSetTests/testTakeAll()", + "ConcurrentSetTests\/testDeleteAll()", + "ConcurrentSetTests\/testInsert()", + "ConcurrentSetTests\/testSet()", + "ConcurrentSetTests\/testTakeAll()", "ConfigTest", - "ConfigTest/testImpressionsModeEmpty()", - "ConfigTest/testImpressionsModeInvalid()", - "ConfigTest/testImpressionsModedebug()", - "ConfigTest/testImpressionsModenone()", - "ConfigTest/testImpressionsModeoptimized()", + "ConfigTest\/testImpressionsModeEmpty()", + "ConfigTest\/testImpressionsModeInvalid()", + "ConfigTest\/testImpressionsModedebug()", + "ConfigTest\/testImpressionsModenone()", + "ConfigTest\/testImpressionsModeoptimized()", "CountsRecorderCountWorkerTests", - "CountsRecorderCountWorkerTests/testFailToSendSome()", - "CountsRecorderCountWorkerTests/testSendNoImpressions()", - "CountsRecorderCountWorkerTests/testSendOneImpression()", - "CountsRecorderCountWorkerTests/testSendSuccess()", + "CountsRecorderCountWorkerTests\/testFailToSendSome()", + "CountsRecorderCountWorkerTests\/testSendNoImpressions()", + "CountsRecorderCountWorkerTests\/testSendOneImpression()", + "CountsRecorderCountWorkerTests\/testSendSuccess()", "CredentialPinParserTests", - "CredentialPinParserTests/testPinsForHost()", + "CredentialPinParserTests\/testPinsForHost()", "DbCipherTest", - "DbCipherTest/testEncryptDecryptDb()", + "DbCipherTest\/testEncryptDecryptDb()", "DbForDifferentApiKeysTest", - "DbForDifferentApiKeysTest/testInitialization()", + "DbForDifferentApiKeysTest\/testInitialization()", "DecompressionTest", - "DecompressionTest/testGzipAllHeaders()", - "DecompressionTest/testGzipCompressionMethodHeader()", - "DecompressionTest/testGzipHeaderComments()", - "DecompressionTest/testGzipHeaderCrc16()", - "DecompressionTest/testGzipHeaderExtraField()", - "DecompressionTest/testGzipHeaderFileName()", - "DecompressionTest/testGzipIncorrectHeader()", - "DecompressionTest/testHeaderFlag0()", - "DecompressionTest/testLoremIpsumGzip()", - "DecompressionTest/testLoremIpsumZlib()", - "DecompressionTest/testZlibCompressionMethodHeader()", + "DecompressionTest\/testGzipAllHeaders()", + "DecompressionTest\/testGzipCompressionMethodHeader()", + "DecompressionTest\/testGzipHeaderComments()", + "DecompressionTest\/testGzipHeaderCrc16()", + "DecompressionTest\/testGzipHeaderExtraField()", + "DecompressionTest\/testGzipHeaderFileName()", + "DecompressionTest\/testGzipIncorrectHeader()", + "DecompressionTest\/testHeaderFlag0()", + "DecompressionTest\/testLoremIpsumGzip()", + "DecompressionTest\/testLoremIpsumZlib()", + "DecompressionTest\/testZlibCompressionMethodHeader()", "EndpointFactoryTest", - "EndpointFactoryTest/testMySegmentsEndpoint()", - "EndpointFactoryTest/testMySegmentsEndpointSlashKeyEncoding()", - "EndpointFactoryTest/testRecordEventsEndpoint()", - "EndpointFactoryTest/testRecordImpressionsEndpoint()", - "EndpointFactoryTest/testSplitChangesEndpoint()", - "EndpointFactoryTest/testStreamingAuthEndpoint()", - "EndpointFactoryTest/testStreamingEndpoint()", - "EndpointFactoryTest/testTelemetryConfigEndpoint()", - "EndpointFactoryTest/testTelemetryUsageEndpoint()", + "EndpointFactoryTest\/testMySegmentsEndpoint()", + "EndpointFactoryTest\/testMySegmentsEndpointSlashKeyEncoding()", + "EndpointFactoryTest\/testRecordEventsEndpoint()", + "EndpointFactoryTest\/testRecordImpressionsEndpoint()", + "EndpointFactoryTest\/testSplitChangesEndpoint()", + "EndpointFactoryTest\/testStreamingAuthEndpoint()", + "EndpointFactoryTest\/testStreamingEndpoint()", + "EndpointFactoryTest\/testTelemetryConfigEndpoint()", + "EndpointFactoryTest\/testTelemetryUsageEndpoint()", "EndpointTest", - "EndpointTest/testDefaultEndpointBuild()", - "EndpointTest/testHeadersEndpointBuild()", - "EndpointTest/testPostEndpointBuild()", + "EndpointTest\/testDefaultEndpointBuild()", + "EndpointTest\/testHeadersEndpointBuild()", + "EndpointTest\/testPostEndpointBuild()", "EqualToSemverMatcherTest", - "EqualToSemverMatcherTest/testGeneralUnsuccessfulMatches()", - "EqualToSemverMatcherTest/testMatchShouldReturnFalseWhenKeyIsNull()", - "EqualToSemverMatcherTest/testMatchShouldReturnFalseWhenPatchDiffers()", - "EqualToSemverMatcherTest/testMatchShouldReturnFalseWhenTargetIsNull()", - "EqualToSemverMatcherTest/testMatchShouldReturnTrueWhenVersionsAreEqual()", - "EqualToSemverMatcherTest/testMatchWithMetadataShouldReturnFalseWhenVersionsDiffer()", - "EqualToSemverMatcherTest/testMatchWithMetadataShouldReturnTrueWhenVersionsAreEqual()", - "EqualToSemverMatcherTest/testMatchWithPreReleaseShouldReturnFalseWhenVersionsDiffer()", - "EqualToSemverMatcherTest/testMatchWithPreReleaseShouldReturnTrueWhenVersionsAreEqual()", + "EqualToSemverMatcherTest\/testGeneralUnsuccessfulMatches()", + "EqualToSemverMatcherTest\/testMatchShouldReturnFalseWhenKeyIsNull()", + "EqualToSemverMatcherTest\/testMatchShouldReturnFalseWhenPatchDiffers()", + "EqualToSemverMatcherTest\/testMatchShouldReturnFalseWhenTargetIsNull()", + "EqualToSemverMatcherTest\/testMatchShouldReturnTrueWhenVersionsAreEqual()", + "EqualToSemverMatcherTest\/testMatchWithMetadataShouldReturnFalseWhenVersionsDiffer()", + "EqualToSemverMatcherTest\/testMatchWithMetadataShouldReturnTrueWhenVersionsAreEqual()", + "EqualToSemverMatcherTest\/testMatchWithPreReleaseShouldReturnFalseWhenVersionsDiffer()", + "EqualToSemverMatcherTest\/testMatchWithPreReleaseShouldReturnTrueWhenVersionsAreEqual()", "EvaluatorTests", - "EvaluatorTests/testAlgoLegacy()", - "EvaluatorTests/testAlgoMurmur3()", - "EvaluatorTests/testAlgoNull()", - "EvaluatorTests/testAllocation1Percent()", - "EvaluatorTests/testBrokenSplit()", - "EvaluatorTests/testDefaultRule()", - "EvaluatorTests/testDefaultTreatment()", - "EvaluatorTests/testDefaultTreatmentFacundo()", - "EvaluatorTests/testEqualsToSetConfigTreatment()", - "EvaluatorTests/testEqualsToSetNoConfigTreatment()", - "EvaluatorTests/testInLargeSegmentWhitelist()", - "EvaluatorTests/testInSegmentTestKey()", - "EvaluatorTests/testInSegmentsRule1()", - "EvaluatorTests/testKilledSplit()", - "EvaluatorTests/testMatchesStringNoConfigTreatment()", - "EvaluatorTests/testMissingDefaultRule()", - "EvaluatorTests/testNotInLargeSegmentWhitelist()", - "EvaluatorTests/testNotInSplit()", - "EvaluatorTests/testWhitelisted()", - "EvaluatorTests/testWhitelistedOff()", - "EvaluatorTests/testsTrafficAllocation50DefaultRule50()", + "EvaluatorTests\/testAlgoLegacy()", + "EvaluatorTests\/testAlgoMurmur3()", + "EvaluatorTests\/testAlgoNull()", + "EvaluatorTests\/testAllocation1Percent()", + "EvaluatorTests\/testBrokenSplit()", + "EvaluatorTests\/testDefaultRule()", + "EvaluatorTests\/testDefaultTreatment()", + "EvaluatorTests\/testDefaultTreatmentFacundo()", + "EvaluatorTests\/testEqualsToSetConfigTreatment()", + "EvaluatorTests\/testEqualsToSetNoConfigTreatment()", + "EvaluatorTests\/testInLargeSegmentWhitelist()", + "EvaluatorTests\/testInSegmentTestKey()", + "EvaluatorTests\/testInSegmentsRule1()", + "EvaluatorTests\/testKilledSplit()", + "EvaluatorTests\/testMatchesStringNoConfigTreatment()", + "EvaluatorTests\/testMissingDefaultRule()", + "EvaluatorTests\/testNotInLargeSegmentWhitelist()", + "EvaluatorTests\/testNotInSplit()", + "EvaluatorTests\/testWhitelisted()", + "EvaluatorTests\/testWhitelistedOff()", + "EvaluatorTests\/testsTrafficAllocation50DefaultRule50()", "EventDTOJsonTest", - "EventDTOJsonTest/testBasic()", - "EventDTOJsonTest/testEncode()", - "EventDTOJsonTest/testNonNumber()", - "EventDTOJsonTest/testProperties()", - "EventDTOJsonTest/testPropertiesDecodeEncode()", + "EventDTOJsonTest\/testBasic()", + "EventDTOJsonTest\/testEncode()", + "EventDTOJsonTest\/testNonNumber()", + "EventDTOJsonTest\/testProperties()", + "EventDTOJsonTest\/testPropertiesDecodeEncode()", "EventDaoTest", - "EventDaoTest/testDataIsEncryptedInDb()", - "EventDaoTest/testInsertGetAes128Cbc()", - "EventDaoTest/testInsertGetPlainText()", - "EventDaoTest/testInsertManyGetAes128Cbc()", - "EventDaoTest/testInsertManyGetPlainText()", - "EventDaoTest/testLoadOutdatedAes128Cbc()", - "EventDaoTest/testLoadOutdatedPlainText()", - "EventDaoTest/testUpdateAes128Cbc()", - "EventDaoTest/testUpdatePlainText()", + "EventDaoTest\/testDataIsEncryptedInDb()", + "EventDaoTest\/testInsertGetAes128Cbc()", + "EventDaoTest\/testInsertGetPlainText()", + "EventDaoTest\/testInsertManyGetAes128Cbc()", + "EventDaoTest\/testInsertManyGetPlainText()", + "EventDaoTest\/testLoadOutdatedAes128Cbc()", + "EventDaoTest\/testLoadOutdatedPlainText()", + "EventDaoTest\/testUpdateAes128Cbc()", + "EventDaoTest\/testUpdatePlainText()", "EventStreamParserTest", - "EventStreamParserTest/testParseColon()", - "EventStreamParserTest/testParseEmptyLineNoEnd()", - "EventStreamParserTest/testParseEnd()", - "EventStreamParserTest/testParseErrorMessage()", - "EventStreamParserTest/testParseKeepAlive()", - "EventStreamParserTest/testParseNoColon()", - "EventStreamParserTest/testParseNoFieldName()", - "EventStreamParserTest/testParseTwoColon()", + "EventStreamParserTest\/testParseColon()", + "EventStreamParserTest\/testParseEmptyLineNoEnd()", + "EventStreamParserTest\/testParseEnd()", + "EventStreamParserTest\/testParseErrorMessage()", + "EventStreamParserTest\/testParseKeepAlive()", + "EventStreamParserTest\/testParseNoColon()", + "EventStreamParserTest\/testParseNoFieldName()", + "EventStreamParserTest\/testParseTwoColon()", "EventValidatorTests", - "EventValidatorTests/testEmptyKey()", - "EventValidatorTests/testEmptyTrafficType()", - "EventValidatorTests/testEmptyType()", - "EventValidatorTests/testLongKey()", - "EventValidatorTests/testNoChachedServerAndUppercasedTrafficType()", - "EventValidatorTests/testNoChachedServerTrafficType()", - "EventValidatorTests/testNullKey()", - "EventValidatorTests/testNullTrafficType()", - "EventValidatorTests/testNullType()", - "EventValidatorTests/testTypeName()", - "EventValidatorTests/testUppercaseCharsInTrafficType()", - "EventValidatorTests/testValidEventAllValues()", - "EventValidatorTests/testValidEventNullValue()", + "EventValidatorTests\/testEmptyKey()", + "EventValidatorTests\/testEmptyTrafficType()", + "EventValidatorTests\/testEmptyType()", + "EventValidatorTests\/testLongKey()", + "EventValidatorTests\/testNoChachedServerAndUppercasedTrafficType()", + "EventValidatorTests\/testNoChachedServerTrafficType()", + "EventValidatorTests\/testNullKey()", + "EventValidatorTests\/testNullTrafficType()", + "EventValidatorTests\/testNullType()", + "EventValidatorTests\/testTypeName()", + "EventValidatorTests\/testUppercaseCharsInTrafficType()", + "EventValidatorTests\/testValidEventAllValues()", + "EventValidatorTests\/testValidEventNullValue()", "EventsRecorderWorkerTests", - "EventsRecorderWorkerTests/testFailToSendSome()", - "EventsRecorderWorkerTests/testSendNoEvents()", - "EventsRecorderWorkerTests/testSendOneEvent()", - "EventsRecorderWorkerTests/testSendSuccess()", + "EventsRecorderWorkerTests\/testFailToSendSome()", + "EventsRecorderWorkerTests\/testSendNoEvents()", + "EventsRecorderWorkerTests\/testSendOneEvent()", + "EventsRecorderWorkerTests\/testSendSuccess()", "EventsStorageTest", - "EventsStorageTest/testClear()", - "EventsStorageTest/testDisablePersistence()", - "EventsStorageTest/testEnablePersistence()", - "EventsStorageTest/testStartDisabledPersistence()", - "EventsStorageTest/testStartEnabledPersistence()", + "EventsStorageTest\/testClear()", + "EventsStorageTest\/testDisablePersistence()", + "EventsStorageTest\/testEnablePersistence()", + "EventsStorageTest\/testStartDisabledPersistence()", + "EventsStorageTest\/testStartEnabledPersistence()", "EventsSynchronizerTest", - "EventsSynchronizerTest/testDestroy()", - "EventsSynchronizerTest/testFlush()", - "EventsSynchronizerTest/testPause()", - "EventsSynchronizerTest/testPush()", - "EventsSynchronizerTest/testResume()", - "EventsSynchronizerTest/testStart()", - "EventsSynchronizerTest/testStop()", + "EventsSynchronizerTest\/testDestroy()", + "EventsSynchronizerTest\/testFlush()", + "EventsSynchronizerTest\/testPause()", + "EventsSynchronizerTest\/testPush()", + "EventsSynchronizerTest\/testResume()", + "EventsSynchronizerTest\/testStart()", + "EventsSynchronizerTest\/testStop()", "EventsTrackerTest", - "EventsTrackerTest/testTrackDisabled()", - "EventsTrackerTest/testTrackEnabled()", + "EventsTrackerTest\/testTrackDisabled()", + "EventsTrackerTest\/testTrackEnabled()", "FactoryMonitorTest", - "FactoryMonitorTest/testCountLiveFactories()", - "FactoryMonitorTest/testDeallocatedCountLiveFactories()", + "FactoryMonitorTest\/testCountLiveFactories()", + "FactoryMonitorTest\/testDeallocatedCountLiveFactories()", "FactoryRegistryTest", - "FactoryRegistryTest/testCountLiveFactories()", - "FactoryRegistryTest/testCountWithDeallocatedLiveFactories()", + "FactoryRegistryTest\/testCountLiveFactories()", + "FactoryRegistryTest\/testCountWithDeallocatedLiveFactories()", "FeatureFlagsPayloadDecoderTest", - "FeatureFlagsPayloadDecoderTest/testDecodeGzip()", - "FeatureFlagsPayloadDecoderTest/testDecodeZlib()", + "FeatureFlagsPayloadDecoderTest\/testDecodeGzip()", + "FeatureFlagsPayloadDecoderTest\/testDecodeZlib()", "FeatureFlagsSynchronizerTest", - "FeatureFlagsSynchronizerTest/testLoadSplitWhenQuerystringNamesChanges()", - "FeatureFlagsSynchronizerTest/testLoadSplitWhenQuerystringSetsChanges()", - "FeatureFlagsSynchronizerTest/testLoadSplitsClearedOnLoadBecauseNotInFilter()", - "FeatureFlagsSynchronizerTest/testLoadSplitsNoClearedOnLoad()", - "FeatureFlagsSynchronizerTest/testLoadSplitsWhenFlagsSetsAndFilterHasChangedClearsAllFeatureFlags()", - "FeatureFlagsSynchronizerTest/testLoadSplitsWhenFlagsSetsHasChangedClearsAllFeatureFlags()", - "FeatureFlagsSynchronizerTest/testStartPeriodicFetchingSingleModeEnabled()", - "FeatureFlagsSynchronizerTest/testStartPeriodicSync()", - "FeatureFlagsSynchronizerTest/testStop()", - "FeatureFlagsSynchronizerTest/testStopPeriodicSync()", - "FeatureFlagsSynchronizerTest/testSynchronizeSplits()", - "FeatureFlagsSynchronizerTest/testSynchronizeSplitsWithChangeNumber()", - "FeatureFlagsSynchronizerTest/testSynchronizeSplitsWithUriTooLong()", - "FeatureFlagsSynchronizerTest/testUpdateSplitsSingleModeEnabled()", + "FeatureFlagsSynchronizerTest\/testLoadSplitWhenQuerystringNamesChanges()", + "FeatureFlagsSynchronizerTest\/testLoadSplitWhenQuerystringSetsChanges()", + "FeatureFlagsSynchronizerTest\/testLoadSplitsClearedOnLoadBecauseNotInFilter()", + "FeatureFlagsSynchronizerTest\/testLoadSplitsNoClearedOnLoad()", + "FeatureFlagsSynchronizerTest\/testLoadSplitsWhenFlagsSetsAndFilterHasChangedClearsAllFeatureFlags()", + "FeatureFlagsSynchronizerTest\/testLoadSplitsWhenFlagsSetsHasChangedClearsAllFeatureFlags()", + "FeatureFlagsSynchronizerTest\/testStartPeriodicFetchingSingleModeEnabled()", + "FeatureFlagsSynchronizerTest\/testStartPeriodicSync()", + "FeatureFlagsSynchronizerTest\/testStop()", + "FeatureFlagsSynchronizerTest\/testStopPeriodicSync()", + "FeatureFlagsSynchronizerTest\/testSynchronizeSplits()", + "FeatureFlagsSynchronizerTest\/testSynchronizeSplitsWithChangeNumber()", + "FeatureFlagsSynchronizerTest\/testSynchronizeSplitsWithUriTooLong()", + "FeatureFlagsSynchronizerTest\/testUpdateSplitsSingleModeEnabled()", "FetchSpecificSplitsTest", - "FetchSpecificSplitsTest/testBothFilters()", - "FetchSpecificSplitsTest/testByNamesFilter()", - "FetchSpecificSplitsTest/testByPrefixFilter()", + "FetchSpecificSplitsTest\/testBothFilters()", + "FetchSpecificSplitsTest\/testByNamesFilter()", + "FetchSpecificSplitsTest\/testByPrefixFilter()", "FetcherThrottleTests", - "FetcherThrottleTests/testDelayValues()", + "FetcherThrottleTests\/testDelayValues()", "FilterBuilderTest", - "FilterBuilderTest/testBasicByNameQueryString()", - "FilterBuilderTest/testBasicBySetQueryString()", - "FilterBuilderTest/testBySetAndByNameQueryString()", - "FilterBuilderTest/testFilterByNamesValuesDeduptedAndGrouped()", - "FilterBuilderTest/testFilterBySetsValuesDeduptedAndGrouped()", - "FilterBuilderTest/testMaxByNameFilterExceded()", - "FilterBuilderTest/testMaxByPrefixFilterExceded()", - "FilterBuilderTest/testNoFilters()", - "FilterBuilderTest/testOnlyOneTypeQueryString()", - "FilterBuilderTest/testQueryStringWithSpecialChars1()", - "FilterBuilderTest/testQueryStringWithSpecialChars2()", - "FilterBuilderTest/testQueryStringWithSpecialChars3()", - "FilterBuilderTest/testQueryStringWithSpecialChars4()", + "FilterBuilderTest\/testBasicByNameQueryString()", + "FilterBuilderTest\/testBasicBySetQueryString()", + "FilterBuilderTest\/testBySetAndByNameQueryString()", + "FilterBuilderTest\/testFilterByNamesValuesDeduptedAndGrouped()", + "FilterBuilderTest\/testFilterBySetsValuesDeduptedAndGrouped()", + "FilterBuilderTest\/testMaxByNameFilterExceded()", + "FilterBuilderTest\/testMaxByPrefixFilterExceded()", + "FilterBuilderTest\/testNoFilters()", + "FilterBuilderTest\/testOnlyOneTypeQueryString()", + "FilterBuilderTest\/testQueryStringWithSpecialChars1()", + "FilterBuilderTest\/testQueryStringWithSpecialChars2()", + "FilterBuilderTest\/testQueryStringWithSpecialChars3()", + "FilterBuilderTest\/testQueryStringWithSpecialChars4()", "FlagSetValidatorTests", - "FlagSetValidatorTests/testCleanAndValidateValues()", - "FlagSetValidatorTests/testValidateOnEvaluationWithFilteredValues()", + "FlagSetValidatorTests\/testCleanAndValidateValues()", + "FlagSetValidatorTests\/testValidateOnEvaluationWithFilteredValues()", "FlagSetsCacheTests", - "FlagSetsCacheTests/testAddToFlagSetsNoFilter()", - "FlagSetsCacheTests/testAddToFlagSetsWithFilter()", - "FlagSetsCacheTests/testRemoveFromFlagSetsNoFilter()", + "FlagSetsCacheTests\/testAddToFlagSetsNoFilter()", + "FlagSetsCacheTests\/testAddToFlagSetsWithFilter()", + "FlagSetsCacheTests\/testRemoveFromFlagSetsNoFilter()", "GreaterThanOrEqualToSemverMatcherTest", - "GreaterThanOrEqualToSemverMatcherTest/testGeneralUnsuccessfulMatches()", - "GreaterThanOrEqualToSemverMatcherTest/testMatchShouldReturnFalseWhenKeyIsLess()", - "GreaterThanOrEqualToSemverMatcherTest/testMatchShouldReturnFalseWhenKeyIsNull()", - "GreaterThanOrEqualToSemverMatcherTest/testMatchShouldReturnTrueWhenKeyIsEqual()", - "GreaterThanOrEqualToSemverMatcherTest/testMatchShouldReturnTrueWhenKeyIsGreater()", - "GreaterThanOrEqualToSemverMatcherTest/testMatchWithMetadataShouldReturnFalseWhenLess()", - "GreaterThanOrEqualToSemverMatcherTest/testMatchWithMetadataShouldReturnTrueWhenEqual()", - "GreaterThanOrEqualToSemverMatcherTest/testMatchWithMetadataShouldReturnTrueWhenGreater()", - "GreaterThanOrEqualToSemverMatcherTest/testMatchWithPreReleaseShouldReturnFalseWhenLess()", - "GreaterThanOrEqualToSemverMatcherTest/testMatchWithPreReleaseShouldReturnTrueWhenEqual()", - "GreaterThanOrEqualToSemverMatcherTest/testMatchWithPreReleaseShouldReturnTrueWhenGreater()", + "GreaterThanOrEqualToSemverMatcherTest\/testGeneralUnsuccessfulMatches()", + "GreaterThanOrEqualToSemverMatcherTest\/testMatchShouldReturnFalseWhenKeyIsLess()", + "GreaterThanOrEqualToSemverMatcherTest\/testMatchShouldReturnFalseWhenKeyIsNull()", + "GreaterThanOrEqualToSemverMatcherTest\/testMatchShouldReturnTrueWhenKeyIsEqual()", + "GreaterThanOrEqualToSemverMatcherTest\/testMatchShouldReturnTrueWhenKeyIsGreater()", + "GreaterThanOrEqualToSemverMatcherTest\/testMatchWithMetadataShouldReturnFalseWhenLess()", + "GreaterThanOrEqualToSemverMatcherTest\/testMatchWithMetadataShouldReturnTrueWhenEqual()", + "GreaterThanOrEqualToSemverMatcherTest\/testMatchWithMetadataShouldReturnTrueWhenGreater()", + "GreaterThanOrEqualToSemverMatcherTest\/testMatchWithPreReleaseShouldReturnFalseWhenLess()", + "GreaterThanOrEqualToSemverMatcherTest\/testMatchWithPreReleaseShouldReturnTrueWhenEqual()", + "GreaterThanOrEqualToSemverMatcherTest\/testMatchWithPreReleaseShouldReturnTrueWhenGreater()", "HashedImpressionDaoTest", - "HashedImpressionDaoTest/testDelete()", - "HashedImpressionDaoTest/testGetAll()", - "HashedImpressionDaoTest/testUpdate()", + "HashedImpressionDaoTest\/testDelete()", + "HashedImpressionDaoTest\/testGetAll()", + "HashedImpressionDaoTest\/testUpdate()", "HttpClientTest", - "HttpClientTest/testDataRequest()", - "HttpClientTest/testDataRequestError()", - "HttpClientTest/testDataRequestWithOrder()", - "HttpClientTest/testStreamRequest()", - "HttpClientTest/testUploadRequest()", + "HttpClientTest\/testDataRequest()", + "HttpClientTest\/testDataRequestError()", + "HttpClientTest\/testDataRequestWithOrder()", + "HttpClientTest\/testStreamRequest()", + "HttpClientTest\/testUploadRequest()", "HttpDataRequestTest", - "HttpDataRequestTest/testError()", - "HttpDataRequestTest/testOnResponseCompletedError()", - "HttpDataRequestTest/testOnResponseCompletedOk()", - "HttpDataRequestTest/testRequestCreation()", - "HttpDataRequestTest/testRequestCreationWithOrder()", - "HttpDataRequestTest/testRequestEnquedOnSend()", + "HttpDataRequestTest\/testError()", + "HttpDataRequestTest\/testOnResponseCompletedError()", + "HttpDataRequestTest\/testOnResponseCompletedOk()", + "HttpDataRequestTest\/testRequestCreation()", + "HttpDataRequestTest\/testRequestCreationWithOrder()", + "HttpDataRequestTest\/testRequestEnquedOnSend()", "HttpEventsRecorderTests", - "HttpEventsRecorderTests/testServerNoReachable()", - "HttpEventsRecorderTests/testSuccessSending()", + "HttpEventsRecorderTests\/testServerNoReachable()", + "HttpEventsRecorderTests\/testSuccessSending()", "HttpImpressionsCountRecorderTests", - "HttpImpressionsCountRecorderTests/testServerNoReachable()", - "HttpImpressionsCountRecorderTests/testSuccessSending()", + "HttpImpressionsCountRecorderTests\/testServerNoReachable()", + "HttpImpressionsCountRecorderTests\/testSuccessSending()", "HttpImpressionsRecorderTests", - "HttpImpressionsRecorderTests/testServerNoReachable()", - "HttpImpressionsRecorderTests/testSuccessSending()", + "HttpImpressionsRecorderTests\/testServerNoReachable()", + "HttpImpressionsRecorderTests\/testSuccessSending()", "HttpMySegmentsFetcherTest", - "HttpMySegmentsFetcherTest/testServerNoReachable()", - "HttpMySegmentsFetcherTest/testSuccessFulFetch()", + "HttpMySegmentsFetcherTest\/testServerNoReachable()", + "HttpMySegmentsFetcherTest\/testSuccessFulFetch()", "HttpRequestListTest", - "HttpRequestListTest/testAddRequest()", - "HttpRequestListTest/testOverrideSetRequest()", - "HttpRequestListTest/testTakeRequest()", + "HttpRequestListTest\/testAddRequest()", + "HttpRequestListTest\/testOverrideSetRequest()", + "HttpRequestListTest\/testTakeRequest()", "HttpRequestManagerTests", - "HttpRequestManagerTests/testPinnedCredentialValidation()", + "HttpRequestManagerTests\/testPinnedCredentialValidation()", "HttpResponseTest", - "HttpResponseTest/testHttp104()", - "HttpResponseTest/testHttp200()", - "HttpResponseTest/testHttp299()", - "HttpResponseTest/testHttp300()", + "HttpResponseTest\/testHttp104()", + "HttpResponseTest\/testHttp200()", + "HttpResponseTest\/testHttp299()", + "HttpResponseTest\/testHttp300()", "HttpSplitFetcherTests", - "HttpSplitFetcherTests/testServerNoReachable()", - "HttpSplitFetcherTests/testSuccessFullFetch()", + "HttpSplitFetcherTests\/testServerNoReachable()", + "HttpSplitFetcherTests\/testSuccessFullFetch()", "HttpStreamRequestTest", - "HttpStreamRequestTest/testErrorResponse()", - "HttpStreamRequestTest/testOnResponseOk()", - "HttpStreamRequestTest/testRequestCreation()", - "HttpStreamRequestTest/testRequestEnquedOnSend()", + "HttpStreamRequestTest\/testErrorResponse()", + "HttpStreamRequestTest\/testOnResponseOk()", + "HttpStreamRequestTest\/testRequestCreation()", + "HttpStreamRequestTest\/testRequestEnquedOnSend()", "HttpTelemetryConfigRecorderTest", - "HttpTelemetryConfigRecorderTest/testServerNoReachable()", - "HttpTelemetryConfigRecorderTest/testSuccessSending()", + "HttpTelemetryConfigRecorderTest\/testServerNoReachable()", + "HttpTelemetryConfigRecorderTest\/testSuccessSending()", "HttpTelemetryStatsRecorderTest", - "HttpTelemetryStatsRecorderTest/testServerNoReachable()", - "HttpTelemetryStatsRecorderTest/testSuccessSending()", + "HttpTelemetryStatsRecorderTest\/testServerNoReachable()", + "HttpTelemetryStatsRecorderTest\/testSuccessSending()", "HttpUniqueKeyRecorderTests", - "HttpUniqueKeyRecorderTests/testServerNoReachable()", - "HttpUniqueKeyRecorderTests/testSuccessSending()", + "HttpUniqueKeyRecorderTests\/testServerNoReachable()", + "HttpUniqueKeyRecorderTests\/testSuccessSending()", "ImpressionDaoTest", - "ImpressionDaoTest/testDataIsEncryptedInDb()", - "ImpressionDaoTest/testInsertGet()", - "ImpressionDaoTest/testInsertGetAes128Cbc()", - "ImpressionDaoTest/testInsertManyGet()", - "ImpressionDaoTest/testInsertManyGetAes128Cbc()", - "ImpressionDaoTest/testLoadOutdated()", - "ImpressionDaoTest/testLoadOutdatedAes128Cbc()", - "ImpressionDaoTest/testUpdate()", - "ImpressionDaoTest/testUpdateAes128Cbc()", + "ImpressionDaoTest\/testDataIsEncryptedInDb()", + "ImpressionDaoTest\/testInsertGet()", + "ImpressionDaoTest\/testInsertGetAes128Cbc()", + "ImpressionDaoTest\/testInsertManyGet()", + "ImpressionDaoTest\/testInsertManyGetAes128Cbc()", + "ImpressionDaoTest\/testLoadOutdated()", + "ImpressionDaoTest\/testLoadOutdatedAes128Cbc()", + "ImpressionDaoTest\/testUpdate()", + "ImpressionDaoTest\/testUpdateAes128Cbc()", "ImpressionHasherTest", - "ImpressionHasherTest/testDifferentChangeNumber()", - "ImpressionHasherTest/testDifferentFeature()", - "ImpressionHasherTest/testDifferentKey()", - "ImpressionHasherTest/testDifferentLabel()", - "ImpressionHasherTest/testDifferentTreatment()", - "ImpressionHasherTest/testNoCrashWhenSplitNull()", + "ImpressionHasherTest\/testDifferentChangeNumber()", + "ImpressionHasherTest\/testDifferentFeature()", + "ImpressionHasherTest\/testDifferentKey()", + "ImpressionHasherTest\/testDifferentLabel()", + "ImpressionHasherTest\/testDifferentTreatment()", + "ImpressionHasherTest\/testNoCrashWhenSplitNull()", "ImpressionsCountDaoTest", - "ImpressionsCountDaoTest/testDataIsEncryptedInDb()", - "ImpressionsCountDaoTest/testInsertGetAes128Cbc()", - "ImpressionsCountDaoTest/testInsertGetPlainText()", - "ImpressionsCountDaoTest/testLoadOutdatedGetAes128Cbc()", - "ImpressionsCountDaoTest/testLoadOutdatedPlainText()", - "ImpressionsCountDaoTest/testUpdateGetAes128Cbc()", - "ImpressionsCountDaoTest/testUpdatePlainText()", + "ImpressionsCountDaoTest\/testDataIsEncryptedInDb()", + "ImpressionsCountDaoTest\/testInsertGetAes128Cbc()", + "ImpressionsCountDaoTest\/testInsertGetPlainText()", + "ImpressionsCountDaoTest\/testLoadOutdatedGetAes128Cbc()", + "ImpressionsCountDaoTest\/testLoadOutdatedPlainText()", + "ImpressionsCountDaoTest\/testUpdateGetAes128Cbc()", + "ImpressionsCountDaoTest\/testUpdatePlainText()", "ImpressionsCounterTest", - "ImpressionsCounterTest/testBasicUsage()", - "ImpressionsCounterTest/testConcurrency()", - "ImpressionsCounterTest/testTruncateTime()", + "ImpressionsCounterTest\/testBasicUsage()", + "ImpressionsCounterTest\/testConcurrency()", + "ImpressionsCounterTest\/testTruncateTime()", "ImpressionsDedupTest", - "ImpressionsDedupTest/testDebug()", - "ImpressionsDedupTest/testOptimized()", + "ImpressionsDedupTest\/testDebug()", + "ImpressionsDedupTest\/testOptimized()", "ImpressionsModeTypeWrapperTest", - "ImpressionsModeTypeWrapperTest/testEmptyInvalidValue()", - "ImpressionsModeTypeWrapperTest/testInitdebugValue()", - "ImpressionsModeTypeWrapperTest/testInitnoneValue()", - "ImpressionsModeTypeWrapperTest/testInitoptimizedValue()", - "ImpressionsModeTypeWrapperTest/testInvalidValue()", - "ImpressionsModeTypeWrapperTest/testdebugValue()", - "ImpressionsModeTypeWrapperTest/testnoneValue()", - "ImpressionsModeTypeWrapperTest/testoptimizedValue()", + "ImpressionsModeTypeWrapperTest\/testEmptyInvalidValue()", + "ImpressionsModeTypeWrapperTest\/testInitdebugValue()", + "ImpressionsModeTypeWrapperTest\/testInitnoneValue()", + "ImpressionsModeTypeWrapperTest\/testInitoptimizedValue()", + "ImpressionsModeTypeWrapperTest\/testInvalidValue()", + "ImpressionsModeTypeWrapperTest\/testdebugValue()", + "ImpressionsModeTypeWrapperTest\/testnoneValue()", + "ImpressionsModeTypeWrapperTest\/testoptimizedValue()", "ImpressionsNoneTest", - "ImpressionsNoneTest/testCorrectData()", - "ImpressionsNoneTest/testPeriodicRecording()", + "ImpressionsNoneTest\/testCorrectData()", + "ImpressionsNoneTest\/testPeriodicRecording()", "ImpressionsObserverTest", - "ImpressionsObserverTest/testBasicFunctionality()", - "ImpressionsObserverTest/testConcurrencyVsAccuracy()", - "ImpressionsObserverTest/testSave()", + "ImpressionsObserverTest\/testBasicFunctionality()", + "ImpressionsObserverTest\/testConcurrencyVsAccuracy()", + "ImpressionsObserverTest\/testSave()", "ImpressionsRecorderWorkerTests", - "ImpressionsRecorderWorkerTests/testFailToSendSome()", - "ImpressionsRecorderWorkerTests/testSendNoImpressions()", - "ImpressionsRecorderWorkerTests/testSendOneImpression()", - "ImpressionsRecorderWorkerTests/testSendSuccess()", + "ImpressionsRecorderWorkerTests\/testFailToSendSome()", + "ImpressionsRecorderWorkerTests\/testSendNoImpressions()", + "ImpressionsRecorderWorkerTests\/testSendOneImpression()", + "ImpressionsRecorderWorkerTests\/testSendSuccess()", "ImpressionsStorageTest", - "ImpressionsStorageTest/testClear()", - "ImpressionsStorageTest/testDisablePersistence()", - "ImpressionsStorageTest/testEnablePersistence()", - "ImpressionsStorageTest/testStartDisabledPersistence()", - "ImpressionsStorageTest/testStartEnabledPersistence()", + "ImpressionsStorageTest\/testClear()", + "ImpressionsStorageTest\/testDisablePersistence()", + "ImpressionsStorageTest\/testEnablePersistence()", + "ImpressionsStorageTest\/testStartDisabledPersistence()", + "ImpressionsStorageTest\/testStartEnabledPersistence()", "ImpressionsTrackerTest", - "ImpressionsTrackerTest/testDestroyDebug()", - "ImpressionsTrackerTest/testDestroyNone()", - "ImpressionsTrackerTest/testDestroyOptimized()", - "ImpressionsTrackerTest/testFlushDebug()", - "ImpressionsTrackerTest/testFlushNone()", - "ImpressionsTrackerTest/testFlushOptimized()", - "ImpressionsTrackerTest/testImpressionPushDebug()", - "ImpressionsTrackerTest/testImpressionPushNone()", - "ImpressionsTrackerTest/testImpressionPushOptimized()", - "ImpressionsTrackerTest/testImpressionPushTrackingDisabledDebug()", - "ImpressionsTrackerTest/testImpressionPushTrackingDisabledNone()", - "ImpressionsTrackerTest/testImpressionPushTrackingDisabledOptimized()", - "ImpressionsTrackerTest/testPauseDebug()", - "ImpressionsTrackerTest/testPauseNone()", - "ImpressionsTrackerTest/testPauseOptimized()", - "ImpressionsTrackerTest/testResume()", - "ImpressionsTrackerTest/testStartDebug()", - "ImpressionsTrackerTest/testStartNone()", - "ImpressionsTrackerTest/testStartOptimized()", - "ImpressionsTrackerTest/testStopDebug()", - "ImpressionsTrackerTest/testStopNone()", - "ImpressionsTrackerTest/testStopOptimized()", + "ImpressionsTrackerTest\/testDestroyDebug()", + "ImpressionsTrackerTest\/testDestroyNone()", + "ImpressionsTrackerTest\/testDestroyOptimized()", + "ImpressionsTrackerTest\/testFlushDebug()", + "ImpressionsTrackerTest\/testFlushNone()", + "ImpressionsTrackerTest\/testFlushOptimized()", + "ImpressionsTrackerTest\/testImpressionPushDebug()", + "ImpressionsTrackerTest\/testImpressionPushNone()", + "ImpressionsTrackerTest\/testImpressionPushOptimized()", + "ImpressionsTrackerTest\/testImpressionPushTrackingDisabledDebug()", + "ImpressionsTrackerTest\/testImpressionPushTrackingDisabledNone()", + "ImpressionsTrackerTest\/testImpressionPushTrackingDisabledOptimized()", + "ImpressionsTrackerTest\/testPauseDebug()", + "ImpressionsTrackerTest\/testPauseNone()", + "ImpressionsTrackerTest\/testPauseOptimized()", + "ImpressionsTrackerTest\/testResume()", + "ImpressionsTrackerTest\/testStartDebug()", + "ImpressionsTrackerTest\/testStartNone()", + "ImpressionsTrackerTest\/testStartOptimized()", + "ImpressionsTrackerTest\/testStopDebug()", + "ImpressionsTrackerTest\/testStopNone()", + "ImpressionsTrackerTest\/testStopOptimized()", "InListSemverMatcherTest", - "InListSemverMatcherTest/testGeneralMatches()", - "InListSemverMatcherTest/testMatchShouldReturnFalseWhenNotInList()", - "InListSemverMatcherTest/testMatchShouldReturnTrueWhenInList()", - "InListSemverMatcherTest/testMatchWithEmptyListShouldReturnFalse()", - "InListSemverMatcherTest/testMatchWithMetadataShouldReturnFalseWhenNotInList()", - "InListSemverMatcherTest/testMatchWithMetadataShouldReturnTrueWhenInList()", - "InListSemverMatcherTest/testMatchWithNullKeyShouldReturnFalse()", - "InListSemverMatcherTest/testMatchWithNullTargetShouldReturnFalse()", - "InListSemverMatcherTest/testMatchWithPreReleaseShouldReturnFalseWhenNotInList()", - "InListSemverMatcherTest/testMatchWithPreReleaseShouldReturnTrueWhenInList()", + "InListSemverMatcherTest\/testGeneralMatches()", + "InListSemverMatcherTest\/testMatchShouldReturnFalseWhenNotInList()", + "InListSemverMatcherTest\/testMatchShouldReturnTrueWhenInList()", + "InListSemverMatcherTest\/testMatchWithEmptyListShouldReturnFalse()", + "InListSemverMatcherTest\/testMatchWithMetadataShouldReturnFalseWhenNotInList()", + "InListSemverMatcherTest\/testMatchWithMetadataShouldReturnTrueWhenInList()", + "InListSemverMatcherTest\/testMatchWithNullKeyShouldReturnFalse()", + "InListSemverMatcherTest\/testMatchWithNullTargetShouldReturnFalse()", + "InListSemverMatcherTest\/testMatchWithPreReleaseShouldReturnFalseWhenNotInList()", + "InListSemverMatcherTest\/testMatchWithPreReleaseShouldReturnTrueWhenInList()", "InMemoryTelemetryStorageTest", - "InMemoryTelemetryStorageTest/testActiveFactoriesCount()", - "InMemoryTelemetryStorageTest/testAuthRejections()", - "InMemoryTelemetryStorageTest/testConcurrence()", - "InMemoryTelemetryStorageTest/testEvent()", - "InMemoryTelemetryStorageTest/testExceptions()", - "InMemoryTelemetryStorageTest/testHttpErrors()", - "InMemoryTelemetryStorageTest/testHttpLatencies()", - "InMemoryTelemetryStorageTest/testImpression()", - "InMemoryTelemetryStorageTest/testLastSync()", - "InMemoryTelemetryStorageTest/testLatencies()", - "InMemoryTelemetryStorageTest/testRedundantFactoriesCount()", - "InMemoryTelemetryStorageTest/testSessionLength()", - "InMemoryTelemetryStorageTest/testStreamingEvents()", - "InMemoryTelemetryStorageTest/testTags()", - "InMemoryTelemetryStorageTest/testTimeUntilReady()", - "InMemoryTelemetryStorageTest/testTimeUntilReadyFromCache()", - "InMemoryTelemetryStorageTest/testTokenRefreshes()", - "InMemoryTelemetryStorageTest/testUpdatesFromSse()", + "InMemoryTelemetryStorageTest\/testActiveFactoriesCount()", + "InMemoryTelemetryStorageTest\/testAuthRejections()", + "InMemoryTelemetryStorageTest\/testConcurrence()", + "InMemoryTelemetryStorageTest\/testEvent()", + "InMemoryTelemetryStorageTest\/testExceptions()", + "InMemoryTelemetryStorageTest\/testHttpErrors()", + "InMemoryTelemetryStorageTest\/testHttpLatencies()", + "InMemoryTelemetryStorageTest\/testImpression()", + "InMemoryTelemetryStorageTest\/testLastSync()", + "InMemoryTelemetryStorageTest\/testLatencies()", + "InMemoryTelemetryStorageTest\/testRedundantFactoriesCount()", + "InMemoryTelemetryStorageTest\/testSessionLength()", + "InMemoryTelemetryStorageTest\/testStreamingEvents()", + "InMemoryTelemetryStorageTest\/testTags()", + "InMemoryTelemetryStorageTest\/testTimeUntilReady()", + "InMemoryTelemetryStorageTest\/testTimeUntilReadyFromCache()", + "InMemoryTelemetryStorageTest\/testTokenRefreshes()", + "InMemoryTelemetryStorageTest\/testUpdatesFromSse()", "InitDbCipherTest", - "InitDbCipherTest/testDecryptDb()", - "InitDbCipherTest/testEncryptDb()", + "InitDbCipherTest\/testDecryptDb()", + "InitDbCipherTest\/testEncryptDb()", "InitialCacheTest", - "InitialCacheTest/testClearChangedSplitFilter()", - "InitialCacheTest/testClearExpiredCache()", - "InitialCacheTest/testExpiredCache()", - "InitialCacheTest/testFlagsSpecChanged()", - "InitialCacheTest/testNoClearNoExpiredCache()", + "InitialCacheTest\/testClearChangedSplitFilter()", + "InitialCacheTest\/testClearExpiredCache()", + "InitialCacheTest\/testExpiredCache()", + "InitialCacheTest\/testFlagsSpecChanged()", + "InitialCacheTest\/testNoClearNoExpiredCache()", "InstantFeatureFlagsUpdateTest", - "InstantFeatureFlagsUpdateTest/testInstantUpdateArchived()", - "InstantFeatureFlagsUpdateTest/testInstantUpdateGzip()", - "InstantFeatureFlagsUpdateTest/testInstantUpdateZlib()", + "InstantFeatureFlagsUpdateTest\/testInstantUpdateArchived()", + "InstantFeatureFlagsUpdateTest\/testInstantUpdateGzip()", + "InstantFeatureFlagsUpdateTest\/testInstantUpdateZlib()", "JwtTokenParserTest", - "JwtTokenParserTest/testEmptyToken()", - "JwtTokenParserTest/testGarbageToken()", - "JwtTokenParserTest/testOkToken()", - "JwtTokenParserTest/testOnlyChannelsWithSeparator()", - "JwtTokenParserTest/testOnlyHeader()", + "JwtTokenParserTest\/testEmptyToken()", + "JwtTokenParserTest\/testGarbageToken()", + "JwtTokenParserTest\/testOkToken()", + "JwtTokenParserTest\/testOnlyChannelsWithSeparator()", + "JwtTokenParserTest\/testOnlyHeader()", "KeyGeneratorTest", - "KeyGeneratorTest/testGenerationOk()", + "KeyGeneratorTest\/testGenerationOk()", "KeyValidatorTests", - "KeyValidatorTests/testInvalidEmptyBucketingKey()", - "KeyValidatorTests/testInvalidEmptyMatchingKey()", - "KeyValidatorTests/testInvalidLongBucketingKey()", - "KeyValidatorTests/testInvalidLongMatchingKey()", - "KeyValidatorTests/testNullMatchingKey()", - "KeyValidatorTests/testValidMatchingAndBucketingKey()", - "KeyValidatorTests/testValidMatchingKey()", + "KeyValidatorTests\/testInvalidEmptyBucketingKey()", + "KeyValidatorTests\/testInvalidEmptyMatchingKey()", + "KeyValidatorTests\/testInvalidLongBucketingKey()", + "KeyValidatorTests\/testInvalidLongMatchingKey()", + "KeyValidatorTests\/testNullMatchingKey()", + "KeyValidatorTests\/testValidMatchingAndBucketingKey()", + "KeyValidatorTests\/testValidMatchingKey()", "LRUCacheTest", - "LRUCacheTest/testAddGet()", - "LRUCacheTest/testEviction()", - "LRUCacheTest/testGetAllPerformance()", - "LRUCacheTest/testInsertPerformance()", + "LRUCacheTest\/testAddGet()", + "LRUCacheTest\/testEviction()", + "LRUCacheTest\/testGetAllPerformance()", + "LRUCacheTest\/testInsertPerformance()", "LatencyCounterTests", - "LatencyCounterTests/testAllBuckets()", - "LatencyCounterTests/testFirstBucket()", - "LatencyCounterTests/testInvalidIndex()", - "LatencyCounterTests/testLastBucket()", + "LatencyCounterTests\/testAllBuckets()", + "LatencyCounterTests\/testFirstBucket()", + "LatencyCounterTests\/testInvalidIndex()", + "LatencyCounterTests\/testLastBucket()", "LegacyHashingTest", - "LegacyHashingTest/testBucket()", - "LegacyHashingTest/testHashing()", + "LegacyHashingTest\/testBucket()", + "LegacyHashingTest\/testHashing()", "LessThanOrEqualToSemverMatcherTest", - "LessThanOrEqualToSemverMatcherTest/testGeneralMatches()", - "LessThanOrEqualToSemverMatcherTest/testMatchShouldReturnFalseWhenGreater()", - "LessThanOrEqualToSemverMatcherTest/testMatchShouldReturnTrueWhenEqual()", - "LessThanOrEqualToSemverMatcherTest/testMatchShouldReturnTrueWhenLess()", - "LessThanOrEqualToSemverMatcherTest/testMatchWithMetadataShouldReturnFalse()", - "LessThanOrEqualToSemverMatcherTest/testMatchWithMetadataShouldReturnTrue()", - "LessThanOrEqualToSemverMatcherTest/testMatchWithNullKeyShouldReturnNull()", - "LessThanOrEqualToSemverMatcherTest/testMatchWithNullTargetShouldReturnFalse()", - "LessThanOrEqualToSemverMatcherTest/testMatchWithPreReleaseShouldReturnTrue()", + "LessThanOrEqualToSemverMatcherTest\/testGeneralMatches()", + "LessThanOrEqualToSemverMatcherTest\/testMatchShouldReturnFalseWhenGreater()", + "LessThanOrEqualToSemverMatcherTest\/testMatchShouldReturnTrueWhenEqual()", + "LessThanOrEqualToSemverMatcherTest\/testMatchShouldReturnTrueWhenLess()", + "LessThanOrEqualToSemverMatcherTest\/testMatchWithMetadataShouldReturnFalse()", + "LessThanOrEqualToSemverMatcherTest\/testMatchWithMetadataShouldReturnTrue()", + "LessThanOrEqualToSemverMatcherTest\/testMatchWithNullKeyShouldReturnNull()", + "LessThanOrEqualToSemverMatcherTest\/testMatchWithNullTargetShouldReturnFalse()", + "LessThanOrEqualToSemverMatcherTest\/testMatchWithPreReleaseShouldReturnTrue()", "LocalhostManagerTests", - "LocalhostManagerTests/testSplitNames()", - "LocalhostManagerTests/testSplits()", - "LocalhostManagerTests/testSplitsByName()", + "LocalhostManagerTests\/testSplitNames()", + "LocalhostManagerTests\/testSplits()", + "LocalhostManagerTests\/testSplitsByName()", "LocalhostParserTests", - "LocalhostParserTests/testIntercomentedLinesFile()", - "LocalhostParserTests/testPerfectFile()", - "LocalhostParserTests/testSpacedLinesFile()", + "LocalhostParserTests\/testIntercomentedLinesFile()", + "LocalhostParserTests\/testPerfectFile()", + "LocalhostParserTests\/testSpacedLinesFile()", "LocalhostSplitClientTests", - "LocalhostSplitClientTests/testNonExistingSplitsTreatment()", - "LocalhostSplitClientTests/testNonExistingSplitsTreatments()", - "LocalhostSplitClientTests/testRightSplitsFileTreatment()", - "LocalhostSplitClientTests/testRightSplitsFileTreatments()", + "LocalhostSplitClientTests\/testNonExistingSplitsTreatment()", + "LocalhostSplitClientTests\/testNonExistingSplitsTreatments()", + "LocalhostSplitClientTests\/testRightSplitsFileTreatment()", + "LocalhostSplitClientTests\/testRightSplitsFileTreatments()", "LocalhostSplitLoaderTests", - "LocalhostSplitLoaderTests/testFileUpdate()", - "LocalhostSplitLoaderTests/testFileUpdate2()", - "LocalhostSplitLoaderTests/testInitial()", - "LocalhostSplitLoaderTests/testInvalidTypeFile()", - "LocalhostSplitLoaderTests/testNonExistingFile()", - "LocalhostSplitLoaderTests/testWrongFormatYml()", - "LocalhostSplitLoaderTests/testWrongLegacyFormatUpdate()", - "LocalhostSplitLoaderTests/testWrongYamlFormatUpdate()", + "LocalhostSplitLoaderTests\/testFileUpdate()", + "LocalhostSplitLoaderTests\/testFileUpdate2()", + "LocalhostSplitLoaderTests\/testInitial()", + "LocalhostSplitLoaderTests\/testInvalidTypeFile()", + "LocalhostSplitLoaderTests\/testNonExistingFile()", + "LocalhostSplitLoaderTests\/testWrongFormatYml()", + "LocalhostSplitLoaderTests\/testWrongLegacyFormatUpdate()", + "LocalhostSplitLoaderTests\/testWrongYamlFormatUpdate()", "LocalhostSplitStorageTests", - "LocalhostSplitStorageTests/testClear()", - "LocalhostSplitStorageTests/testDestroy()", - "LocalhostSplitStorageTests/testGetAllSplits()", - "LocalhostSplitStorageTests/testGetCount()", - "LocalhostSplitStorageTests/testGetManySplits()", - "LocalhostSplitStorageTests/testGetSplit()", - "LocalhostSplitStorageTests/testUpdateSplitChange()", + "LocalhostSplitStorageTests\/testClear()", + "LocalhostSplitStorageTests\/testDestroy()", + "LocalhostSplitStorageTests\/testGetAllSplits()", + "LocalhostSplitStorageTests\/testGetCount()", + "LocalhostSplitStorageTests\/testGetManySplits()", + "LocalhostSplitStorageTests\/testGetSplit()", + "LocalhostSplitStorageTests\/testUpdateSplitChange()", "LocalhostSynchronizerTests", - "LocalhostSynchronizerTests/testLoadAndSdkReady()", - "LocalhostSynchronizerTests/testUpdateSplits()", - "LocalhostSynchronizerTests/testUpdateYaml()", + "LocalhostSynchronizerTests\/testLoadAndSdkReady()", + "LocalhostSynchronizerTests\/testUpdateSplits()", + "LocalhostSynchronizerTests\/testUpdateYaml()", "LocalhostTests", - "LocalhostTests/testDefaultFactoryCreation()", - "LocalhostTests/testLoadYml()", - "LocalhostTests/testLocalhostFactoryCreation()", - "LocalhostTests/testUsingSpaceSeparatedFile()", - "LocalhostTests/testUsingYamlFile()", - "LocalhostTests/testUsingYamlFromApi()", + "LocalhostTests\/testDefaultFactoryCreation()", + "LocalhostTests\/testLoadYml()", + "LocalhostTests\/testLocalhostFactoryCreation()", + "LocalhostTests\/testUsingSpaceSeparatedFile()", + "LocalhostTests\/testUsingYamlFile()", + "LocalhostTests\/testUsingYamlFromApi()", "LocalhostYamlParserTest", - "LocalhostYamlParserTest/testCorrectFile()", - "LocalhostYamlParserTest/testMissingFirstSplit()", - "LocalhostYamlParserTest/testMissingSplitData()", - "LocalhostYamlParserTest/testMissingSplitTreatment()", - "LocalhostYamlParserTest/testWrongFormat()", + "LocalhostYamlParserTest\/testCorrectFile()", + "LocalhostYamlParserTest\/testMissingFirstSplit()", + "LocalhostYamlParserTest\/testMissingSplitData()", + "LocalhostYamlParserTest\/testMissingSplitTreatment()", + "LocalhostYamlParserTest\/testWrongFormat()", "LoggerTest", - "LoggerTest/testDebug()", - "LoggerTest/testError()", - "LoggerTest/testInfo()", - "LoggerTest/testNone()", - "LoggerTest/testVerbose()", - "LoggerTest/testWarning()", + "LoggerTest\/testDebug()", + "LoggerTest\/testError()", + "LoggerTest\/testInfo()", + "LoggerTest\/testNone()", + "LoggerTest\/testVerbose()", + "LoggerTest\/testWarning()", "MatcherEvalTests", - "MatcherEvalTests/testEval()", + "MatcherEvalTests\/testEval()", "MultiClientEvaluationTest", - "MultiClientEvaluationTest/testEvaluation()", - "MultiClientEvaluationTest/testEvaluationFromCache()", - "MultiClientEvaluationTest/testEvaluationWithAttributes()", - "MultiClientEvaluationTest/testImpressions()", - "MultiClientEvaluationTest/testTrack()", + "MultiClientEvaluationTest\/testEvaluation()", + "MultiClientEvaluationTest\/testEvaluationFromCache()", + "MultiClientEvaluationTest\/testEvaluationWithAttributes()", + "MultiClientEvaluationTest\/testImpressions()", + "MultiClientEvaluationTest\/testTrack()", "MultiClientStreamingResetTest", - "MultiClientStreamingResetTest/testNoStreamingDelay()", - "MultiClientStreamingResetTest/testStress()", - "MultiClientStreamingResetTest/testWithStreamingDelay()", + "MultiClientStreamingResetTest\/testNoStreamingDelay()", + "MultiClientStreamingResetTest\/testStress()", + "MultiClientStreamingResetTest\/testWithStreamingDelay()", "Murmur3HashingTest", - "Murmur3HashingTest/test64x128()", - "Murmur3HashingTest/testBucket()", + "Murmur3HashingTest\/test64x128()", + "Murmur3HashingTest\/testBucket()", "MyLargeSegmentsStorageTests", - "MyLargeSegmentsStorageTests/testChangeNumber()", - "MyLargeSegmentsStorageTests/testClear()", - "MyLargeSegmentsStorageTests/testGetMySegmentsAfterLoad()", - "MyLargeSegmentsStorageTests/testNoLoaded()", - "MyLargeSegmentsStorageTests/testUpdateEmptySegments()", - "MyLargeSegmentsStorageTests/testUpdateSegments()", + "MyLargeSegmentsStorageTests\/testChangeNumber()", + "MyLargeSegmentsStorageTests\/testClear()", + "MyLargeSegmentsStorageTests\/testGetMySegmentsAfterLoad()", + "MyLargeSegmentsStorageTests\/testNoLoaded()", + "MyLargeSegmentsStorageTests\/testUpdateEmptySegments()", + "MyLargeSegmentsStorageTests\/testUpdateSegments()", "MySegmentServerErrorTest", - "MySegmentServerErrorTest/test()", + "MySegmentServerErrorTest\/test()", "MySegmentsBgSyncWorkerTest", - "MySegmentsBgSyncWorkerTest/testNoSuccess()", - "MySegmentsBgSyncWorkerTest/testOneTimeFetchSuccess()", + "MySegmentsBgSyncWorkerTest\/testNoSuccess()", + "MySegmentsBgSyncWorkerTest\/testOneTimeFetchSuccess()", "MySegmentsDaoTest", - "MySegmentsDaoTest/testDataIsEncryptedInDb()", - "MySegmentsDaoTest/testGetInvalidKeyAes128Cbc()", - "MySegmentsDaoTest/testGetInvalidKeyPlainText()", - "MySegmentsDaoTest/testUpdateGetAes128Cbc()", - "MySegmentsDaoTest/testUpdateGetPlainText()", + "MySegmentsDaoTest\/testDataIsEncryptedInDb()", + "MySegmentsDaoTest\/testGetInvalidKeyAes128Cbc()", + "MySegmentsDaoTest\/testGetInvalidKeyPlainText()", + "MySegmentsDaoTest\/testUpdateGetAes128Cbc()", + "MySegmentsDaoTest\/testUpdateGetPlainText()", "MySegmentsPayloadDecoderTest", - "MySegmentsPayloadDecoderTest/testUserKeyHash()", + "MySegmentsPayloadDecoderTest\/testUserKeyHash()", "MySegmentsStorageTests", - "MySegmentsStorageTests/testChangeNumber()", - "MySegmentsStorageTests/testClear()", - "MySegmentsStorageTests/testGetMySegmentsAfterLoad()", - "MySegmentsStorageTests/testNoLoaded()", - "MySegmentsStorageTests/testUpdateEmptySegments()", - "MySegmentsStorageTests/testUpdateSegments()" + "MySegmentsStorageTests\/testChangeNumber()", + "MySegmentsStorageTests\/testClear()", + "MySegmentsStorageTests\/testGetMySegmentsAfterLoad()", + "MySegmentsStorageTests\/testNoLoaded()", + "MySegmentsStorageTests\/testUpdateEmptySegments()", + "MySegmentsStorageTests\/testUpdateSegments()" ], - "target": { - "containerPath": "container:Split.xcodeproj", - "identifier": "592C6AA4211B6C99002D120C", - "name": "SplitTests" + "target" : { + "containerPath" : "container:Split.xcodeproj", + "identifier" : "592C6AA4211B6C99002D120C", + "name" : "SplitTests" } } ], - "version": 1 -} \ No newline at end of file + "version" : 1 +} From de6235164a9cffda799cee59b67d87dc0103d38d Mon Sep 17 00:00:00 2001 From: Martin Cardozo Date: Mon, 26 May 2025 17:15:21 -0300 Subject: [PATCH 16/46] Unit tests added --- Split.xcodeproj/project.pbxproj | 4 + .../SplitView+StringConvertible.swift | 2 +- Split/Engine/Evaluator.swift | 2 +- Split/Matchers/PrerequisitesMatcher.swift | 13 +- Split/Models/SplitModel/Split.swift | 13 +- SplitTests/Fake/EvaluatorStub.swift | 3 +- .../Matcher/PrerequisitesMatcherTest.swift | 204 ++++++++++++++++++ SplitTests/SplitDTOTests.swift | 4 +- SplitTests/SplitManagerTest.swift | 2 +- 9 files changed, 232 insertions(+), 15 deletions(-) create mode 100644 SplitTests/Matcher/PrerequisitesMatcherTest.swift diff --git a/Split.xcodeproj/project.pbxproj b/Split.xcodeproj/project.pbxproj index b9cc7606c..973b45aec 100644 --- a/Split.xcodeproj/project.pbxproj +++ b/Split.xcodeproj/project.pbxproj @@ -356,6 +356,7 @@ 5B91B8392DDE4A3B000510F0 /* SplitDTOTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B91B8382DDE4A30000510F0 /* SplitDTOTests.swift */; }; 5BF52DF62DE0B60700FEDAFE /* PrerequisitesMatcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BF52DF52DE0B60300FEDAFE /* PrerequisitesMatcher.swift */; }; 5BF52DF72DE0B60700FEDAFE /* PrerequisitesMatcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BF52DF52DE0B60300FEDAFE /* PrerequisitesMatcher.swift */; }; + 5BF52DF92DE4B8D400FEDAFE /* PrerequisitesMatcherTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BF52DF82DE4B8CA00FEDAFE /* PrerequisitesMatcherTest.swift */; }; 9500D9922C56F9BA00383593 /* HostDomainFilterTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9500D9912C56F9BA00383593 /* HostDomainFilterTests.swift */; }; 9500D9A92C59297400383593 /* HostDomainFilter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9500D9A82C59297400383593 /* HostDomainFilter.swift */; }; 9500D9AA2C59382000383593 /* HostDomainFilter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9500D9A82C59297400383593 /* HostDomainFilter.swift */; }; @@ -1557,6 +1558,7 @@ 59FB7C3D22037B9400ECC96A /* SpaceDelimitedLocalhostSplitsParser.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SpaceDelimitedLocalhostSplitsParser.swift; sourceTree = ""; }; 5B91B8382DDE4A30000510F0 /* SplitDTOTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SplitDTOTests.swift; sourceTree = ""; }; 5BF52DF52DE0B60300FEDAFE /* PrerequisitesMatcher.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PrerequisitesMatcher.swift; sourceTree = ""; }; + 5BF52DF82DE4B8CA00FEDAFE /* PrerequisitesMatcherTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PrerequisitesMatcherTest.swift; sourceTree = ""; }; 9500D9912C56F9BA00383593 /* HostDomainFilterTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HostDomainFilterTests.swift; sourceTree = ""; }; 9500D9A82C59297400383593 /* HostDomainFilter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HostDomainFilter.swift; sourceTree = ""; }; 9500D9AC2C5A918300383593 /* split_cache_v5.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = split_cache_v5.xcdatamodel; sourceTree = ""; }; @@ -3712,6 +3714,7 @@ C5977C0E2BF2832A003E293A /* Matcher */ = { isa = PBXGroup; children = ( + 5BF52DF82DE4B8CA00FEDAFE /* PrerequisitesMatcherTest.swift */, C539CAD02D8AFBAF0050C732 /* InRuleBasedSegmentMatcherTest.swift */, C5977C002BF27390003E293A /* SemverTest.swift */, C5977C0F2BF28846003E293A /* BetweenSemverMatcherTest.swift */, @@ -4581,6 +4584,7 @@ 59F4AAA124FFC94100A1C69A /* NotificationManagerKeeperTest.swift in Sources */, 955E12372BFCDEAC00AE6D10 /* HashedImpressionDaoMock.swift in Sources */, 95ABF4FC29369B73006ED016 /* TelemetrySynchronizerStub.swift in Sources */, + 5BF52DF92DE4B8D400FEDAFE /* PrerequisitesMatcherTest.swift in Sources */, C5BD1E4F2D130EAF008EF198 /* ImpressionsToggleTest.swift in Sources */, 95715A8529D353C100A1B2F9 /* DbCipherTest.swift in Sources */, 59D84BDC2215CD52003DA248 /* LocalhostSplitLoaderTests.swift in Sources */, diff --git a/Split/Common/Extensions/SplitView+StringConvertible.swift b/Split/Common/Extensions/SplitView+StringConvertible.swift index c988bec88..336becf1f 100644 --- a/Split/Common/Extensions/SplitView+StringConvertible.swift +++ b/Split/Common/Extensions/SplitView+StringConvertible.swift @@ -35,7 +35,7 @@ extension SplitView { output+="prerequisites = [\n" prerequisites.forEach { prerequisite in output+=""" - \(prerequisite.n): {\(prerequisite.ts?.joined(separator: ","))}\n + \(prerequisite.n): {\(prerequisite.ts.joined(separator: ","))}\n """ } output+="]\n" diff --git a/Split/Engine/Evaluator.swift b/Split/Engine/Evaluator.swift index 6eb1f2a8a..844c01438 100644 --- a/Split/Engine/Evaluator.swift +++ b/Split/Engine/Evaluator.swift @@ -37,7 +37,7 @@ class DefaultEvaluator: Evaluator { // 2. Extract neccesary info let changeNumber = split.changeNumber ?? -1 - let defaultTreatment = split.defaultTreatment ?? SplitConstants.control + let defaultTreatment = split.defaultTreatment ?? SplitConstants.control let bucketKey = selectBucketKey(matchingKey: matchingKey, bucketingKey: bucketingKey) let values = EvalValues(matchValue: matchingKey, matchingKey: matchingKey, bucketingKey: bucketKey, attributes: attributes) diff --git a/Split/Matchers/PrerequisitesMatcher.swift b/Split/Matchers/PrerequisitesMatcher.swift index 93171bd77..854ca0b26 100644 --- a/Split/Matchers/PrerequisitesMatcher.swift +++ b/Split/Matchers/PrerequisitesMatcher.swift @@ -13,16 +13,19 @@ class PrerequisitesMatcher: BaseMatcher, MatcherProtocol { // This evaluation passes JUST if -all- prerequisite are met func evaluate(values: EvalValues, context: EvalContext?) -> Bool { - guard let prerequisites = prerequisites, !prerequisites.isEmpty, let evaluator = context?.evaluator else { return true } + + guard let prerequisites = prerequisites, !prerequisites.isEmpty else { return true } for prerequisite in prerequisites { - guard let splitName = prerequisite.n, let treaments = prerequisite.ts, !treaments.isEmpty else { continue } + guard !prerequisite.ts.isEmpty else { return true } do { - let eval = try evaluator.evalTreatment(matchingKey: values.matchingKey, bucketingKey: values.bucketingKey, splitName: splitName, attributes: nil) + let evalResult = try context?.evaluator?.evalTreatment(matchingKey: values.matchingKey, bucketingKey: values.bucketingKey, splitName: prerequisite.n, attributes: nil) - if treaments.contains(values.matchValue as? String ?? "") { - return false + if let treatment = evalResult?.treatment { + if !prerequisite.ts.contains(treatment) { + return false + } } } catch { Logger.e("Error evaluating condition in PrerequisitesMatcher: \(error)") diff --git a/Split/Models/SplitModel/Split.swift b/Split/Models/SplitModel/Split.swift index b4d6c9b3f..333a24ad5 100644 --- a/Split/Models/SplitModel/Split.swift +++ b/Split/Models/SplitModel/Split.swift @@ -4,7 +4,7 @@ import Foundation // The JSON is -partially- parsed at startup to improve SDK ready times (for example "conditions" are left out). // After parsing just the strictly necesary stuff, it saves the complete JSON to finish parsing later. -// Once .sdkReady is fired, it concurrently finishes parsing the rest. +// Once .sdkReady is fired, it concurrently finishes parsing the rest (on SplitStorage.get()) typealias Split = SplitDTO @@ -60,6 +60,13 @@ class SplitDTO: NSObject, SplitBase, Codable { } @objc public class Prerequisite: NSObject, Codable { - var n: String? - var ts: [String]? + var n: String + var ts: [String] + + #if DEBUG + init(n: String, ts: [String]) { + self.n = n + self.ts = ts + } + #endif } diff --git a/SplitTests/Fake/EvaluatorStub.swift b/SplitTests/Fake/EvaluatorStub.swift index f2ca5431b..52ca72cb2 100644 --- a/SplitTests/Fake/EvaluatorStub.swift +++ b/SplitTests/Fake/EvaluatorStub.swift @@ -12,8 +12,7 @@ import Foundation class EvaluatorStub: Evaluator { var lastAttributes = [Any]() - func evalTreatment(matchingKey: String, bucketingKey: String?, - splitName: String, attributes: [String: Any]?) throws -> EvaluationResult { + func evalTreatment(matchingKey: String, bucketingKey: String?, splitName: String, attributes: [String: Any]?) throws -> EvaluationResult { lastAttributes.append(attributes ?? "nil") return EvaluationResult(treatment: "on", label: "some") } diff --git a/SplitTests/Matcher/PrerequisitesMatcherTest.swift b/SplitTests/Matcher/PrerequisitesMatcherTest.swift new file mode 100644 index 000000000..128a5a2d4 --- /dev/null +++ b/SplitTests/Matcher/PrerequisitesMatcherTest.swift @@ -0,0 +1,204 @@ +// Created by Martin Cardozo on 26/05/2025. +// Copyright © 2025 Split. All rights reserved. + +import XCTest +@testable import Split + +class PrerequisitesMatcherTests: XCTestCase { + + private var storage = SplitsStorageStub() + private var context: EvalContext? + + func testPrerequisiteMet() { + let prerequisites = [ + Prerequisite(n: "always_on", ts: ["not-existing", "on", "other"]) + ] + + let SUT = PrerequisitesMatcher(prerequisites: prerequisites) + let values = EvalValues(matchValue: "", matchingKey: "", bucketingKey: nil) + + XCTAssertTrue(SUT.evaluate(values: values, context: context), "If a prerequisite is met it should return true") + } + + func testPrerequisiteMet2() { // To conform to the specs + let prerequisites = [ + Prerequisite(n: "always_off", ts: ["not-existing", "off"]) + ] + + let SUT = PrerequisitesMatcher(prerequisites: prerequisites) + let values = EvalValues(matchValue: "", matchingKey: "", bucketingKey: nil) + + XCTAssertTrue(SUT.evaluate(values: values, context: context), "If a prerequisite is met it should return true") + } + + func testPrerequisiteNotMet() { + let prerequisites = [ + Prerequisite(n: "always_on", ts: ["off", "v1"]) + ] + + let SUT = PrerequisitesMatcher(prerequisites: prerequisites) + let values = EvalValues(matchValue: "", matchingKey: "", bucketingKey: nil) + + XCTAssertFalse(SUT.evaluate(values: values, context: context), "If just one prerequisite is not met it should return false") + } + + func testPrerequisiteNotMet2() { // To conform to the specs + let prerequisites = [ + Prerequisite(n: "always_off", ts: ["on", "v1"]) + ] + + let SUT = PrerequisitesMatcher(prerequisites: prerequisites) + let values = EvalValues(matchValue: "", matchingKey: "", bucketingKey: nil) + + XCTAssertFalse(SUT.evaluate(values: values, context: context), "If just one prerequisite is not met it should return false") + } + + func testMultiplePrerequisites() { + let prerequisites = [ + Prerequisite(n: "always_on", ts: ["on"]), + Prerequisite(n: "always_off", ts: ["off"]) + ] + + let SUT = PrerequisitesMatcher(prerequisites: prerequisites) + let values = EvalValues(matchValue: "", matchingKey: "", bucketingKey: nil) + + XCTAssertTrue(SUT.evaluate(values: values, context: context), "If all prerequisites are met it should return true") + } + + func testMultiplePrerequisites2() { + let prerequisites = [ + Prerequisite(n: "always_on", ts: ["on"]), + Prerequisite(n: "always_off", ts: ["on"]) + ] + + let SUT = PrerequisitesMatcher(prerequisites: prerequisites) + let values = EvalValues(matchValue: "", matchingKey: "", bucketingKey: nil) + + XCTAssertFalse(SUT.evaluate(values: values, context: context), "If any prerequisite is not met it should return false") + } + + //MARK: Edge cases + func testNoPrerequisites() { + let SUT = PrerequisitesMatcher(prerequisites: nil) + let values = EvalValues(matchValue: "on", matchingKey: "key", bucketingKey: nil) + + XCTAssertTrue(SUT.evaluate(values: values, context: context), "If there is no prerequisites, it should return true") + } + + func testEmptyPrerequisites() { + let SUT = PrerequisitesMatcher(prerequisites: []) + let values = EvalValues(matchValue: "on", matchingKey: "key", bucketingKey: nil) + + XCTAssertTrue(SUT.evaluate(values: values, context: context), "If prerequisites exists but it's just empty, it should return true") + } + + func testNonExistentFeatureFlag() { + let SUT = PrerequisitesMatcher(prerequisites: [Prerequisite(n: "asldjh38", ts: ["on"])]) + let values = EvalValues(matchValue: "on", matchingKey: "key", bucketingKey: nil) + + XCTAssertFalse(SUT.evaluate(values: values, context: context), "If the feature flag is non existent it should return false") + } + +} + +//MARK: Testing Mocked Data +extension PrerequisitesMatcherTests { + override func setUp() { + let split1Condition = """ + { + "conditionType": "ROLLOUT", + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { "trafficType": "user", "attribute": null }, + "matcherType": "ALL_KEYS", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": null, + "unaryNumericMatcherData": null, + "betweenMatcherData": null + } + ] + }, + "partitions": [ + { "treatment": "on", "size": 100 }, + { "treatment": "off", "size": 0 } + ], + "label": "in segment all" + } + """.data(using: .utf8)! + + let split = SplitDTO( + name: "always_on", + trafficType: "user", + status: .active, + sets: [], + json: "", + killed: false, + impressionsDisabled: false + ) + + split.trafficAllocation = 100 + split.trafficAllocationSeed = 1012950810 + split.seed = -725161385 + split.defaultTreatment = "off" + split.changeNumber = 1494364996459 + let decoder = JSONDecoder() + var condition = try! decoder.decode(Condition.self, from: split1Condition) + split.conditions = [condition] + + storage.updateWithoutChecks(split: split) + + let split2Condition = """ + { + "conditionType": "ROLLOUT", + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "user", + "attribute": null + }, + "matcherType": "ALL_KEYS", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": null, + "unaryNumericMatcherData": null, + "betweenMatcherData": null + } + ] + }, + "partitions": [ + { "treatment": "on", "size": 0 }, + { "treatment": "off", "size": 100 } + ], + "label": "in segment all" + } + """.data(using: .utf8)! + + let split2 = SplitDTO( + name: "always_off", + trafficType: "user", + status: .active, + sets: [], + json: "", + killed: false, + impressionsDisabled: false + ) + + split2.trafficAllocation = 100 + split2.trafficAllocationSeed = -331690370 + split2.seed = 403891040 + split2.defaultTreatment = "on" + split2.changeNumber = 1494365020316 + + condition = try! decoder.decode(Condition.self, from: split2Condition) + split2.conditions = [condition] + + storage.updateWithoutChecks(split: split2) + + context = EvalContext(evaluator: DefaultEvaluator(splitsStorage: storage, mySegmentsStorage: MySegmentsStorageStub()), mySegmentsStorage: MySegmentsStorageStub(), myLargeSegmentsStorage: nil, ruleBasedSegmentsStorage: nil) + } +} diff --git a/SplitTests/SplitDTOTests.swift b/SplitTests/SplitDTOTests.swift index 8f7ae9528..a7d4643c4 100644 --- a/SplitTests/SplitDTOTests.swift +++ b/SplitTests/SplitDTOTests.swift @@ -16,8 +16,8 @@ class SplitDTOTests: XCTestCase { let decoded = try? TargetingRulesChangeDecoder.decode(from: data.data(using: .utf8)!) for i in 0.. Date: Mon, 26 May 2025 17:29:38 -0300 Subject: [PATCH 17/46] Cleanup --- Split/Engine/Evaluator.swift | 2 +- .../Matcher/PrerequisitesMatcherTest.swift | 55 ++++++------------- 2 files changed, 19 insertions(+), 38 deletions(-) diff --git a/Split/Engine/Evaluator.swift b/Split/Engine/Evaluator.swift index 844c01438..e00488b5d 100644 --- a/Split/Engine/Evaluator.swift +++ b/Split/Engine/Evaluator.swift @@ -109,7 +109,7 @@ class DefaultEvaluator: Evaluator { } private func selectBucketKey(matchingKey: String, bucketingKey: String?) -> String { - if let bucketingKey = bucketingKey { return bucketingKey } + if let bucketingKey = bucketingKey, !bucketingKey.isEmpty { return bucketingKey } return matchingKey } diff --git a/SplitTests/Matcher/PrerequisitesMatcherTest.swift b/SplitTests/Matcher/PrerequisitesMatcherTest.swift index 128a5a2d4..bcdc9ce6f 100644 --- a/SplitTests/Matcher/PrerequisitesMatcherTest.swift +++ b/SplitTests/Matcher/PrerequisitesMatcherTest.swift @@ -101,9 +101,18 @@ class PrerequisitesMatcherTests: XCTestCase { } -//MARK: Testing Mocked Data +//MARK: Testing Data extension PrerequisitesMatcherTests { override func setUp() { + + // SPLIT 1 + let split = SplitDTO(name: "always_on", trafficType: "user", status: .active, sets: [], json: "", killed: false, impressionsDisabled: false) + split.trafficAllocation = 100 + split.trafficAllocationSeed = 1012950810 + split.seed = -725161385 + split.defaultTreatment = "off" + split.changeNumber = 1494364996459 + let decoder = JSONDecoder() let split1Condition = """ { "conditionType": "ROLLOUT", @@ -128,28 +137,16 @@ extension PrerequisitesMatcherTests { "label": "in segment all" } """.data(using: .utf8)! - - let split = SplitDTO( - name: "always_on", - trafficType: "user", - status: .active, - sets: [], - json: "", - killed: false, - impressionsDisabled: false - ) - - split.trafficAllocation = 100 - split.trafficAllocationSeed = 1012950810 - split.seed = -725161385 - split.defaultTreatment = "off" - split.changeNumber = 1494364996459 - let decoder = JSONDecoder() var condition = try! decoder.decode(Condition.self, from: split1Condition) split.conditions = [condition] - storage.updateWithoutChecks(split: split) - + // SPLIT 2 + let split2 = SplitDTO(name: "always_off", trafficType: "user", status: .active, sets: [], json: "", killed: false, impressionsDisabled: false) + split2.trafficAllocation = 100 + split2.trafficAllocationSeed = -331690370 + split2.seed = 403891040 + split2.defaultTreatment = "on" + split2.changeNumber = 1494365020316 let split2Condition = """ { "conditionType": "ROLLOUT", @@ -177,26 +174,10 @@ extension PrerequisitesMatcherTests { "label": "in segment all" } """.data(using: .utf8)! - - let split2 = SplitDTO( - name: "always_off", - trafficType: "user", - status: .active, - sets: [], - json: "", - killed: false, - impressionsDisabled: false - ) - - split2.trafficAllocation = 100 - split2.trafficAllocationSeed = -331690370 - split2.seed = 403891040 - split2.defaultTreatment = "on" - split2.changeNumber = 1494365020316 - condition = try! decoder.decode(Condition.self, from: split2Condition) split2.conditions = [condition] + storage.updateWithoutChecks(split: split) storage.updateWithoutChecks(split: split2) context = EvalContext(evaluator: DefaultEvaluator(splitsStorage: storage, mySegmentsStorage: MySegmentsStorageStub()), mySegmentsStorage: MySegmentsStorageStub(), myLargeSegmentsStorage: nil, ruleBasedSegmentsStorage: nil) From 1912467794dbe271a23aca1e2fa561b75d6ef81b Mon Sep 17 00:00:00 2001 From: Martin Cardozo Date: Mon, 26 May 2025 17:42:13 -0300 Subject: [PATCH 18/46] New PrerequisitesMatcher. New result label and new step in Evaluator --- Split.xcodeproj/project.pbxproj | 7 +- Split/Engine/Evaluator.swift | 173 +++++++++---------- Split/Impressions/ImpressionsConstants.swift | 17 +- Split/Matchers/PrerequisitesMatcher.swift | 18 ++ 4 files changed, 115 insertions(+), 100 deletions(-) create mode 100644 Split/Matchers/PrerequisitesMatcher.swift diff --git a/Split.xcodeproj/project.pbxproj b/Split.xcodeproj/project.pbxproj index 422681e92..b34cf6aff 100644 --- a/Split.xcodeproj/project.pbxproj +++ b/Split.xcodeproj/project.pbxproj @@ -354,7 +354,8 @@ 59FB7C3C2203795F00ECC96A /* LocalhostSplitsParser.swift in Sources */ = {isa = PBXBuildFile; fileRef = 59FB7C3B2203795F00ECC96A /* LocalhostSplitsParser.swift */; }; 59FB7C3E22037B9400ECC96A /* SpaceDelimitedLocalhostSplitsParser.swift in Sources */ = {isa = PBXBuildFile; fileRef = 59FB7C3D22037B9400ECC96A /* SpaceDelimitedLocalhostSplitsParser.swift */; }; 5B91B8392DDE4A3B000510F0 /* SplitDTOTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B91B8382DDE4A30000510F0 /* SplitDTOTests.swift */; }; - 5BF52DEF2DDF884D00FEDAFE /* RuleBasedSegment.swift in Sources */ = {isa = PBXBuildFile; fileRef = C539CAB52D88C1F10050C732 /* RuleBasedSegment.swift */; }; + 5BF52DFB2DE5095800FEDAFE /* PrerequisitesMatcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BF52DFA2DE5095300FEDAFE /* PrerequisitesMatcher.swift */; }; + 5BF52DFC2DE5095800FEDAFE /* PrerequisitesMatcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BF52DFA2DE5095300FEDAFE /* PrerequisitesMatcher.swift */; }; 9500D9922C56F9BA00383593 /* HostDomainFilterTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9500D9912C56F9BA00383593 /* HostDomainFilterTests.swift */; }; 9500D9A92C59297400383593 /* HostDomainFilter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9500D9A82C59297400383593 /* HostDomainFilter.swift */; }; 9500D9AA2C59382000383593 /* HostDomainFilter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9500D9A82C59297400383593 /* HostDomainFilter.swift */; }; @@ -1555,6 +1556,7 @@ 59FB7C3B2203795F00ECC96A /* LocalhostSplitsParser.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocalhostSplitsParser.swift; sourceTree = ""; }; 59FB7C3D22037B9400ECC96A /* SpaceDelimitedLocalhostSplitsParser.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SpaceDelimitedLocalhostSplitsParser.swift; sourceTree = ""; }; 5B91B8382DDE4A30000510F0 /* SplitDTOTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SplitDTOTests.swift; sourceTree = ""; }; + 5BF52DFA2DE5095300FEDAFE /* PrerequisitesMatcher.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PrerequisitesMatcher.swift; sourceTree = ""; }; 9500D9912C56F9BA00383593 /* HostDomainFilterTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HostDomainFilterTests.swift; sourceTree = ""; }; 9500D9A82C59297400383593 /* HostDomainFilter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HostDomainFilter.swift; sourceTree = ""; }; 9500D9AC2C5A918300383593 /* split_cache_v5.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = split_cache_v5.xcdatamodel; sourceTree = ""; }; @@ -2423,6 +2425,7 @@ 3B6DEECB20EA6AE30067435E /* Matchers */ = { isa = PBXGroup; children = ( + 5BF52DFA2DE5095300FEDAFE /* PrerequisitesMatcher.swift */, C539CACD2D8A08C00050C732 /* InRuleBasedSegmentMatcher.swift */, C5A3F5222BED4EC5009FACD3 /* Semver */, 3B6DEECC20EA6AE30067435E /* AllKeysMatcher.swift */, @@ -4233,6 +4236,7 @@ 95C1600D27D28CF4008562E3 /* PersistentAttributesStorage.swift in Sources */, 95726075262F548500350CCA /* SplitBgSynchronizer.swift in Sources */, 95C1600B27D28CB8008562E3 /* OneKeyPersistentAttributesStorage.swift in Sources */, + 5BF52DFB2DE5095800FEDAFE /* PrerequisitesMatcher.swift in Sources */, 9519A91127D6935700278AEC /* ByKeyAttributesStorage.swift in Sources */, 59D84BD622158A24003DA248 /* LocalhostSplitFactory.swift in Sources */, 3B6DEF4C20EA6AE50067435E /* SplitBase.swift in Sources */, @@ -4824,6 +4828,7 @@ 95B02CC628D0BDC10030EC8B /* CompressionUtil.swift in Sources */, 95B02CC728D0BDC10030EC8B /* Stopwatch.swift in Sources */, 95B02CC828D0BDC10030EC8B /* ConcurrentArrayQueue.swift in Sources */, + 5BF52DFC2DE5095800FEDAFE /* PrerequisitesMatcher.swift in Sources */, 95B02CC928D0BDC10030EC8B /* SynchronizedList.swift in Sources */, 95B02CCA28D0BDC10030EC8B /* ConcurrentSet.swift in Sources */, C539CADC2D93229D0050C732 /* EvaluationOptions.swift in Sources */, diff --git a/Split/Engine/Evaluator.swift b/Split/Engine/Evaluator.swift index be5e8393c..e00488b5d 100644 --- a/Split/Engine/Evaluator.swift +++ b/Split/Engine/Evaluator.swift @@ -3,113 +3,74 @@ // Split // // Created by Natalia Stele on 11/14/17. -// import Foundation -// swiftlint:disable function_body_length -struct EvaluationResult { - var treatment: String - var label: String - var changeNumber: Int64? - var configuration: String? - var impressionsDisabled: Bool - - init(treatment: String, label: String, changeNumber: Int64? = nil, configuration: String? = nil, - impressionsDisabled: Bool = false) { - self.treatment = treatment - self.label = label - self.changeNumber = changeNumber - self.configuration = configuration - self.impressionsDisabled = impressionsDisabled - } -} - -struct EvalValues { - let matchValue: Any? - let matchingKey: String - let bucketingKey: String? - let attributes: [String: Any]? - - init(matchValue: Any?, matchingKey: String, bucketingKey: String? = nil, attributes: [String: Any]? = nil) { - self.matchValue = matchValue - self.matchingKey = matchingKey - self.bucketingKey = bucketingKey - self.attributes = attributes - } -} - -// Components needed -struct EvalContext { - let evaluator: Evaluator? - let mySegmentsStorage: MySegmentsStorage? - let myLargeSegmentsStorage: MySegmentsStorage? - let ruleBasedSegmentsStorage: RuleBasedSegmentsStorage? -} protocol Evaluator { - func evalTreatment(matchingKey: String, bucketingKey: String?, - splitName: String, attributes: [String: Any]?) throws -> EvaluationResult + func evalTreatment(matchingKey: String, bucketingKey: String?, splitName: String, attributes: [String: Any]?) throws -> EvaluationResult } class DefaultEvaluator: Evaluator { + // Internal for testing purposes var splitter: SplitterProtocol = Splitter.shared + private let splitsStorage: SplitsStorage private let mySegmentsStorage: MySegmentsStorage private let myLargeSegmentsStorage: MySegmentsStorage? private let ruleBasedSegmentsStorage: RuleBasedSegmentsStorage? - init(splitsStorage: SplitsStorage, - mySegmentsStorage: MySegmentsStorage, - myLargeSegmentsStorage: MySegmentsStorage? = nil, - ruleBasedSegmentsStorage: RuleBasedSegmentsStorage? = nil) { + init(splitsStorage: SplitsStorage, mySegmentsStorage: MySegmentsStorage, myLargeSegmentsStorage: MySegmentsStorage? = nil, ruleBasedSegmentsStorage: RuleBasedSegmentsStorage? = nil) { self.splitsStorage = splitsStorage self.mySegmentsStorage = mySegmentsStorage self.myLargeSegmentsStorage = myLargeSegmentsStorage self.ruleBasedSegmentsStorage = ruleBasedSegmentsStorage } - func evalTreatment(matchingKey: String, bucketingKey: String?, - splitName: String, attributes: [String: Any]?) throws -> EvaluationResult { + func evalTreatment(matchingKey: String, bucketingKey: String?, splitName: String, attributes: [String: Any]?) throws -> EvaluationResult { - guard let split = splitsStorage.get(name: splitName), - split.status != .archived else { - Logger.w("The feature flag definition for '\(splitName)' has not been found") - return EvaluationResult(treatment: SplitConstants.control, label: ImpressionsConstants.splitNotFound) + // 1. Guarantee Split exists & is active + guard let split = splitsStorage.get(name: splitName), split.status != .archived else { + Logger.w("The feature flag definition for '\(splitName)' not found") + return EvaluationResult(treatment: SplitConstants.control, label: ImpressionsConstants.splitNotFound) } - + + // 2. Extract neccesary info let changeNumber = split.changeNumber ?? -1 - let defaultTreatment = split.defaultTreatment ?? SplitConstants.control - if let killed = split.killed, killed { + let defaultTreatment = split.defaultTreatment ?? SplitConstants.control + let bucketKey = selectBucketKey(matchingKey: matchingKey, bucketingKey: bucketingKey) + let values = EvalValues(matchValue: matchingKey, matchingKey: matchingKey, bucketingKey: bucketKey, attributes: attributes) + + // 3. Guarantee is not killed + guard let killed = split.killed, !killed else { + return EvaluationResult(treatment: defaultTreatment, label: ImpressionsConstants.killed, changeNumber: changeNumber, + configuration: split.configurations?[defaultTreatment], impressionsDisabled: split.isImpressionsDisabled()) + } + + // 4. Evaluate Prerequisites + if !PrerequisitesMatcher().evaluate(values: values, context: getContext()) { return EvaluationResult(treatment: defaultTreatment, - label: ImpressionsConstants.killed, + label: ImpressionsConstants.prerequisitesNotMet, changeNumber: changeNumber, configuration: split.configurations?[defaultTreatment], impressionsDisabled: split.isImpressionsDisabled()) } - var inRollOut: Bool = false - var splitAlgo: Algorithm = Algorithm.legacy - - if let rawAlgo = split.algo, let algo = Algorithm.init(rawValue: rawAlgo) { - splitAlgo = algo + + // 5. Evaluate core conditions + guard let conditions = split.conditions, let trafficAllocationSeed = split.trafficAllocationSeed, let seed = split.seed else { + return EvaluationResult(treatment: SplitConstants.control, label: ImpressionsConstants.exception) } - - let bucketKey = selectBucketKey(matchingKey: matchingKey, bucketingKey: bucketingKey) - - guard let conditions: [Condition] = split.conditions, - let trafficAllocationSeed = split.trafficAllocationSeed, - let seed = split.seed else { - return EvaluationResult(treatment: SplitConstants.control, label: ImpressionsConstants.exception) - } - + var splitAlgo: Algorithm = Algorithm.legacy + if let rawAlgo = split.algo, let algo = Algorithm.init(rawValue: rawAlgo) { splitAlgo = algo } do { + var inRollOut: Bool = false for condition in conditions { + + // Traffic Allocation if !inRollOut && condition.conditionType == ConditionType.rollout { if let trafficAllocation = split.trafficAllocation, trafficAllocation < 100 { - let bucket: Int64 = splitter.getBucket(seed: trafficAllocationSeed, - key: bucketKey, - algo: splitAlgo) + let bucket: Int64 = splitter.getBucket(seed: trafficAllocationSeed, key: bucketKey, algo: splitAlgo) if bucket > trafficAllocation { return EvaluationResult(treatment: defaultTreatment, label: ImpressionsConstants.notInSplit, @@ -121,49 +82,79 @@ class DefaultEvaluator: Evaluator { } } - // Returns the first condition that match. - let values = EvalValues(matchValue: matchingKey, matchingKey: matchingKey, - bucketingKey: bucketKey, attributes: attributes) + // Core conditions (returns the first one that match) if try condition.match(values: values, context: getContext()) { let key: Key = Key(matchingKey: matchingKey, bucketingKey: bucketKey) - let treatment = splitter.getTreatment(key: key, seed: seed, attributes: attributes, - partions: condition.partitions, algo: splitAlgo) + let treatment = splitter.getTreatment(key: key, seed: seed, attributes: attributes, partions: condition.partitions, algo: splitAlgo) + return EvaluationResult(treatment: treatment, label: condition.label!, changeNumber: changeNumber, configuration: split.configurations?[treatment], impressionsDisabled: split.isImpressionsDisabled()) } } - let result = EvaluationResult(treatment: defaultTreatment, + return EvaluationResult(treatment: defaultTreatment, label: ImpressionsConstants.noConditionMatched, changeNumber: changeNumber, configuration: split.configurations?[defaultTreatment], impressionsDisabled: split.isImpressionsDisabled()) - return result } catch EvaluatorError.matcherNotFound { - Logger.e("The matcher has not been found") - return EvaluationResult(treatment: SplitConstants.control, label: ImpressionsConstants.matcherNotFound, - changeNumber: changeNumber, impressionsDisabled: split.isImpressionsDisabled()) + Logger.e("Matcher not found") + return EvaluationResult(treatment: SplitConstants.control, label: ImpressionsConstants.matcherNotFound, changeNumber: changeNumber, impressionsDisabled: split.isImpressionsDisabled()) } } private func getContext() -> EvalContext { - return EvalContext(evaluator: self, - mySegmentsStorage: mySegmentsStorage, - myLargeSegmentsStorage: myLargeSegmentsStorage, - ruleBasedSegmentsStorage: ruleBasedSegmentsStorage) + EvalContext(evaluator: self, mySegmentsStorage: mySegmentsStorage,myLargeSegmentsStorage: myLargeSegmentsStorage, ruleBasedSegmentsStorage: ruleBasedSegmentsStorage) } private func selectBucketKey(matchingKey: String, bucketingKey: String?) -> String { - if let key = bucketingKey, !key.isEmpty() { - return key - } + if let bucketingKey = bucketingKey, !bucketingKey.isEmpty { return bucketingKey } + return matchingKey } } private extension Split { func isImpressionsDisabled() -> Bool { - return self.impressionsDisabled ?? false + impressionsDisabled ?? false + } +} + +// MARK: Components needed +struct EvalValues { + let matchValue: Any? + let matchingKey: String + let bucketingKey: String? + let attributes: [String: Any]? + + init(matchValue: Any?, matchingKey: String, bucketingKey: String? = nil, attributes: [String: Any]? = nil) { + self.matchValue = matchValue + self.matchingKey = matchingKey + self.bucketingKey = bucketingKey + self.attributes = attributes + } +} + +struct EvalContext { + let evaluator: Evaluator? + let mySegmentsStorage: MySegmentsStorage? + let myLargeSegmentsStorage: MySegmentsStorage? + let ruleBasedSegmentsStorage: RuleBasedSegmentsStorage? +} + +struct EvaluationResult { + var treatment: String + var label: String + var changeNumber: Int64? + var configuration: String? + var impressionsDisabled: Bool + + init(treatment: String, label: String, changeNumber: Int64? = nil, configuration: String? = nil, impressionsDisabled: Bool = false) { + self.treatment = treatment + self.label = label + self.changeNumber = changeNumber + self.configuration = configuration + self.impressionsDisabled = impressionsDisabled } } diff --git a/Split/Impressions/ImpressionsConstants.swift b/Split/Impressions/ImpressionsConstants.swift index 608de4053..e00d1ab05 100644 --- a/Split/Impressions/ImpressionsConstants.swift +++ b/Split/Impressions/ImpressionsConstants.swift @@ -8,12 +8,13 @@ import Foundation struct ImpressionsConstants { - static let noConditionMatched: String = "default rule" - static let killed: String = "killed" - static let splitNotFound: String = "definition not found" - static let notInSplit: String = "not in split" - static let matcherNotFound: String = "matcher not found" - static let exception: String = "exception" - static let notReady: String = "not ready" - static let unsupportedMatcherType: String = "targeting rule type unsupported by sdk" + static let noConditionMatched = "default rule" + static let killed = "killed" + static let splitNotFound = "definition not found" + static let notInSplit = "not in split" + static let matcherNotFound = "matcher not found" + static let exception = "exception" + static let notReady = "not ready" + static let unsupportedMatcherType = "targeting rule type unsupported by sdk" + static let prerequisitesNotMet = "prerequisites not met" } diff --git a/Split/Matchers/PrerequisitesMatcher.swift b/Split/Matchers/PrerequisitesMatcher.swift new file mode 100644 index 000000000..623fd3fdc --- /dev/null +++ b/Split/Matchers/PrerequisitesMatcher.swift @@ -0,0 +1,18 @@ +// Created by Martin Cardozo on 22/05/2025. +// Copyright © 2025 Split. All rights reserved. + +import Foundation + +class PrerequisitesMatcher: BaseMatcher, MatcherProtocol { + + private var prerequisites: [Prerequisite]? + + init(prerequisites: [Prerequisite]? = nil) { + self.prerequisites = prerequisites + } + + // This evaluation passes JUST if -all- prerequisite are met + func evaluate(values: EvalValues, context: EvalContext?) -> Bool { + return true + } +} From 0857b4837b47fa6612a7b46e49da2b2174e17704 Mon Sep 17 00:00:00 2001 From: Martin Cardozo Date: Tue, 27 May 2025 10:23:02 -0300 Subject: [PATCH 19/46] Improved --- Split/Engine/Evaluator.swift | 3 +- Split/Matchers/PrerequisitesMatcher.swift | 12 +++--- .../Matcher/PrerequisitesMatcherTest.swift | 41 +++++++++---------- 3 files changed, 27 insertions(+), 29 deletions(-) diff --git a/Split/Engine/Evaluator.swift b/Split/Engine/Evaluator.swift index e00488b5d..9b66dc74f 100644 --- a/Split/Engine/Evaluator.swift +++ b/Split/Engine/Evaluator.swift @@ -48,14 +48,13 @@ class DefaultEvaluator: Evaluator { } // 4. Evaluate Prerequisites - if !PrerequisitesMatcher().evaluate(values: values, context: getContext()) { + if !PrerequisitesMatcher(split.prerequisites).evaluate(values: values, context: getContext()) { return EvaluationResult(treatment: defaultTreatment, label: ImpressionsConstants.prerequisitesNotMet, changeNumber: changeNumber, configuration: split.configurations?[defaultTreatment], impressionsDisabled: split.isImpressionsDisabled()) } - // 5. Evaluate core conditions guard let conditions = split.conditions, let trafficAllocationSeed = split.trafficAllocationSeed, let seed = split.seed else { diff --git a/Split/Matchers/PrerequisitesMatcher.swift b/Split/Matchers/PrerequisitesMatcher.swift index 854ca0b26..9b6105e56 100644 --- a/Split/Matchers/PrerequisitesMatcher.swift +++ b/Split/Matchers/PrerequisitesMatcher.swift @@ -7,7 +7,7 @@ class PrerequisitesMatcher: BaseMatcher, MatcherProtocol { private var prerequisites: [Prerequisite]? - init(prerequisites: [Prerequisite]? = nil) { + init(_ prerequisites: [Prerequisite]? = nil) { self.prerequisites = prerequisites } @@ -20,12 +20,12 @@ class PrerequisitesMatcher: BaseMatcher, MatcherProtocol { guard !prerequisite.ts.isEmpty else { return true } do { - let evalResult = try context?.evaluator?.evalTreatment(matchingKey: values.matchingKey, bucketingKey: values.bucketingKey, splitName: prerequisite.n, attributes: nil) + guard let treatment = try context?.evaluator?.evalTreatment(matchingKey: values.matchingKey, bucketingKey: values.bucketingKey, splitName: prerequisite.n, attributes: nil).treatment else { + continue + } - if let treatment = evalResult?.treatment { - if !prerequisite.ts.contains(treatment) { - return false - } + if !prerequisite.ts.contains(treatment) { // ts = Prerequisite treatments list + return false } } catch { Logger.e("Error evaluating condition in PrerequisitesMatcher: \(error)") diff --git a/SplitTests/Matcher/PrerequisitesMatcherTest.swift b/SplitTests/Matcher/PrerequisitesMatcherTest.swift index bcdc9ce6f..cf496b13f 100644 --- a/SplitTests/Matcher/PrerequisitesMatcherTest.swift +++ b/SplitTests/Matcher/PrerequisitesMatcherTest.swift @@ -8,25 +8,28 @@ class PrerequisitesMatcherTests: XCTestCase { private var storage = SplitsStorageStub() private var context: EvalContext? + private var values: EvalValues! + + override func setUp() { + splitsSetup() + } func testPrerequisiteMet() { let prerequisites = [ Prerequisite(n: "always_on", ts: ["not-existing", "on", "other"]) ] - let SUT = PrerequisitesMatcher(prerequisites: prerequisites) - let values = EvalValues(matchValue: "", matchingKey: "", bucketingKey: nil) + let SUT = PrerequisitesMatcher(prerequisites) XCTAssertTrue(SUT.evaluate(values: values, context: context), "If a prerequisite is met it should return true") } - func testPrerequisiteMet2() { // To conform to the specs + func testPrerequisiteMet2() { let prerequisites = [ Prerequisite(n: "always_off", ts: ["not-existing", "off"]) ] - let SUT = PrerequisitesMatcher(prerequisites: prerequisites) - let values = EvalValues(matchValue: "", matchingKey: "", bucketingKey: nil) + let SUT = PrerequisitesMatcher(prerequisites) XCTAssertTrue(SUT.evaluate(values: values, context: context), "If a prerequisite is met it should return true") } @@ -36,19 +39,17 @@ class PrerequisitesMatcherTests: XCTestCase { Prerequisite(n: "always_on", ts: ["off", "v1"]) ] - let SUT = PrerequisitesMatcher(prerequisites: prerequisites) - let values = EvalValues(matchValue: "", matchingKey: "", bucketingKey: nil) + let SUT = PrerequisitesMatcher(prerequisites) XCTAssertFalse(SUT.evaluate(values: values, context: context), "If just one prerequisite is not met it should return false") } - func testPrerequisiteNotMet2() { // To conform to the specs + func testPrerequisiteNotMet2() { let prerequisites = [ Prerequisite(n: "always_off", ts: ["on", "v1"]) ] - let SUT = PrerequisitesMatcher(prerequisites: prerequisites) - let values = EvalValues(matchValue: "", matchingKey: "", bucketingKey: nil) + let SUT = PrerequisitesMatcher(prerequisites) XCTAssertFalse(SUT.evaluate(values: values, context: context), "If just one prerequisite is not met it should return false") } @@ -59,8 +60,7 @@ class PrerequisitesMatcherTests: XCTestCase { Prerequisite(n: "always_off", ts: ["off"]) ] - let SUT = PrerequisitesMatcher(prerequisites: prerequisites) - let values = EvalValues(matchValue: "", matchingKey: "", bucketingKey: nil) + let SUT = PrerequisitesMatcher(prerequisites) XCTAssertTrue(SUT.evaluate(values: values, context: context), "If all prerequisites are met it should return true") } @@ -71,30 +71,26 @@ class PrerequisitesMatcherTests: XCTestCase { Prerequisite(n: "always_off", ts: ["on"]) ] - let SUT = PrerequisitesMatcher(prerequisites: prerequisites) - let values = EvalValues(matchValue: "", matchingKey: "", bucketingKey: nil) + let SUT = PrerequisitesMatcher(prerequisites) XCTAssertFalse(SUT.evaluate(values: values, context: context), "If any prerequisite is not met it should return false") } //MARK: Edge cases func testNoPrerequisites() { - let SUT = PrerequisitesMatcher(prerequisites: nil) - let values = EvalValues(matchValue: "on", matchingKey: "key", bucketingKey: nil) + let SUT = PrerequisitesMatcher(nil) XCTAssertTrue(SUT.evaluate(values: values, context: context), "If there is no prerequisites, it should return true") } func testEmptyPrerequisites() { - let SUT = PrerequisitesMatcher(prerequisites: []) - let values = EvalValues(matchValue: "on", matchingKey: "key", bucketingKey: nil) + let SUT = PrerequisitesMatcher([]) XCTAssertTrue(SUT.evaluate(values: values, context: context), "If prerequisites exists but it's just empty, it should return true") } func testNonExistentFeatureFlag() { - let SUT = PrerequisitesMatcher(prerequisites: [Prerequisite(n: "asldjh38", ts: ["on"])]) - let values = EvalValues(matchValue: "on", matchingKey: "key", bucketingKey: nil) + let SUT = PrerequisitesMatcher([Prerequisite(n: "asldjh38", ts: ["on"])]) XCTAssertFalse(SUT.evaluate(values: values, context: context), "If the feature flag is non existent it should return false") } @@ -103,7 +99,8 @@ class PrerequisitesMatcherTests: XCTestCase { //MARK: Testing Data extension PrerequisitesMatcherTests { - override func setUp() { + + func splitsSetup() { // SPLIT 1 let split = SplitDTO(name: "always_on", trafficType: "user", status: .active, sets: [], json: "", killed: false, impressionsDisabled: false) @@ -181,5 +178,7 @@ extension PrerequisitesMatcherTests { storage.updateWithoutChecks(split: split2) context = EvalContext(evaluator: DefaultEvaluator(splitsStorage: storage, mySegmentsStorage: MySegmentsStorageStub()), mySegmentsStorage: MySegmentsStorageStub(), myLargeSegmentsStorage: nil, ruleBasedSegmentsStorage: nil) + + values = EvalValues(matchValue: "", matchingKey: "", bucketingKey: nil) } } From f1e668e281fc2c6269754cd1af01a6d30bdd8fa1 Mon Sep 17 00:00:00 2001 From: Martin Cardozo Date: Tue, 27 May 2025 15:01:44 -0300 Subject: [PATCH 20/46] Pull request comments fixed. Tests added --- Split.xcodeproj/project.pbxproj | 4 + Split/Engine/Evaluator.swift | 18 ++--- Split/Matchers/PrerequisitesMatcher.swift | 6 +- SplitTests/EvaluatorTests.swift | 76 ++++++++++++------- .../Fake/PrerequisitesMatcherMock.swift | 17 +++++ 5 files changed, 83 insertions(+), 38 deletions(-) create mode 100644 SplitTests/Fake/PrerequisitesMatcherMock.swift diff --git a/Split.xcodeproj/project.pbxproj b/Split.xcodeproj/project.pbxproj index b34cf6aff..57dcb7801 100644 --- a/Split.xcodeproj/project.pbxproj +++ b/Split.xcodeproj/project.pbxproj @@ -356,6 +356,7 @@ 5B91B8392DDE4A3B000510F0 /* SplitDTOTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B91B8382DDE4A30000510F0 /* SplitDTOTests.swift */; }; 5BF52DFB2DE5095800FEDAFE /* PrerequisitesMatcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BF52DFA2DE5095300FEDAFE /* PrerequisitesMatcher.swift */; }; 5BF52DFC2DE5095800FEDAFE /* PrerequisitesMatcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BF52DFA2DE5095300FEDAFE /* PrerequisitesMatcher.swift */; }; + 5BF52E032DE62F0500FEDAFE /* PrerequisitesMatcherMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BF52E022DE62EFE00FEDAFE /* PrerequisitesMatcherMock.swift */; }; 9500D9922C56F9BA00383593 /* HostDomainFilterTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9500D9912C56F9BA00383593 /* HostDomainFilterTests.swift */; }; 9500D9A92C59297400383593 /* HostDomainFilter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9500D9A82C59297400383593 /* HostDomainFilter.swift */; }; 9500D9AA2C59382000383593 /* HostDomainFilter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9500D9A82C59297400383593 /* HostDomainFilter.swift */; }; @@ -1557,6 +1558,7 @@ 59FB7C3D22037B9400ECC96A /* SpaceDelimitedLocalhostSplitsParser.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SpaceDelimitedLocalhostSplitsParser.swift; sourceTree = ""; }; 5B91B8382DDE4A30000510F0 /* SplitDTOTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SplitDTOTests.swift; sourceTree = ""; }; 5BF52DFA2DE5095300FEDAFE /* PrerequisitesMatcher.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PrerequisitesMatcher.swift; sourceTree = ""; }; + 5BF52E022DE62EFE00FEDAFE /* PrerequisitesMatcherMock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PrerequisitesMatcherMock.swift; sourceTree = ""; }; 9500D9912C56F9BA00383593 /* HostDomainFilterTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HostDomainFilterTests.swift; sourceTree = ""; }; 9500D9A82C59297400383593 /* HostDomainFilter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HostDomainFilter.swift; sourceTree = ""; }; 9500D9AC2C5A918300383593 /* split_cache_v5.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = split_cache_v5.xcdatamodel; sourceTree = ""; }; @@ -2765,6 +2767,7 @@ 590DF9CD213EB92D0082B94F /* Fake */ = { isa = PBXGroup; children = ( + 5BF52E022DE62EFE00FEDAFE /* PrerequisitesMatcherMock.swift */, C539CAE12D9477770050C732 /* PropertyValidatorStub.swift */, 954F9C73257961AA00140B81 /* Service */, 5905D4E6255B23C8006DA3B1 /* Storage */, @@ -4761,6 +4764,7 @@ 95B1802B2763DE4B002DC9DF /* HttpTelemetryStatsRecorderTest.swift in Sources */, C5977C102BF28846003E293A /* BetweenSemverMatcherTest.swift in Sources */, 95B17FF5275EAA6F002DC9DF /* InMemoryTelemetryStorageTest.swift in Sources */, + 5BF52E032DE62F0500FEDAFE /* PrerequisitesMatcherMock.swift in Sources */, 95ABF511293ABDDC006ED016 /* ImpressionsStorageStub.swift in Sources */, 955E124D2C010B9000AE6D10 /* ImpressionsObserverMock.swift in Sources */, 9557C1CA2614F67700CD9B5C /* DbForTwoDifferentApiKeyTest.swift in Sources */, diff --git a/Split/Engine/Evaluator.swift b/Split/Engine/Evaluator.swift index e00488b5d..0afe0bd5b 100644 --- a/Split/Engine/Evaluator.swift +++ b/Split/Engine/Evaluator.swift @@ -14,6 +14,7 @@ class DefaultEvaluator: Evaluator { // Internal for testing purposes var splitter: SplitterProtocol = Splitter.shared + var prerequisitesMatcher: PrerequisitesMatcherProtocol = PrerequisitesMatcher() private let splitsStorage: SplitsStorage private let mySegmentsStorage: MySegmentsStorage @@ -28,34 +29,33 @@ class DefaultEvaluator: Evaluator { } func evalTreatment(matchingKey: String, bucketingKey: String?, splitName: String, attributes: [String: Any]?) throws -> EvaluationResult { - + // 1. Guarantee Split exists & is active guard let split = splitsStorage.get(name: splitName), split.status != .archived else { - Logger.w("The feature flag definition for '\(splitName)' not found") + Logger.w("The feature flag definition for '\(splitName)' has not been found") return EvaluationResult(treatment: SplitConstants.control, label: ImpressionsConstants.splitNotFound) } - // 2. Extract neccesary info + // 2. Guarantee is not killed let changeNumber = split.changeNumber ?? -1 let defaultTreatment = split.defaultTreatment ?? SplitConstants.control - let bucketKey = selectBucketKey(matchingKey: matchingKey, bucketingKey: bucketingKey) - let values = EvalValues(matchValue: matchingKey, matchingKey: matchingKey, bucketingKey: bucketKey, attributes: attributes) - - // 3. Guarantee is not killed guard let killed = split.killed, !killed else { return EvaluationResult(treatment: defaultTreatment, label: ImpressionsConstants.killed, changeNumber: changeNumber, configuration: split.configurations?[defaultTreatment], impressionsDisabled: split.isImpressionsDisabled()) } + // 3. Extract necessary info + let bucketKey = selectBucketKey(matchingKey: matchingKey, bucketingKey: bucketingKey) + let values = EvalValues(matchValue: matchingKey, matchingKey: matchingKey, bucketingKey: bucketKey, attributes: attributes) + // 4. Evaluate Prerequisites - if !PrerequisitesMatcher().evaluate(values: values, context: getContext()) { + if !prerequisitesMatcher.evaluate(values: values, context: getContext()) { return EvaluationResult(treatment: defaultTreatment, label: ImpressionsConstants.prerequisitesNotMet, changeNumber: changeNumber, configuration: split.configurations?[defaultTreatment], impressionsDisabled: split.isImpressionsDisabled()) } - // 5. Evaluate core conditions guard let conditions = split.conditions, let trafficAllocationSeed = split.trafficAllocationSeed, let seed = split.seed else { diff --git a/Split/Matchers/PrerequisitesMatcher.swift b/Split/Matchers/PrerequisitesMatcher.swift index 623fd3fdc..dbd1ddb08 100644 --- a/Split/Matchers/PrerequisitesMatcher.swift +++ b/Split/Matchers/PrerequisitesMatcher.swift @@ -3,8 +3,12 @@ import Foundation -class PrerequisitesMatcher: BaseMatcher, MatcherProtocol { +protocol PrerequisitesMatcherProtocol { + func evaluate(values: EvalValues, context: EvalContext?) -> Bool +} +class PrerequisitesMatcher: BaseMatcher, MatcherProtocol, PrerequisitesMatcherProtocol { + private var prerequisites: [Prerequisite]? init(prerequisites: [Prerequisite]? = nil) { diff --git a/SplitTests/EvaluatorTests.swift b/SplitTests/EvaluatorTests.swift index 6f97db834..f2168c7fb 100644 --- a/SplitTests/EvaluatorTests.swift +++ b/SplitTests/EvaluatorTests.swift @@ -43,10 +43,9 @@ class EvaluatorTests: XCTestCase { var result: EvaluationResult! let matchingKey = "nico_test" let splitName = "FACUNDO_TEST" - do { - result = try evaluator.evalTreatment(matchingKey: matchingKey, bucketingKey: nil, splitName: splitName, attributes: nil) - } catch { - } + + result = try? evaluator.evalTreatment(matchingKey: matchingKey, bucketingKey: nil, splitName: splitName, attributes: nil) + XCTAssertNotNil(result) XCTAssertEqual("on", result.treatment) XCTAssertNil(result.configuration) @@ -57,10 +56,9 @@ class EvaluatorTests: XCTestCase { var result: EvaluationResult! let matchingKey = "bla" let splitName = "FACUNDO_TEST" - do { - result = try evaluator.evalTreatment(matchingKey: matchingKey, bucketingKey: nil, splitName: splitName, attributes: nil) - } catch { - } + + result = try? evaluator.evalTreatment(matchingKey: matchingKey, bucketingKey: nil, splitName: splitName, attributes: nil) + XCTAssertNotNil(result) XCTAssertEqual("off", result.treatment) XCTAssertNil(result.configuration) @@ -71,10 +69,9 @@ class EvaluatorTests: XCTestCase { var result: EvaluationResult! let matchingKey = "anyKey" let splitName = "FACUNDO_TEST" - do { - result = try evaluator.evalTreatment(matchingKey: matchingKey, bucketingKey: nil, splitName: splitName, attributes: nil) - } catch { - } + + result = try? evaluator.evalTreatment(matchingKey: matchingKey, bucketingKey: nil, splitName: splitName, attributes: nil) + XCTAssertNotNil(result) XCTAssertEqual("off", result.treatment) XCTAssertNil(result.configuration) @@ -85,10 +82,9 @@ class EvaluatorTests: XCTestCase { func testInSegmentTestKey() { var result: EvaluationResult! let splitName = "a_new_split_2" - do { - result = try evaluator.evalTreatment(matchingKey: matchingKey, bucketingKey: nil, splitName: splitName, attributes: nil) - } catch { - } + + result = try? evaluator.evalTreatment(matchingKey: matchingKey, bucketingKey: nil, splitName: splitName, attributes: nil) + XCTAssertNotNil(result) XCTAssertEqual("off", result.treatment) XCTAssertNil(result.configuration) @@ -100,24 +96,49 @@ class EvaluatorTests: XCTestCase { var result: EvaluationResult! let matchingKey = "anyKey" let splitName = "OldTest" - do { - result = try evaluator.evalTreatment(matchingKey: matchingKey, bucketingKey: nil, splitName: splitName, attributes: nil) - } catch { - } + + result = try? evaluator.evalTreatment(matchingKey: matchingKey, bucketingKey: nil, splitName: splitName, attributes: nil) + XCTAssertNotNil(result) XCTAssertEqual("off", result.treatment) XCTAssertNotNil(result.configuration) XCTAssertEqual(ImpressionsConstants.killed, result.label) } + func testPassingPrerequisites() { + var result: EvaluationResult! + let matchingKey = "anyKey" + let splitName = "FACUNDO_TEST" + + (evaluator as! DefaultEvaluator).prerequisitesMatcher = PrerequisitesMatcherMock(shouldPass: true) + + result = try? evaluator.evalTreatment(matchingKey: matchingKey, bucketingKey: nil, splitName: splitName, attributes: nil) + + XCTAssertNotNil(result) + XCTAssertNotNil(result.configuration) + XCTAssertEqual("off", result.treatment) + } + + func testNotPassingPrerequisites() { + var result: EvaluationResult! + let matchingKey = "anyKey" + let splitName = "FACUNDO_TEST" + + (evaluator as! DefaultEvaluator).prerequisitesMatcher = PrerequisitesMatcherMock(shouldPass: false) + + result = try? evaluator.evalTreatment(matchingKey: matchingKey, bucketingKey: nil, splitName: splitName, attributes: nil) + + XCTAssertNotNil(result) + XCTAssertEqual(ImpressionsConstants.prerequisitesNotMet, result.label) + } + func testNotInSplit() { var result: EvaluationResult! let matchingKey = "anyKey" let splitName = "split_not_available_to_test_right_now" - do { - result = try evaluator.evalTreatment(matchingKey: matchingKey, bucketingKey: nil, splitName: splitName, attributes: nil) - } catch { - } + + result = try? evaluator.evalTreatment(matchingKey: matchingKey, bucketingKey: nil, splitName: splitName, attributes: nil) + XCTAssertNotNil(result) XCTAssertEqual(SplitConstants.control, result.treatment) XCTAssertNil(result.configuration) @@ -128,10 +149,9 @@ class EvaluatorTests: XCTestCase { var result: EvaluationResult! let matchingKey = "anyKey" let splitName = "broken_split" - do { - result = try evaluator.evalTreatment(matchingKey: matchingKey, bucketingKey: nil, splitName: splitName, attributes: nil) - } catch { - } + + result = try? evaluator.evalTreatment(matchingKey: matchingKey, bucketingKey: nil, splitName: splitName, attributes: nil) + XCTAssertNotNil(result) XCTAssertEqual(SplitConstants.control, result.treatment) XCTAssertNil(result.configuration) diff --git a/SplitTests/Fake/PrerequisitesMatcherMock.swift b/SplitTests/Fake/PrerequisitesMatcherMock.swift new file mode 100644 index 000000000..26bdb5592 --- /dev/null +++ b/SplitTests/Fake/PrerequisitesMatcherMock.swift @@ -0,0 +1,17 @@ +// Created by Martin Cardozo on 27/05/2025. +// Copyright © 2025 Split. All rights reserved. + +@testable import Split + +class PrerequisitesMatcherMock: BaseMatcher, MatcherProtocol, PrerequisitesMatcherProtocol { + + private let returnValue: Bool + + init(shouldPass: Bool) { + self.returnValue = shouldPass + } + + func evaluate(values: EvalValues, context: EvalContext?) -> Bool { + returnValue + } +} From 40239d6cfbb92fbe254a9a88a8f363cbb21aa3e0 Mon Sep 17 00:00:00 2001 From: Martin Cardozo Date: Tue, 27 May 2025 15:08:55 -0300 Subject: [PATCH 21/46] Fixed test --- SplitTests/EvaluatorTests.swift | 1 - 1 file changed, 1 deletion(-) diff --git a/SplitTests/EvaluatorTests.swift b/SplitTests/EvaluatorTests.swift index f2168c7fb..18cac1305 100644 --- a/SplitTests/EvaluatorTests.swift +++ b/SplitTests/EvaluatorTests.swift @@ -115,7 +115,6 @@ class EvaluatorTests: XCTestCase { result = try? evaluator.evalTreatment(matchingKey: matchingKey, bucketingKey: nil, splitName: splitName, attributes: nil) XCTAssertNotNil(result) - XCTAssertNotNil(result.configuration) XCTAssertEqual("off", result.treatment) } From 76830ec19a25afb874e4c195f82ab847aab38897 Mon Sep 17 00:00:00 2001 From: Martin Cardozo Date: Wed, 28 May 2025 10:18:17 -0300 Subject: [PATCH 22/46] New coding keys --- .../SplitView+StringConvertible.swift | 2 +- Split/Matchers/PrerequisitesMatcher.swift | 6 ++--- Split/Models/SplitModel/Split.swift | 23 ++++++++++++++----- SplitTests/SplitDTOTests.swift | 6 ++--- 4 files changed, 24 insertions(+), 13 deletions(-) diff --git a/Split/Common/Extensions/SplitView+StringConvertible.swift b/Split/Common/Extensions/SplitView+StringConvertible.swift index 336becf1f..59767261c 100644 --- a/Split/Common/Extensions/SplitView+StringConvertible.swift +++ b/Split/Common/Extensions/SplitView+StringConvertible.swift @@ -35,7 +35,7 @@ extension SplitView { output+="prerequisites = [\n" prerequisites.forEach { prerequisite in output+=""" - \(prerequisite.n): {\(prerequisite.ts.joined(separator: ","))}\n + \(prerequisite.flagName): {\(prerequisite.treatments.joined(separator: ","))}\n """ } output+="]\n" diff --git a/Split/Matchers/PrerequisitesMatcher.swift b/Split/Matchers/PrerequisitesMatcher.swift index 9b6105e56..d93a0e90e 100644 --- a/Split/Matchers/PrerequisitesMatcher.swift +++ b/Split/Matchers/PrerequisitesMatcher.swift @@ -17,14 +17,14 @@ class PrerequisitesMatcher: BaseMatcher, MatcherProtocol { guard let prerequisites = prerequisites, !prerequisites.isEmpty else { return true } for prerequisite in prerequisites { - guard !prerequisite.ts.isEmpty else { return true } + guard !prerequisite.treatments.isEmpty else { return true } do { - guard let treatment = try context?.evaluator?.evalTreatment(matchingKey: values.matchingKey, bucketingKey: values.bucketingKey, splitName: prerequisite.n, attributes: nil).treatment else { + guard let treatment = try context?.evaluator?.evalTreatment(matchingKey: values.matchingKey, bucketingKey: values.bucketingKey, splitName: prerequisite.flagName, attributes: nil).treatment else { continue } - if !prerequisite.ts.contains(treatment) { // ts = Prerequisite treatments list + if !prerequisite.treatments.contains(treatment) { // ts = Prerequisite treatments list return false } } catch { diff --git a/Split/Models/SplitModel/Split.swift b/Split/Models/SplitModel/Split.swift index 333a24ad5..bfcb2784e 100644 --- a/Split/Models/SplitModel/Split.swift +++ b/Split/Models/SplitModel/Split.swift @@ -60,13 +60,24 @@ class SplitDTO: NSObject, SplitBase, Codable { } @objc public class Prerequisite: NSObject, Codable { - var n: String - var ts: [String] - + @objc public var flagName: String + @objc public var treatments: [String] + + enum CodingKeys: String, CodingKey { + case flagName = "n" + case treatments = "ts" + } + + required public init(from decoder: Decoder) throws { + let container = try decoder.container(keyedBy: CodingKeys.self) + self.flagName = try container.decodeIfPresent(String.self, forKey: .flagName) ?? "" + self.treatments = try container.decodeIfPresent([String].self, forKey: .treatments) ?? [] + } + #if DEBUG - init(n: String, ts: [String]) { - self.n = n - self.ts = ts + init(flagName: String, treatments: [String]) { + self.flagName = flagName + self.treatments = treatments } #endif } diff --git a/SplitTests/SplitDTOTests.swift b/SplitTests/SplitDTOTests.swift index a7d4643c4..ecea21218 100644 --- a/SplitTests/SplitDTOTests.swift +++ b/SplitTests/SplitDTOTests.swift @@ -16,8 +16,8 @@ class SplitDTOTests: XCTestCase { let decoded = try? TargetingRulesChangeDecoder.decode(from: data.data(using: .utf8)!) for i in 0.. Date: Wed, 28 May 2025 10:22:59 -0300 Subject: [PATCH 23/46] Tests --- SplitiOSUnit_1.xctestplan | 14 ++++++++++++++ SplitiOSUnit_2.xctestplan | 23 +++++++++++++++++++++++ SplitiOSUnit_3.xctestplan | 14 ++++++++++++++ 3 files changed, 51 insertions(+) diff --git a/SplitiOSUnit_1.xctestplan b/SplitiOSUnit_1.xctestplan index ce56ef22a..544b81db0 100644 --- a/SplitiOSUnit_1.xctestplan +++ b/SplitiOSUnit_1.xctestplan @@ -693,6 +693,16 @@ "PersistentUniqueKeysStorageTests\/testPop()", "PersistentUniqueKeysStorageTests\/testPush()", "PersistentUniqueKeysStorageTests\/testSetActive()", + "PrerequisitesMatcherTests", + "PrerequisitesMatcherTests\/testEmptyPrerequisites()", + "PrerequisitesMatcherTests\/testMultiplePrerequisites()", + "PrerequisitesMatcherTests\/testMultiplePrerequisites2()", + "PrerequisitesMatcherTests\/testNoPrerequisites()", + "PrerequisitesMatcherTests\/testNonExistentFeatureFlag()", + "PrerequisitesMatcherTests\/testPrerequisiteMet()", + "PrerequisitesMatcherTests\/testPrerequisiteMet2()", + "PrerequisitesMatcherTests\/testPrerequisiteNotMet()", + "PrerequisitesMatcherTests\/testPrerequisiteNotMet2()", "PushManagerEventBroadcasterTest", "PushManagerEventBroadcasterTest\/testRegister()", "PushManagerEventBroadcasterTest\/testStop()", @@ -806,6 +816,10 @@ "SplitConfigurationsParsingTest\/testEncodingNestedMultiConfig()", "SplitConfigurationsParsingTest\/testEncodingNullConfig()", "SplitConfigurationsParsingTest\/testEncodingOneBasicConfig()", + "SplitDTOTests", + "SplitDTOTests\/testCorrectPrerequisites()", + "SplitDTOTests\/testEmptyPrerequisites()", + "SplitDTOTests\/testMalformedPrerequisites()", "SplitDaoTest", "SplitDaoTest\/testCreateGetAes128Cbc()", "SplitDaoTest\/testCreateGetPlainText()", diff --git a/SplitiOSUnit_2.xctestplan b/SplitiOSUnit_2.xctestplan index 5314bc907..2a114cdf9 100644 --- a/SplitiOSUnit_2.xctestplan +++ b/SplitiOSUnit_2.xctestplan @@ -184,6 +184,7 @@ "EqualToSemverMatcherTest\/testMatchShouldReturnTrueWhenVersionsAreEqual()", "EqualToSemverMatcherTest\/testMatchWithMetadataShouldReturnFalseWhenVersionsDiffer()", "EqualToSemverMatcherTest\/testMatchWithPreReleaseShouldReturnTrueWhenVersionsAreEqual()", + "EvaluatorTests", "EvaluatorTests\/testAlgoLegacy()", "EvaluatorTests\/testAlgoMurmur3()", "EvaluatorTests\/testAlgoNull()", @@ -192,15 +193,23 @@ "EvaluatorTests\/testDefaultRule()", "EvaluatorTests\/testDefaultTreatment()", "EvaluatorTests\/testDefaultTreatmentFacundo()", + "EvaluatorTests\/testEqualsToSetConfigTreatment()", "EvaluatorTests\/testEqualsToSetNoConfigTreatment()", + "EvaluatorTests\/testImpressionsDisabledFalse()", + "EvaluatorTests\/testImpressionsDisabledTrue()", "EvaluatorTests\/testInLargeSegmentWhitelist()", "EvaluatorTests\/testInSegmentTestKey()", "EvaluatorTests\/testInSegmentsRule1()", "EvaluatorTests\/testKilledSplit()", + "EvaluatorTests\/testMatchesStringNoConfigTreatment()", + "EvaluatorTests\/testMissingDefaultRule()", "EvaluatorTests\/testNotInLargeSegmentWhitelist()", "EvaluatorTests\/testNotInSplit()", + "EvaluatorTests\/testNotPassingPrerequisites()", + "EvaluatorTests\/testPassingPrerequisites()", "EvaluatorTests\/testWhitelisted()", "EvaluatorTests\/testWhitelistedOff()", + "EvaluatorTests\/testimpressionsDisabledNil()", "EvaluatorTests\/testsTrafficAllocation50DefaultRule50()", "EventDTOJsonTest", "EventDTOJsonTest\/testBasic()", @@ -692,6 +701,16 @@ "PersistentUniqueKeysStorageTests\/testPop()", "PersistentUniqueKeysStorageTests\/testPush()", "PersistentUniqueKeysStorageTests\/testSetActive()", + "PrerequisitesMatcherTests", + "PrerequisitesMatcherTests\/testEmptyPrerequisites()", + "PrerequisitesMatcherTests\/testMultiplePrerequisites()", + "PrerequisitesMatcherTests\/testMultiplePrerequisites2()", + "PrerequisitesMatcherTests\/testNoPrerequisites()", + "PrerequisitesMatcherTests\/testNonExistentFeatureFlag()", + "PrerequisitesMatcherTests\/testPrerequisiteMet()", + "PrerequisitesMatcherTests\/testPrerequisiteMet2()", + "PrerequisitesMatcherTests\/testPrerequisiteNotMet()", + "PrerequisitesMatcherTests\/testPrerequisiteNotMet2()", "PushManagerEventBroadcasterTest", "PushManagerEventBroadcasterTest\/testRegister()", "PushNotificationManagerTest", @@ -808,6 +827,10 @@ "SplitConfigurationsParsingTest\/testEncodingNestedMultiConfig()", "SplitConfigurationsParsingTest\/testEncodingNullConfig()", "SplitConfigurationsParsingTest\/testEncodingOneBasicConfig()", + "SplitDTOTests", + "SplitDTOTests\/testCorrectPrerequisites()", + "SplitDTOTests\/testEmptyPrerequisites()", + "SplitDTOTests\/testMalformedPrerequisites()", "SplitDaoTest", "SplitDaoTest\/testCreateGetAes128Cbc()", "SplitDaoTest\/testCreateGetPlainText()", diff --git a/SplitiOSUnit_3.xctestplan b/SplitiOSUnit_3.xctestplan index 9139c2250..0ada2a57b 100644 --- a/SplitiOSUnit_3.xctestplan +++ b/SplitiOSUnit_3.xctestplan @@ -698,6 +698,16 @@ "PersistentUniqueKeysStorageTests\/testPop()", "PersistentUniqueKeysStorageTests\/testPush()", "PersistentUniqueKeysStorageTests\/testSetActive()", + "PrerequisitesMatcherTests", + "PrerequisitesMatcherTests\/testEmptyPrerequisites()", + "PrerequisitesMatcherTests\/testMultiplePrerequisites()", + "PrerequisitesMatcherTests\/testMultiplePrerequisites2()", + "PrerequisitesMatcherTests\/testNoPrerequisites()", + "PrerequisitesMatcherTests\/testNonExistentFeatureFlag()", + "PrerequisitesMatcherTests\/testPrerequisiteMet()", + "PrerequisitesMatcherTests\/testPrerequisiteMet2()", + "PrerequisitesMatcherTests\/testPrerequisiteNotMet()", + "PrerequisitesMatcherTests\/testPrerequisiteNotMet2()", "PushManagerEventBroadcasterTest", "PushManagerEventBroadcasterTest\/testRegister()", "PushManagerEventBroadcasterTest\/testStop()", @@ -811,6 +821,10 @@ "SplitConfigurationsParsingTest\/testEncodingNestedMultiConfig()", "SplitConfigurationsParsingTest\/testEncodingNullConfig()", "SplitConfigurationsParsingTest\/testEncodingOneBasicConfig()", + "SplitDTOTests", + "SplitDTOTests\/testCorrectPrerequisites()", + "SplitDTOTests\/testEmptyPrerequisites()", + "SplitDTOTests\/testMalformedPrerequisites()", "SplitDaoTest", "SplitDaoTest\/testCreateGetAes128Cbc()", "SplitDaoTest\/testCreateGetPlainText()", From efeb85ba6608fe6e7b5cd294e793d3e28952fe3d Mon Sep 17 00:00:00 2001 From: Martin Cardozo Date: Wed, 28 May 2025 10:27:18 -0300 Subject: [PATCH 24/46] Fixes on paramters properties --- .../Matcher/PrerequisitesMatcherTest.swift | 18 +++++++-------- SplitTests/SplitManagerTest.swift | 4 ++-- SplitiOSUnit_4.xctestplan | 22 ------------------- 3 files changed, 11 insertions(+), 33 deletions(-) diff --git a/SplitTests/Matcher/PrerequisitesMatcherTest.swift b/SplitTests/Matcher/PrerequisitesMatcherTest.swift index cf496b13f..f2888cb58 100644 --- a/SplitTests/Matcher/PrerequisitesMatcherTest.swift +++ b/SplitTests/Matcher/PrerequisitesMatcherTest.swift @@ -16,7 +16,7 @@ class PrerequisitesMatcherTests: XCTestCase { func testPrerequisiteMet() { let prerequisites = [ - Prerequisite(n: "always_on", ts: ["not-existing", "on", "other"]) + Prerequisite(flagName: "always_on", treatments: ["not-existing", "on", "other"]) ] let SUT = PrerequisitesMatcher(prerequisites) @@ -26,7 +26,7 @@ class PrerequisitesMatcherTests: XCTestCase { func testPrerequisiteMet2() { let prerequisites = [ - Prerequisite(n: "always_off", ts: ["not-existing", "off"]) + Prerequisite(flagName: "always_off", treatments: ["not-existing", "off"]) ] let SUT = PrerequisitesMatcher(prerequisites) @@ -36,7 +36,7 @@ class PrerequisitesMatcherTests: XCTestCase { func testPrerequisiteNotMet() { let prerequisites = [ - Prerequisite(n: "always_on", ts: ["off", "v1"]) + Prerequisite(flagName: "always_on", treatments: ["off", "v1"]) ] let SUT = PrerequisitesMatcher(prerequisites) @@ -46,7 +46,7 @@ class PrerequisitesMatcherTests: XCTestCase { func testPrerequisiteNotMet2() { let prerequisites = [ - Prerequisite(n: "always_off", ts: ["on", "v1"]) + Prerequisite(flagName: "always_off", treatments: ["on", "v1"]) ] let SUT = PrerequisitesMatcher(prerequisites) @@ -56,8 +56,8 @@ class PrerequisitesMatcherTests: XCTestCase { func testMultiplePrerequisites() { let prerequisites = [ - Prerequisite(n: "always_on", ts: ["on"]), - Prerequisite(n: "always_off", ts: ["off"]) + Prerequisite(flagName: "always_on", treatments: ["on"]), + Prerequisite(flagName: "always_off", treatments: ["off"]) ] let SUT = PrerequisitesMatcher(prerequisites) @@ -67,8 +67,8 @@ class PrerequisitesMatcherTests: XCTestCase { func testMultiplePrerequisites2() { let prerequisites = [ - Prerequisite(n: "always_on", ts: ["on"]), - Prerequisite(n: "always_off", ts: ["on"]) + Prerequisite(flagName: "always_on", treatments: ["on"]), + Prerequisite(flagName: "always_off", treatments: ["on"]) ] let SUT = PrerequisitesMatcher(prerequisites) @@ -90,7 +90,7 @@ class PrerequisitesMatcherTests: XCTestCase { } func testNonExistentFeatureFlag() { - let SUT = PrerequisitesMatcher([Prerequisite(n: "asldjh38", ts: ["on"])]) + let SUT = PrerequisitesMatcher([Prerequisite(flagName: "asldjh38", treatments: ["on"])]) XCTAssertFalse(SUT.evaluate(values: values, context: context), "If the feature flag is non existent it should return false") } diff --git a/SplitTests/SplitManagerTest.swift b/SplitTests/SplitManagerTest.swift index a9ad4199e..ff3fa6d74 100644 --- a/SplitTests/SplitManagerTest.swift +++ b/SplitTests/SplitManagerTest.swift @@ -65,8 +65,8 @@ class SplitManagerTest: XCTestCase { XCTAssertEqual(split0?.sets?.sorted(), ["set1", "set2"]) XCTAssertNotNil(split0?.configs) XCTAssertTrue(split0?.impressionsDisabled ?? false, "Split0 track impressions") - XCTAssertEqual(split0?.prerequisites?.first?.n, "flag1") - XCTAssertEqual(split0?.prerequisites?.first?.ts.sorted(), ["on", "v1"]) + XCTAssertEqual(split0?.prerequisites?.first?.flagName, "flag1") + XCTAssertEqual(split0?.prerequisites?.first?.treatments.sorted(), ["on", "v1"]) XCTAssertEqual(treatments0?.count, 6, "Split0 treatment count") XCTAssertEqual(treatments0?.sorted().joined(separator: ",").lowercased(), "t1_0,t2_0,t3_0,t4_0,t5_0,t6_0", "Split0 treatment names") diff --git a/SplitiOSUnit_4.xctestplan b/SplitiOSUnit_4.xctestplan index 42f6bd966..4943d4b3e 100644 --- a/SplitiOSUnit_4.xctestplan +++ b/SplitiOSUnit_4.xctestplan @@ -179,28 +179,6 @@ "EqualToSemverMatcherTest\/testMatchWithMetadataShouldReturnTrueWhenVersionsAreEqual()", "EqualToSemverMatcherTest\/testMatchWithPreReleaseShouldReturnFalseWhenVersionsDiffer()", "EqualToSemverMatcherTest\/testMatchWithPreReleaseShouldReturnTrueWhenVersionsAreEqual()", - "EvaluatorTests", - "EvaluatorTests\/testAlgoLegacy()", - "EvaluatorTests\/testAlgoMurmur3()", - "EvaluatorTests\/testAlgoNull()", - "EvaluatorTests\/testAllocation1Percent()", - "EvaluatorTests\/testBrokenSplit()", - "EvaluatorTests\/testDefaultRule()", - "EvaluatorTests\/testDefaultTreatment()", - "EvaluatorTests\/testDefaultTreatmentFacundo()", - "EvaluatorTests\/testEqualsToSetConfigTreatment()", - "EvaluatorTests\/testEqualsToSetNoConfigTreatment()", - "EvaluatorTests\/testInLargeSegmentWhitelist()", - "EvaluatorTests\/testInSegmentTestKey()", - "EvaluatorTests\/testInSegmentsRule1()", - "EvaluatorTests\/testKilledSplit()", - "EvaluatorTests\/testMatchesStringNoConfigTreatment()", - "EvaluatorTests\/testMissingDefaultRule()", - "EvaluatorTests\/testNotInLargeSegmentWhitelist()", - "EvaluatorTests\/testNotInSplit()", - "EvaluatorTests\/testWhitelisted()", - "EvaluatorTests\/testWhitelistedOff()", - "EvaluatorTests\/testsTrafficAllocation50DefaultRule50()", "EventDTOJsonTest\/testBasic()", "EventDTOJsonTest\/testEncode()", "EventDTOJsonTest\/testNonNumber()", From 5065a232977e87c65b495dc3e8989a770748dfb2 Mon Sep 17 00:00:00 2001 From: Martin Cardozo Date: Wed, 28 May 2025 10:58:43 -0300 Subject: [PATCH 25/46] Prerequisites parameter name not needed now. --- Split/Matchers/PrerequisitesMatcher.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Split/Matchers/PrerequisitesMatcher.swift b/Split/Matchers/PrerequisitesMatcher.swift index 335fd810e..49af82b8e 100644 --- a/Split/Matchers/PrerequisitesMatcher.swift +++ b/Split/Matchers/PrerequisitesMatcher.swift @@ -11,7 +11,7 @@ class PrerequisitesMatcher: BaseMatcher, MatcherProtocol, PrerequisitesMatcherPr private var prerequisites: [Prerequisite]? - init(prerequisites: [Prerequisite]? = nil) { + init(_ prerequisites: [Prerequisite]? = nil) { self.prerequisites = prerequisites } From 34d9028935ae8524bdf0af7e188b236615afc2d4 Mon Sep 17 00:00:00 2001 From: Martin Cardozo Date: Wed, 28 May 2025 14:03:26 -0300 Subject: [PATCH 26/46] Improved Implementation --- Split/Engine/Evaluator.swift | 18 ++++++++++------ Split/Matchers/PrerequisitesMatcher.swift | 6 +----- SplitTests/EvaluatorTests.swift | 21 ++++++++----------- .../Fake/PrerequisitesMatcherMock.swift | 2 +- 4 files changed, 23 insertions(+), 24 deletions(-) diff --git a/Split/Engine/Evaluator.swift b/Split/Engine/Evaluator.swift index ca6b10885..897325dc3 100644 --- a/Split/Engine/Evaluator.swift +++ b/Split/Engine/Evaluator.swift @@ -12,15 +12,15 @@ protocol Evaluator { class DefaultEvaluator: Evaluator { - // Internal for testing purposes - var splitter: SplitterProtocol = Splitter.shared - var prerequisitesMatcher: PrerequisitesMatcherProtocol = PrerequisitesMatcher() + // For testing purposes + internal var splitter: SplitterProtocol = Splitter.shared + private var prerequisitesMatcherFactory: ([Prerequisite]) -> MatcherProtocol = { prerequisites in PrerequisitesMatcher(prerequisites) } private let splitsStorage: SplitsStorage private let mySegmentsStorage: MySegmentsStorage private let myLargeSegmentsStorage: MySegmentsStorage? private let ruleBasedSegmentsStorage: RuleBasedSegmentsStorage? - + init(splitsStorage: SplitsStorage, mySegmentsStorage: MySegmentsStorage, myLargeSegmentsStorage: MySegmentsStorage? = nil, ruleBasedSegmentsStorage: RuleBasedSegmentsStorage? = nil) { self.splitsStorage = splitsStorage self.mySegmentsStorage = mySegmentsStorage @@ -49,8 +49,8 @@ class DefaultEvaluator: Evaluator { let values = EvalValues(matchValue: matchingKey, matchingKey: matchingKey, bucketingKey: bucketKey, attributes: attributes) // 4. Evaluate Prerequisites - split.prerequisites - if !prerequisitesMatcher.evaluate(values: values, context: getContext()) { + let matcher = prerequisitesMatcherFactory(split.prerequisites ?? []) + if !matcher.evaluate(values: values, context: getContext()) { return EvaluationResult(treatment: defaultTreatment, label: ImpressionsConstants.prerequisitesNotMet, changeNumber: changeNumber, @@ -114,6 +114,12 @@ class DefaultEvaluator: Evaluator { return matchingKey } + + #if DEBUG + internal func overridePrerequisitesMatcher(_ factory: @escaping ([Prerequisite]) -> MatcherProtocol) { + prerequisitesMatcherFactory = factory + } + #endif } private extension Split { diff --git a/Split/Matchers/PrerequisitesMatcher.swift b/Split/Matchers/PrerequisitesMatcher.swift index 49af82b8e..62ea77640 100644 --- a/Split/Matchers/PrerequisitesMatcher.swift +++ b/Split/Matchers/PrerequisitesMatcher.swift @@ -3,11 +3,7 @@ import Foundation -protocol PrerequisitesMatcherProtocol { - func evaluate(values: EvalValues, context: EvalContext?) -> Bool -} - -class PrerequisitesMatcher: BaseMatcher, MatcherProtocol, PrerequisitesMatcherProtocol { +class PrerequisitesMatcher: BaseMatcher, MatcherProtocol { private var prerequisites: [Prerequisite]? diff --git a/SplitTests/EvaluatorTests.swift b/SplitTests/EvaluatorTests.swift index 18cac1305..1e6f96db7 100644 --- a/SplitTests/EvaluatorTests.swift +++ b/SplitTests/EvaluatorTests.swift @@ -106,29 +106,26 @@ class EvaluatorTests: XCTestCase { } func testPassingPrerequisites() { - var result: EvaluationResult! - let matchingKey = "anyKey" - let splitName = "FACUNDO_TEST" - (evaluator as! DefaultEvaluator).prerequisitesMatcher = PrerequisitesMatcherMock(shouldPass: true) + // This overrides the matcher-factory of the Evaluator, so we can inject the mock + (evaluator as! DefaultEvaluator).overridePrerequisitesMatcher( { _ in PrerequisitesMatcherMock(shouldPass: true) } ) - result = try? evaluator.evalTreatment(matchingKey: matchingKey, bucketingKey: nil, splitName: splitName, attributes: nil) + let result = try? evaluator.evalTreatment(matchingKey: "anyKey", bucketingKey: nil, splitName: "FACUNDO_TEST", attributes: nil) XCTAssertNotNil(result) - XCTAssertEqual("off", result.treatment) + XCTAssertEqual("off", result!.treatment, "If all prerequisites are satisfied, the flag should keep evaluating and return the default treatment") } func testNotPassingPrerequisites() { - var result: EvaluationResult! - let matchingKey = "anyKey" - let splitName = "FACUNDO_TEST" - (evaluator as! DefaultEvaluator).prerequisitesMatcher = PrerequisitesMatcherMock(shouldPass: false) + // This overrides the matcher-factory of the Evaluator, so we can inject the mock + (evaluator as! DefaultEvaluator).overridePrerequisitesMatcher( { _ in PrerequisitesMatcherMock(shouldPass: false) } ) - result = try? evaluator.evalTreatment(matchingKey: matchingKey, bucketingKey: nil, splitName: splitName, attributes: nil) + let result = try? evaluator.evalTreatment(matchingKey: "anyKey", bucketingKey: nil, splitName: "FACUNDO_TEST", attributes: nil) XCTAssertNotNil(result) - XCTAssertEqual(ImpressionsConstants.prerequisitesNotMet, result.label) + XCTAssertEqual(result?.treatment, "off", "If any prerequisite is not satisfied, the flag should return the default treatment") + XCTAssertEqual(ImpressionsConstants.prerequisitesNotMet, result!.label, "If any prerequisite is not satisfied, the label should be 'prerequisitesNotMet'") } func testNotInSplit() { diff --git a/SplitTests/Fake/PrerequisitesMatcherMock.swift b/SplitTests/Fake/PrerequisitesMatcherMock.swift index 26bdb5592..33eff16d0 100644 --- a/SplitTests/Fake/PrerequisitesMatcherMock.swift +++ b/SplitTests/Fake/PrerequisitesMatcherMock.swift @@ -3,7 +3,7 @@ @testable import Split -class PrerequisitesMatcherMock: BaseMatcher, MatcherProtocol, PrerequisitesMatcherProtocol { +class PrerequisitesMatcherMock: BaseMatcher, MatcherProtocol { private let returnValue: Bool From 9010471bddcc0f582fadbf4e8698d6007c555cb7 Mon Sep 17 00:00:00 2001 From: Martin Cardozo Date: Wed, 28 May 2025 14:07:27 -0300 Subject: [PATCH 27/46] Cleanup --- Split/Matchers/PrerequisitesMatcher.swift | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Split/Matchers/PrerequisitesMatcher.swift b/Split/Matchers/PrerequisitesMatcher.swift index 62ea77640..d93a0e90e 100644 --- a/Split/Matchers/PrerequisitesMatcher.swift +++ b/Split/Matchers/PrerequisitesMatcher.swift @@ -4,7 +4,7 @@ import Foundation class PrerequisitesMatcher: BaseMatcher, MatcherProtocol { - + private var prerequisites: [Prerequisite]? init(_ prerequisites: [Prerequisite]? = nil) { @@ -13,6 +13,7 @@ class PrerequisitesMatcher: BaseMatcher, MatcherProtocol { // This evaluation passes JUST if -all- prerequisite are met func evaluate(values: EvalValues, context: EvalContext?) -> Bool { + guard let prerequisites = prerequisites, !prerequisites.isEmpty else { return true } for prerequisite in prerequisites { @@ -31,6 +32,7 @@ class PrerequisitesMatcher: BaseMatcher, MatcherProtocol { return false } } + return true } } From 31c1fe3365fb081ac425eb1ef2670285b8e228e8 Mon Sep 17 00:00:00 2001 From: Martin Cardozo Date: Wed, 28 May 2025 14:09:47 -0300 Subject: [PATCH 28/46] Cleanup --- Split/Matchers/BaseMatcher.swift | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/Split/Matchers/BaseMatcher.swift b/Split/Matchers/BaseMatcher.swift index ae8788ddc..9049f522f 100644 --- a/Split/Matchers/BaseMatcher.swift +++ b/Split/Matchers/BaseMatcher.swift @@ -12,25 +12,26 @@ class BaseMatcher: NSObject { var attribute: String? var type: MatcherType? - init(negate: Bool? = nil, attribute: String? = nil, type: MatcherType? = nil) { + init(negate: Bool? = nil, + attribute: String? = nil, type: MatcherType? = nil) { self.negate = negate self.attribute = attribute self.type = type } func isNegate() -> Bool { - negate ?? false + return negate ?? false } func getAttribute() -> String? { - attribute + return attribute } func getMatcherType() -> MatcherType { - type! + return type! } func matcherHasAttribute() -> Bool { - attribute != nil + return attribute != nil } } From 708ce6ee1ab21741d84006788e3943c733daac9d Mon Sep 17 00:00:00 2001 From: Martin Cardozo Date: Wed, 28 May 2025 14:23:19 -0300 Subject: [PATCH 29/46] Solving branch problem --- Split/Engine/Evaluator.swift | 173 ++++++++++++++++++----------------- 1 file changed, 89 insertions(+), 84 deletions(-) diff --git a/Split/Engine/Evaluator.swift b/Split/Engine/Evaluator.swift index 9b66dc74f..dab5a12fa 100644 --- a/Split/Engine/Evaluator.swift +++ b/Split/Engine/Evaluator.swift @@ -3,73 +3,109 @@ // Split // // Created by Natalia Stele on 11/14/17. +// import Foundation +// swiftlint:disable function_body_length +struct EvaluationResult { + var treatment: String + var label: String + var changeNumber: Int64? + var configuration: String? + var impressionsDisabled: Bool + + init(treatment: String, label: String, changeNumber: Int64? = nil, configuration: String? = nil, + impressionsDisabled: Bool = false) { + self.treatment = treatment + self.label = label + self.changeNumber = changeNumber + self.configuration = configuration + self.impressionsDisabled = impressionsDisabled + } +} + +struct EvalValues { + let matchValue: Any? + let matchingKey: String + let bucketingKey: String? + let attributes: [String: Any]? + + init(matchValue: Any?, matchingKey: String, bucketingKey: String? = nil, attributes: [String: Any]? = nil) { + self.matchValue = matchValue + self.matchingKey = matchingKey + self.bucketingKey = bucketingKey + self.attributes = attributes + } +} + +// Components needed +struct EvalContext { + let evaluator: Evaluator? + let mySegmentsStorage: MySegmentsStorage? + let myLargeSegmentsStorage: MySegmentsStorage? +} protocol Evaluator { - func evalTreatment(matchingKey: String, bucketingKey: String?, splitName: String, attributes: [String: Any]?) throws -> EvaluationResult + func evalTreatment(matchingKey: String, bucketingKey: String?, + splitName: String, attributes: [String: Any]?) throws -> EvaluationResult } class DefaultEvaluator: Evaluator { - // Internal for testing purposes var splitter: SplitterProtocol = Splitter.shared - private let splitsStorage: SplitsStorage private let mySegmentsStorage: MySegmentsStorage private let myLargeSegmentsStorage: MySegmentsStorage? - private let ruleBasedSegmentsStorage: RuleBasedSegmentsStorage? - init(splitsStorage: SplitsStorage, mySegmentsStorage: MySegmentsStorage, myLargeSegmentsStorage: MySegmentsStorage? = nil, ruleBasedSegmentsStorage: RuleBasedSegmentsStorage? = nil) { + init(splitsStorage: SplitsStorage, + mySegmentsStorage: MySegmentsStorage, + myLargeSegmentsStorage: MySegmentsStorage?) { self.splitsStorage = splitsStorage self.mySegmentsStorage = mySegmentsStorage self.myLargeSegmentsStorage = myLargeSegmentsStorage - self.ruleBasedSegmentsStorage = ruleBasedSegmentsStorage } - func evalTreatment(matchingKey: String, bucketingKey: String?, splitName: String, attributes: [String: Any]?) throws -> EvaluationResult { + func evalTreatment(matchingKey: String, bucketingKey: String?, + splitName: String, attributes: [String: Any]?) throws -> EvaluationResult { - // 1. Guarantee Split exists & is active - guard let split = splitsStorage.get(name: splitName), split.status != .archived else { - Logger.w("The feature flag definition for '\(splitName)' not found") - return EvaluationResult(treatment: SplitConstants.control, label: ImpressionsConstants.splitNotFound) + guard let split = splitsStorage.get(name: splitName), + split.status != .archived else { + Logger.w("The feature flag definition for '\(splitName)' has not been found") + return EvaluationResult(treatment: SplitConstants.control, label: ImpressionsConstants.splitNotFound) } - - // 2. Extract neccesary info + let changeNumber = split.changeNumber ?? -1 - let defaultTreatment = split.defaultTreatment ?? SplitConstants.control - let bucketKey = selectBucketKey(matchingKey: matchingKey, bucketingKey: bucketingKey) - let values = EvalValues(matchValue: matchingKey, matchingKey: matchingKey, bucketingKey: bucketKey, attributes: attributes) - - // 3. Guarantee is not killed - guard let killed = split.killed, !killed else { - return EvaluationResult(treatment: defaultTreatment, label: ImpressionsConstants.killed, changeNumber: changeNumber, - configuration: split.configurations?[defaultTreatment], impressionsDisabled: split.isImpressionsDisabled()) - } - - // 4. Evaluate Prerequisites - if !PrerequisitesMatcher(split.prerequisites).evaluate(values: values, context: getContext()) { + let defaultTreatment = split.defaultTreatment ?? SplitConstants.control + if let killed = split.killed, killed { return EvaluationResult(treatment: defaultTreatment, - label: ImpressionsConstants.prerequisitesNotMet, + label: ImpressionsConstants.killed, changeNumber: changeNumber, configuration: split.configurations?[defaultTreatment], impressionsDisabled: split.isImpressionsDisabled()) } - - // 5. Evaluate core conditions - guard let conditions = split.conditions, let trafficAllocationSeed = split.trafficAllocationSeed, let seed = split.seed else { - return EvaluationResult(treatment: SplitConstants.control, label: ImpressionsConstants.exception) - } + + var inRollOut: Bool = false var splitAlgo: Algorithm = Algorithm.legacy - if let rawAlgo = split.algo, let algo = Algorithm.init(rawValue: rawAlgo) { splitAlgo = algo } + + if let rawAlgo = split.algo, let algo = Algorithm.init(rawValue: rawAlgo) { + splitAlgo = algo + } + + let bucketKey = selectBucketKey(matchingKey: matchingKey, bucketingKey: bucketingKey) + + guard let conditions: [Condition] = split.conditions, + let trafficAllocationSeed = split.trafficAllocationSeed, + let seed = split.seed else { + return EvaluationResult(treatment: SplitConstants.control, label: ImpressionsConstants.exception) + } + do { - var inRollOut: Bool = false for condition in conditions { - - // Traffic Allocation if !inRollOut && condition.conditionType == ConditionType.rollout { if let trafficAllocation = split.trafficAllocation, trafficAllocation < 100 { - let bucket: Int64 = splitter.getBucket(seed: trafficAllocationSeed, key: bucketKey, algo: splitAlgo) + let bucket: Int64 = splitter.getBucket(seed: trafficAllocationSeed, + key: bucketKey, + algo: splitAlgo) if bucket > trafficAllocation { return EvaluationResult(treatment: defaultTreatment, label: ImpressionsConstants.notInSplit, @@ -81,79 +117,48 @@ class DefaultEvaluator: Evaluator { } } - // Core conditions (returns the first one that match) + // Returns the first condition that match. + let values = EvalValues(matchValue: matchingKey, matchingKey: matchingKey, + bucketingKey: bucketKey, attributes: attributes) if try condition.match(values: values, context: getContext()) { let key: Key = Key(matchingKey: matchingKey, bucketingKey: bucketKey) - let treatment = splitter.getTreatment(key: key, seed: seed, attributes: attributes, partions: condition.partitions, algo: splitAlgo) - + let treatment = splitter.getTreatment(key: key, seed: seed, attributes: attributes, + partions: condition.partitions, algo: splitAlgo) return EvaluationResult(treatment: treatment, label: condition.label!, changeNumber: changeNumber, configuration: split.configurations?[treatment], impressionsDisabled: split.isImpressionsDisabled()) } } - return EvaluationResult(treatment: defaultTreatment, + let result = EvaluationResult(treatment: defaultTreatment, label: ImpressionsConstants.noConditionMatched, changeNumber: changeNumber, configuration: split.configurations?[defaultTreatment], impressionsDisabled: split.isImpressionsDisabled()) + return result } catch EvaluatorError.matcherNotFound { - Logger.e("Matcher not found") - return EvaluationResult(treatment: SplitConstants.control, label: ImpressionsConstants.matcherNotFound, changeNumber: changeNumber, impressionsDisabled: split.isImpressionsDisabled()) + Logger.e("The matcher has not been found") + return EvaluationResult(treatment: SplitConstants.control, label: ImpressionsConstants.matcherNotFound, + changeNumber: changeNumber, impressionsDisabled: split.isImpressionsDisabled()) } } private func getContext() -> EvalContext { - EvalContext(evaluator: self, mySegmentsStorage: mySegmentsStorage,myLargeSegmentsStorage: myLargeSegmentsStorage, ruleBasedSegmentsStorage: ruleBasedSegmentsStorage) + return EvalContext(evaluator: self, + mySegmentsStorage: mySegmentsStorage, + myLargeSegmentsStorage: myLargeSegmentsStorage) } private func selectBucketKey(matchingKey: String, bucketingKey: String?) -> String { - if let bucketingKey = bucketingKey, !bucketingKey.isEmpty { return bucketingKey } - + if let key = bucketingKey, !key.isEmpty() { + return key + } return matchingKey } } private extension Split { func isImpressionsDisabled() -> Bool { - impressionsDisabled ?? false - } -} - -// MARK: Components needed -struct EvalValues { - let matchValue: Any? - let matchingKey: String - let bucketingKey: String? - let attributes: [String: Any]? - - init(matchValue: Any?, matchingKey: String, bucketingKey: String? = nil, attributes: [String: Any]? = nil) { - self.matchValue = matchValue - self.matchingKey = matchingKey - self.bucketingKey = bucketingKey - self.attributes = attributes - } -} - -struct EvalContext { - let evaluator: Evaluator? - let mySegmentsStorage: MySegmentsStorage? - let myLargeSegmentsStorage: MySegmentsStorage? - let ruleBasedSegmentsStorage: RuleBasedSegmentsStorage? -} - -struct EvaluationResult { - var treatment: String - var label: String - var changeNumber: Int64? - var configuration: String? - var impressionsDisabled: Bool - - init(treatment: String, label: String, changeNumber: Int64? = nil, configuration: String? = nil, impressionsDisabled: Bool = false) { - self.treatment = treatment - self.label = label - self.changeNumber = changeNumber - self.configuration = configuration - self.impressionsDisabled = impressionsDisabled + return self.impressionsDisabled ?? false } } From 9defa10740d7751c526aef422749b52185708157 Mon Sep 17 00:00:00 2001 From: Martin Cardozo Date: Wed, 28 May 2025 14:28:55 -0300 Subject: [PATCH 30/46] Solving problem of branches --- Split/Engine/Constants/SplitConstants.swift | 2 +- Split/Engine/Evaluator.swift | 9 +++++++-- Split/Matchers/BaseMatcher.swift | 9 +++++---- SplitTests/Fake/EvaluatorStub.swift | 3 ++- SplitTests/Murmur3HashingTest.swift | 2 +- 5 files changed, 16 insertions(+), 9 deletions(-) diff --git a/Split/Engine/Constants/SplitConstants.swift b/Split/Engine/Constants/SplitConstants.swift index 910ef121d..3e274550d 100644 --- a/Split/Engine/Constants/SplitConstants.swift +++ b/Split/Engine/Constants/SplitConstants.swift @@ -8,5 +8,5 @@ import Foundation struct SplitConstants { - static let control = "control" + static let control: String = "control" } diff --git a/Split/Engine/Evaluator.swift b/Split/Engine/Evaluator.swift index dab5a12fa..be5e8393c 100644 --- a/Split/Engine/Evaluator.swift +++ b/Split/Engine/Evaluator.swift @@ -43,6 +43,7 @@ struct EvalContext { let evaluator: Evaluator? let mySegmentsStorage: MySegmentsStorage? let myLargeSegmentsStorage: MySegmentsStorage? + let ruleBasedSegmentsStorage: RuleBasedSegmentsStorage? } protocol Evaluator { @@ -56,13 +57,16 @@ class DefaultEvaluator: Evaluator { private let splitsStorage: SplitsStorage private let mySegmentsStorage: MySegmentsStorage private let myLargeSegmentsStorage: MySegmentsStorage? + private let ruleBasedSegmentsStorage: RuleBasedSegmentsStorage? init(splitsStorage: SplitsStorage, mySegmentsStorage: MySegmentsStorage, - myLargeSegmentsStorage: MySegmentsStorage?) { + myLargeSegmentsStorage: MySegmentsStorage? = nil, + ruleBasedSegmentsStorage: RuleBasedSegmentsStorage? = nil) { self.splitsStorage = splitsStorage self.mySegmentsStorage = mySegmentsStorage self.myLargeSegmentsStorage = myLargeSegmentsStorage + self.ruleBasedSegmentsStorage = ruleBasedSegmentsStorage } func evalTreatment(matchingKey: String, bucketingKey: String?, @@ -146,7 +150,8 @@ class DefaultEvaluator: Evaluator { private func getContext() -> EvalContext { return EvalContext(evaluator: self, mySegmentsStorage: mySegmentsStorage, - myLargeSegmentsStorage: myLargeSegmentsStorage) + myLargeSegmentsStorage: myLargeSegmentsStorage, + ruleBasedSegmentsStorage: ruleBasedSegmentsStorage) } private func selectBucketKey(matchingKey: String, bucketingKey: String?) -> String { diff --git a/Split/Matchers/BaseMatcher.swift b/Split/Matchers/BaseMatcher.swift index 9049f522f..b2660fa6f 100644 --- a/Split/Matchers/BaseMatcher.swift +++ b/Split/Matchers/BaseMatcher.swift @@ -3,6 +3,7 @@ // Split // // Created by Natalia Stele on 11/5/17. +// import Foundation @@ -20,18 +21,18 @@ class BaseMatcher: NSObject { } func isNegate() -> Bool { - return negate ?? false + return self.negate ?? false } func getAttribute() -> String? { - return attribute + return self.attribute } func getMatcherType() -> MatcherType { - return type! + return self.type! } func matcherHasAttribute() -> Bool { - return attribute != nil + return self.attribute != nil } } diff --git a/SplitTests/Fake/EvaluatorStub.swift b/SplitTests/Fake/EvaluatorStub.swift index 52ca72cb2..f2ca5431b 100644 --- a/SplitTests/Fake/EvaluatorStub.swift +++ b/SplitTests/Fake/EvaluatorStub.swift @@ -12,7 +12,8 @@ import Foundation class EvaluatorStub: Evaluator { var lastAttributes = [Any]() - func evalTreatment(matchingKey: String, bucketingKey: String?, splitName: String, attributes: [String: Any]?) throws -> EvaluationResult { + func evalTreatment(matchingKey: String, bucketingKey: String?, + splitName: String, attributes: [String: Any]?) throws -> EvaluationResult { lastAttributes.append(attributes ?? "nil") return EvaluationResult(treatment: "on", label: "some") } diff --git a/SplitTests/Murmur3HashingTest.swift b/SplitTests/Murmur3HashingTest.swift index b2ad52c6b..314fcb1fe 100644 --- a/SplitTests/Murmur3HashingTest.swift +++ b/SplitTests/Murmur3HashingTest.swift @@ -17,7 +17,7 @@ class Murmur3HashingTest: XCTestCase { "murmur3-sample-v4", "murmur3-sample-v3", "murmur3-sample-double-treatment-users"] - for file in files { + for file in files { var data = CsvHelper.readDataFromCSV(sourceClass: self, fileName: file) data = CsvHelper.cleanRows(file: data!) let csvRows = CsvHelper.csv(data: data!) From 3e4e7b8e1b537f8f5cd4cd3606b07348829f61e1 Mon Sep 17 00:00:00 2001 From: Martin Cardozo Date: Wed, 28 May 2025 14:29:33 -0300 Subject: [PATCH 31/46] Same --- Split/Engine/Constants/SplitConstants.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Split/Engine/Constants/SplitConstants.swift b/Split/Engine/Constants/SplitConstants.swift index 3e274550d..e92b25e0b 100644 --- a/Split/Engine/Constants/SplitConstants.swift +++ b/Split/Engine/Constants/SplitConstants.swift @@ -7,6 +7,6 @@ import Foundation -struct SplitConstants { +struct SplitConstants { static let control: String = "control" } From 7cd120340fd7922366251fdfc546b6bbc5726442 Mon Sep 17 00:00:00 2001 From: Martin Cardozo Date: Wed, 28 May 2025 14:30:18 -0300 Subject: [PATCH 32/46] Same --- Split/Matchers/BaseMatcher.swift | 1 + 1 file changed, 1 insertion(+) diff --git a/Split/Matchers/BaseMatcher.swift b/Split/Matchers/BaseMatcher.swift index b2660fa6f..cc52e7b0b 100644 --- a/Split/Matchers/BaseMatcher.swift +++ b/Split/Matchers/BaseMatcher.swift @@ -15,6 +15,7 @@ class BaseMatcher: NSObject { init(negate: Bool? = nil, attribute: String? = nil, type: MatcherType? = nil) { + self.negate = negate self.attribute = attribute self.type = type From 393e41c59fdec9dc594c19ffd25bed258bda3a1a Mon Sep 17 00:00:00 2001 From: Martin Cardozo Date: Wed, 28 May 2025 14:31:40 -0300 Subject: [PATCH 33/46] Same --- Split/Matchers/BaseMatcher.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Split/Matchers/BaseMatcher.swift b/Split/Matchers/BaseMatcher.swift index cc52e7b0b..c41a2b634 100644 --- a/Split/Matchers/BaseMatcher.swift +++ b/Split/Matchers/BaseMatcher.swift @@ -15,7 +15,7 @@ class BaseMatcher: NSObject { init(negate: Bool? = nil, attribute: String? = nil, type: MatcherType? = nil) { - + self.negate = negate self.attribute = attribute self.type = type From d4d3c5d890eaff3e31c18f1d10e52fe3aa8ed351 Mon Sep 17 00:00:00 2001 From: Martin Cardozo Date: Wed, 28 May 2025 14:34:22 -0300 Subject: [PATCH 34/46] Cleanup --- Split/Engine/Evaluator.swift | 4 ++-- Split/Impressions/ImpressionsConstants.swift | 18 +++++++++--------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/Split/Engine/Evaluator.swift b/Split/Engine/Evaluator.swift index 897325dc3..66dc48943 100644 --- a/Split/Engine/Evaluator.swift +++ b/Split/Engine/Evaluator.swift @@ -20,7 +20,7 @@ class DefaultEvaluator: Evaluator { private let mySegmentsStorage: MySegmentsStorage private let myLargeSegmentsStorage: MySegmentsStorage? private let ruleBasedSegmentsStorage: RuleBasedSegmentsStorage? - + init(splitsStorage: SplitsStorage, mySegmentsStorage: MySegmentsStorage, myLargeSegmentsStorage: MySegmentsStorage? = nil, ruleBasedSegmentsStorage: RuleBasedSegmentsStorage? = nil) { self.splitsStorage = splitsStorage self.mySegmentsStorage = mySegmentsStorage @@ -29,7 +29,7 @@ class DefaultEvaluator: Evaluator { } func evalTreatment(matchingKey: String, bucketingKey: String?, splitName: String, attributes: [String: Any]?) throws -> EvaluationResult { - + // 1. Guarantee Split exists & is active guard let split = splitsStorage.get(name: splitName), split.status != .archived else { Logger.w("The feature flag definition for '\(splitName)' has not been found") diff --git a/Split/Impressions/ImpressionsConstants.swift b/Split/Impressions/ImpressionsConstants.swift index e00d1ab05..d622c7995 100644 --- a/Split/Impressions/ImpressionsConstants.swift +++ b/Split/Impressions/ImpressionsConstants.swift @@ -8,13 +8,13 @@ import Foundation struct ImpressionsConstants { - static let noConditionMatched = "default rule" - static let killed = "killed" - static let splitNotFound = "definition not found" - static let notInSplit = "not in split" - static let matcherNotFound = "matcher not found" - static let exception = "exception" - static let notReady = "not ready" - static let unsupportedMatcherType = "targeting rule type unsupported by sdk" - static let prerequisitesNotMet = "prerequisites not met" + static let noConditionMatched: String = "default rule" + static let killed: String = "killed" + static let splitNotFound: String = "definition not found" + static let notInSplit: String = "not in split" + static let matcherNotFound: String = "matcher not found" + static let exception: String = "exception" + static let notReady: String = "not ready" + static let unsupportedMatcherType: String = "targeting rule type unsupported by sdk" + static let prerequisitesNotMet: String = "prerequisites not met" } From 470216d3e0b1190ecdabfbc9c88faa6652e672a7 Mon Sep 17 00:00:00 2001 From: Martin Cardozo Date: Wed, 28 May 2025 14:34:59 -0300 Subject: [PATCH 35/46] Cleanup --- Split/Impressions/ImpressionsConstants.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Split/Impressions/ImpressionsConstants.swift b/Split/Impressions/ImpressionsConstants.swift index d622c7995..1400466f5 100644 --- a/Split/Impressions/ImpressionsConstants.swift +++ b/Split/Impressions/ImpressionsConstants.swift @@ -16,5 +16,5 @@ struct ImpressionsConstants { static let exception: String = "exception" static let notReady: String = "not ready" static let unsupportedMatcherType: String = "targeting rule type unsupported by sdk" - static let prerequisitesNotMet: String = "prerequisites not met" + static let prerequisitesNotMet = "prerequisites not met" } From 85e8919ccc80224e15b618c6349ece3df765e211 Mon Sep 17 00:00:00 2001 From: Martin Cardozo Date: Wed, 28 May 2025 17:43:28 -0300 Subject: [PATCH 36/46] Deleting Comment Co-authored-by: gthea --- Split/Matchers/PrerequisitesMatcher.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Split/Matchers/PrerequisitesMatcher.swift b/Split/Matchers/PrerequisitesMatcher.swift index d93a0e90e..073285302 100644 --- a/Split/Matchers/PrerequisitesMatcher.swift +++ b/Split/Matchers/PrerequisitesMatcher.swift @@ -24,7 +24,7 @@ class PrerequisitesMatcher: BaseMatcher, MatcherProtocol { continue } - if !prerequisite.treatments.contains(treatment) { // ts = Prerequisite treatments list + if !prerequisite.treatments.contains(treatment) { return false } } catch { From 58b4da77e5045bfcc727e574f21ce3f0f077f052 Mon Sep 17 00:00:00 2001 From: Martin Cardozo Date: Fri, 30 May 2025 12:26:37 -0300 Subject: [PATCH 37/46] Sharing branch --- Split.xcodeproj/project.pbxproj | 10 +- Split/Matchers/PrerequisitesMatcher.swift | 2 +- SplitTests/EvaluatorTests.swift | 45 +- .../Api/SplitIntegrationTest.swift | 36 + .../Resources/split_prerequisites_test.json | 166 + SplitTests/Resources/splitchanges_1.json | 154 +- SplitTests/Resources/splits.json | 2834 +++++++++-------- 7 files changed, 1883 insertions(+), 1364 deletions(-) create mode 100644 SplitTests/Resources/split_prerequisites_test.json diff --git a/Split.xcodeproj/project.pbxproj b/Split.xcodeproj/project.pbxproj index 361c8935a..94d940988 100644 --- a/Split.xcodeproj/project.pbxproj +++ b/Split.xcodeproj/project.pbxproj @@ -353,11 +353,11 @@ 59FB7C35220329B900ECC96A /* SplitFactoryBuilderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 59FB7C34220329B900ECC96A /* SplitFactoryBuilderTests.swift */; }; 59FB7C3C2203795F00ECC96A /* LocalhostSplitsParser.swift in Sources */ = {isa = PBXBuildFile; fileRef = 59FB7C3B2203795F00ECC96A /* LocalhostSplitsParser.swift */; }; 59FB7C3E22037B9400ECC96A /* SpaceDelimitedLocalhostSplitsParser.swift in Sources */ = {isa = PBXBuildFile; fileRef = 59FB7C3D22037B9400ECC96A /* SpaceDelimitedLocalhostSplitsParser.swift */; }; + 5B64AB8E2DE8BEE000B29864 /* split_prerequisites_test.json in Resources */ = {isa = PBXBuildFile; fileRef = 5B64AB8D2DE8BED700B29864 /* split_prerequisites_test.json */; }; 5B91B8392DDE4A3B000510F0 /* SplitDTOTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B91B8382DDE4A30000510F0 /* SplitDTOTests.swift */; }; - 5BF52E032DE62F0500FEDAFE /* PrerequisitesMatcherMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BF52E022DE62EFE00FEDAFE /* PrerequisitesMatcherMock.swift */; }; - 5BF52DF62DE0B60700FEDAFE /* PrerequisitesMatcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BF52DF52DE0B60300FEDAFE /* PrerequisitesMatcher.swift */; }; 5BF52DF72DE0B60700FEDAFE /* PrerequisitesMatcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BF52DF52DE0B60300FEDAFE /* PrerequisitesMatcher.swift */; }; 5BF52DF92DE4B8D400FEDAFE /* PrerequisitesMatcherTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BF52DF82DE4B8CA00FEDAFE /* PrerequisitesMatcherTest.swift */; }; + 5BF52E032DE62F0500FEDAFE /* PrerequisitesMatcherMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BF52E022DE62EFE00FEDAFE /* PrerequisitesMatcherMock.swift */; }; 9500D9922C56F9BA00383593 /* HostDomainFilterTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9500D9912C56F9BA00383593 /* HostDomainFilterTests.swift */; }; 9500D9A92C59297400383593 /* HostDomainFilter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9500D9A82C59297400383593 /* HostDomainFilter.swift */; }; 9500D9AA2C59382000383593 /* HostDomainFilter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9500D9A82C59297400383593 /* HostDomainFilter.swift */; }; @@ -1557,10 +1557,11 @@ 59FB7C34220329B900ECC96A /* SplitFactoryBuilderTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SplitFactoryBuilderTests.swift; sourceTree = ""; }; 59FB7C3B2203795F00ECC96A /* LocalhostSplitsParser.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocalhostSplitsParser.swift; sourceTree = ""; }; 59FB7C3D22037B9400ECC96A /* SpaceDelimitedLocalhostSplitsParser.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SpaceDelimitedLocalhostSplitsParser.swift; sourceTree = ""; }; + 5B64AB8D2DE8BED700B29864 /* split_prerequisites_test.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = split_prerequisites_test.json; sourceTree = ""; }; 5B91B8382DDE4A30000510F0 /* SplitDTOTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SplitDTOTests.swift; sourceTree = ""; }; - 5BF52E022DE62EFE00FEDAFE /* PrerequisitesMatcherMock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PrerequisitesMatcherMock.swift; sourceTree = ""; }; 5BF52DF52DE0B60300FEDAFE /* PrerequisitesMatcher.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PrerequisitesMatcher.swift; sourceTree = ""; }; 5BF52DF82DE4B8CA00FEDAFE /* PrerequisitesMatcherTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PrerequisitesMatcherTest.swift; sourceTree = ""; }; + 5BF52E022DE62EFE00FEDAFE /* PrerequisitesMatcherMock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PrerequisitesMatcherMock.swift; sourceTree = ""; }; 9500D9912C56F9BA00383593 /* HostDomainFilterTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HostDomainFilterTests.swift; sourceTree = ""; }; 9500D9A82C59297400383593 /* HostDomainFilter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HostDomainFilter.swift; sourceTree = ""; }; 9500D9AC2C5A918300383593 /* split_cache_v5.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = split_cache_v5.xcdatamodel; sourceTree = ""; }; @@ -2939,6 +2940,7 @@ 592C6ADB211CBB86002D120C /* Resources */ = { isa = PBXGroup; children = ( + 5B64AB8D2DE8BED700B29864 /* split_prerequisites_test.json */, C5A7D5562DDBD4280081D190 /* split_changes_rbs.json */, 95F7BBD32C1A273900C5F2E4 /* Cert */, C5977C052BF273A3003E293A /* between_semver.csv */, @@ -3975,6 +3977,7 @@ 95F7BBDB2C1A27C200C5F2E4 /* rsa_4096_public_01.pem in Resources */, 5982D92F219C83D000230F44 /* legacy_1_short.csv in Resources */, 95DF58FA2BEE5140009220B8 /* SplitiOSUnit_4.xctestplan in Resources */, + 5B64AB8E2DE8BEE000B29864 /* split_prerequisites_test.json in Resources */, 599EDAF32270A15B00D7DACB /* localhost.splits in Resources */, 590DF9D1213F07400082B94F /* split_sample_feature6.json in Resources */, 95F7BBEF2C1B413500C5F2E4 /* ec_apple_public_key.der in Resources */, @@ -4836,7 +4839,6 @@ 95B02CC628D0BDC10030EC8B /* CompressionUtil.swift in Sources */, 95B02CC728D0BDC10030EC8B /* Stopwatch.swift in Sources */, 95B02CC828D0BDC10030EC8B /* ConcurrentArrayQueue.swift in Sources */, - 5BF52DFC2DE5095800FEDAFE /* PrerequisitesMatcher.swift in Sources */, 95B02CC928D0BDC10030EC8B /* SynchronizedList.swift in Sources */, 95B02CCA28D0BDC10030EC8B /* ConcurrentSet.swift in Sources */, C539CADC2D93229D0050C732 /* EvaluationOptions.swift in Sources */, diff --git a/Split/Matchers/PrerequisitesMatcher.swift b/Split/Matchers/PrerequisitesMatcher.swift index d93a0e90e..073285302 100644 --- a/Split/Matchers/PrerequisitesMatcher.swift +++ b/Split/Matchers/PrerequisitesMatcher.swift @@ -24,7 +24,7 @@ class PrerequisitesMatcher: BaseMatcher, MatcherProtocol { continue } - if !prerequisite.treatments.contains(treatment) { // ts = Prerequisite treatments list + if !prerequisite.treatments.contains(treatment) { return false } } catch { diff --git a/SplitTests/EvaluatorTests.swift b/SplitTests/EvaluatorTests.swift index 1e6f96db7..0c48cad14 100644 --- a/SplitTests/EvaluatorTests.swift +++ b/SplitTests/EvaluatorTests.swift @@ -158,8 +158,7 @@ class EvaluatorTests: XCTestCase { var result: EvaluationResult! var evaluator: Evaluator! guard let split = loadSplit(splitName: "split_sample_feature6") else { - XCTAssertTrue(false) - return + XCTFail("Test flag not found"); return } split.algo = nil evaluator = customEvaluator(split: split) @@ -174,8 +173,7 @@ class EvaluatorTests: XCTestCase { var result: EvaluationResult! var evaluator: Evaluator! guard let split = loadSplit(splitName: "split_sample_feature6") else { - XCTAssertTrue(false) - return + XCTFail("Test flag not found"); return } split.algo = 2 evaluator = customEvaluator(split: split) @@ -190,8 +188,7 @@ class EvaluatorTests: XCTestCase { var result: EvaluationResult! var evaluator: Evaluator! guard let split = loadSplit(splitName: "split_sample_feature6") else { - XCTAssertTrue(false) - return + XCTFail("Test flag not found"); return } split.algo = 2 evaluator = customEvaluator(split: split) @@ -213,8 +210,7 @@ class EvaluatorTests: XCTestCase { var evaluator: Evaluator! guard let split = loadSplit(splitName: "split_traffic_alloc_50_default_rule_50") else { - XCTAssertTrue(false) - return + XCTFail("Test flag not found"); return } let splitName = split.name! split.algo = 2 @@ -258,8 +254,7 @@ class EvaluatorTests: XCTestCase { var result: EvaluationResult! var evaluator: Evaluator! guard let split = loadSplit(splitName: "split_sample_feature6") else { - XCTAssertTrue(false) - return + XCTFail("Test flag not found"); return } let attributes = ["atributo2": ["salamin"]] split.algo = 2 @@ -275,8 +270,7 @@ class EvaluatorTests: XCTestCase { var result: EvaluationResult! var evaluator: Evaluator! guard let split = loadSplit(splitName: "split_sample_feature6") else { - XCTAssertTrue(false) - return + XCTFail("Test flag not found"); return } let attributes = ["atributo1": "mila"] split.algo = 2 @@ -292,8 +286,7 @@ class EvaluatorTests: XCTestCase { var result: EvaluationResult! var evaluator: Evaluator! guard let split = loadSplit(splitName: "split_sample_feature6") else { - XCTAssertTrue(false) - return + XCTFail("Test flag not found"); return } let attributes = ["atribute": ["papapa"]] split.algo = 2 @@ -310,8 +303,7 @@ class EvaluatorTests: XCTestCase { var result: EvaluationResult! var evaluator: Evaluator! guard let split = loadSplit(splitName: "split_sample_feature6") else { - XCTAssertTrue(false) - return + XCTFail("Test flag not found"); return } split.trafficAllocation = 0 split.algo = 2 @@ -339,8 +331,7 @@ class EvaluatorTests: XCTestCase { var result: EvaluationResult! var evaluator: Evaluator! guard let split = loadSplit(splitName: "split_sample_feature6") else { - XCTAssertTrue(false) - return + XCTFail("Test flag not found"); return } split.trafficAllocation = 100 split.algo = 2 @@ -378,6 +369,21 @@ class EvaluatorTests: XCTestCase { treatment = result!.treatment XCTAssertEqual(treatment, "on", "Result should be 'on'") } + + func testNewFlag() { + var result: EvaluationResult! + var evaluator: Evaluator! + guard let split = loadSplit(splitName: "split_prerequisites_test") else { + XCTFail("Test flag not found"); return + } + +// evaluator = customEvaluator(split: split) +// result = try? evaluator.evalTreatment(matchingKey: matchingKey, bucketingKey: nil, splitName: split.name!, attributes: nil) +// XCTAssertNotNil(result) +// XCTAssertEqual("t4_6", result?.treatment) +// XCTAssertNotNil(result?.configuration) +// XCTAssertEqual("default rule", result?.label) + } func testInLargeSegmentWhitelist() { inLargeSegmentWhiteListTest(key: matchingKey) @@ -403,8 +409,7 @@ class EvaluatorTests: XCTestCase { var result: EvaluationResult! var evaluator: Evaluator! guard let split = loadSplit(splitName: "split_sample_feature6") else { - XCTAssertTrue(false) - return + XCTFail("Test flag not found"); return } split.algo = 2 if (disabled != nil) { diff --git a/SplitTests/Integration/Api/SplitIntegrationTest.swift b/SplitTests/Integration/Api/SplitIntegrationTest.swift index 47d6f061f..70b54cfd8 100644 --- a/SplitTests/Integration/Api/SplitIntegrationTest.swift +++ b/SplitTests/Integration/Api/SplitIntegrationTest.swift @@ -193,6 +193,42 @@ class SplitIntegrationTests: XCTestCase { semaphore.wait() factory = nil } + + func testPrerequisitesTreatment() throws { + + let splitConfig: SplitClientConfig = SplitClientConfig() + splitConfig.featuresRefreshRate = 30 + splitConfig.segmentsRefreshRate = 30 + splitConfig.impressionRefreshRate = 30 + splitConfig.sdkReadyTimeOut = 60000 + splitConfig.trafficType = trafficType + splitConfig.eventsPerPush = 10 + splitConfig.eventsQueueSize = 100 + splitConfig.eventsPushRate = 999999 + splitConfig.eventsFirstPushWindow = 999 + splitConfig.logLevel = TestingHelper.testLogLevel + splitConfig.impressionsMode = "DEBUG" + splitConfig.serviceEndpoints = ServiceEndpoints.builder() + .set(sdkEndpoint: serverUrl).set(eventsEndpoint: serverUrl).build() + + let key: Key = Key(matchingKey: "bilal@split.io", bucketingKey: nil) + let factory = DefaultSplitFactoryBuilder().setHttpClient(httpClient).setApiKey(apiKey).setKey(key).setConfig(splitConfig).build() + let client = factory?.client + let manager = factory?.manager + + // SDK Ready + let sdkReadyExpectation = XCTestExpectation(description: "SDK READY Expectation") + client?.on(event: SplitEvent.sdkReady) { sdkReadyExpectation.fulfill() } + wait(for: [sdkReadyExpectation], timeout: 5) + + // Ensures healthy JSON data, tests SplitView and default treatment + XCTAssertEqual(client?.getTreatment("always_on_if_prerequisite"), "off") + XCTAssertEqual(manager?.split(featureName: "always_on_if_prerequisite")!.prerequisites![0].flagName, "rbs_test_flag") + XCTAssertEqual(manager?.split(featureName: "always_on_if_prerequisite")!.prerequisites![0].treatments[0], "v1") + + // Tests Prerequisites Filtering + XCTAssertEqual(client?.getTreatment("always_on_if_prerequisite"), "off") + } func testImpressionsCount() throws { diff --git a/SplitTests/Resources/split_prerequisites_test.json b/SplitTests/Resources/split_prerequisites_test.json new file mode 100644 index 000000000..5c2464289 --- /dev/null +++ b/SplitTests/Resources/split_prerequisites_test.json @@ -0,0 +1,166 @@ +{ + "ff": { + "s": -1, + "t": 100, + "d": [ + { + "name": "always_on_if_prerequisite", + "trafficTypeName": "user", + "trafficAllocation": 100, + "trafficAllocationSeed": 1828377380, + "changeNumber": 5, + "seed": -790401604, + "status": "ACTIVE", + "killed": false, + "defaultTreatment": "off", + "prerequisites": [ + { + "n": "rbs_test_flag", + "ts": [ + "v1" + ] + } + ], + "conditions": [ + { + "conditionType": "ROLLOUT", + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "user", + "attribute": null + }, + "matcherType": "ALL_KEYS", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": null, + "unaryNumericMatcherData": null, + "betweenMatcherData": null + } + ] + }, + "partitions": [ + { + "treatment": "on", + "size": 100 + } + ], + "label": "always_on_if_prerequisite label" + } + ] + }, + { + "name": "rbs_test_flag", + "trafficTypeName": "user", + "trafficAllocation": 100, + "trafficAllocationSeed": 1828377380, + "seed": -286617921, + "status": "ACTIVE", + "killed": false, + "defaultTreatment": "off", + "algo": 2, + "conditions": [ + { + "conditionType": "ROLLOUT", + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "user" + }, + "matcherType": "IN_RULE_BASED_SEGMENT", + "negate": false, + "userDefinedSegmentMatcherData": { + "segmentName": "test_rule_based_segment" + } + } + ] + }, + "partitions": [ + { + "treatment": "v1", + "size": 100 + }, + { + "treatment": "v2", + "size": 0 + } + ], + "label": "in rule based segment test_rule_based_segment" + }, + { + "conditionType": "ROLLOUT", + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "user" + }, + "matcherType": "ALL_KEYS", + "negate": false + } + ] + }, + "partitions": [ + { + "treatment": "v1", + "size": 0 + }, + { + "treatment": "v2", + "size": 100 + } + ], + "label": "default rule" + } + ], + "configurations": {}, + "sets": [], + "impressionsDisabled": false + } + ] + }, + "rbs": { + "s": -1, + "t": 100, + "d": [ + { + "name": "test_rule_based_segment", + "status": "ACTIVE", + "trafficTypeName": "user", + "excluded": { + "keys": [ + "mauro@split.io", + "gaston@split.io" + ], + "segments": [] + }, + "conditions": [ + { + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "user" + }, + "matcherType": "ENDS_WITH", + "negate": false, + "whitelistMatcherData": { + "whitelist": [ + "@split.io" + ] + } + } + ] + } + } + ] + } + ] + } +} diff --git a/SplitTests/Resources/splitchanges_1.json b/SplitTests/Resources/splitchanges_1.json index 420ef8e70..fe1dbafe1 100644 --- a/SplitTests/Resources/splitchanges_1.json +++ b/SplitTests/Resources/splitchanges_1.json @@ -2880,8 +2880,160 @@ "label":"in segment all" } ] + }, + { + "name": "always_on_if_prerequisite", + "trafficTypeName": "user", + "trafficAllocation": 100, + "trafficAllocationSeed": 1828377380, + "changeNumber": 5, + "seed": -790401604, + "status": "ACTIVE", + "killed": false, + "defaultTreatment": "off", + "prerequisites": [ + { + "n": "rbs_test_flag", + "ts": [ + "v1" + ] + } + ], + "conditions": [ + { + "conditionType": "ROLLOUT", + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "user", + "attribute": null + }, + "matcherType": "ALL_KEYS", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": null, + "unaryNumericMatcherData": null, + "betweenMatcherData": null + } + ] + }, + "partitions": [ + { + "treatment": "on", + "size": 100 + } + ], + "label": "always_on_if_prerequisite label" + } + ] + }, + { + "name": "rbs_test_flag", + "trafficTypeName": "user", + "trafficAllocation": 100, + "trafficAllocationSeed": 1828377380, + "seed": -286617921, + "status": "ACTIVE", + "killed": false, + "defaultTreatment": "off", + "algo": 2, + "conditions": [ + { + "conditionType": "ROLLOUT", + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "user" + }, + "matcherType": "IN_RULE_BASED_SEGMENT", + "negate": false, + "userDefinedSegmentMatcherData": { + "segmentName": "test_rule_based_segment" + } + } + ] + }, + "partitions": [ + { + "treatment": "v1", + "size": 100 + }, + { + "treatment": "v2", + "size": 0 + } + ], + "label": "in rule based segment test_rule_based_segment" + }, + { + "conditionType": "ROLLOUT", + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "user" + }, + "matcherType": "ALL_KEYS", + "negate": false + } + ] + }, + "partitions": [ + { + "treatment": "v1", + "size": 0 + }, + { + "treatment": "v2", + "size": 100 + } + ], + "label": "default rule" + } + ], + "configurations": {}, + "sets": [], + "impressionsDisabled": false } ], "s": 1506703262916, "t":1506703262916 -}, "rbs": {"s":-1, "t":-1, "d": []}} +}, "rbs": {"s":-1, "t":-1, "d": [ + { + "name": "test_rule_based_segment", + "status": "ACTIVE", + "trafficTypeName": "user", + "excluded": { + "keys": [ + "mauro@split.io", + "gaston@split.io" + ], + "segments": [] + }, + "conditions": [ + { + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "user" + }, + "matcherType": "ENDS_WITH", + "negate": false, + "whitelistMatcherData": { + "whitelist": [ + "@split.io" + ] + } + } + ] + } + }] + }] +}} diff --git a/SplitTests/Resources/splits.json b/SplitTests/Resources/splits.json index 67500d653..76f5e8037 100644 --- a/SplitTests/Resources/splits.json +++ b/SplitTests/Resources/splits.json @@ -1,1343 +1,1501 @@ - [{ - "trafficTypeName": "custom", - "name": "SAMPLE_FEATURE0", - "prerequisites": [ - { "n": "flag1", "ts": ["on","v1"] }, - { "n": "flag2", "ts": ["off"] } - ], - "trafficAllocation": 100, - "trafficAllocationSeed": 1595297106, - "seed": -1332540447, - "status": "ACTIVE", - "killed": false, - "defaultTreatment": "off", - "changeNumber": 1, - "algo": 2, - "sets": ["set1", "set2"], - "impressionsDisabled": true, - "configurations": { - "t1": "{\"f1\": \"v1\"}", - "t2": "{\"f2\": \"v2\"}" - }, - "conditions": [{ - "conditionType": "ROLLOUT", - "matcherGroup": { - "combiner": "AND", - "matchers": [{ - "keySelector": { - "trafficType": "custom", - "attribute": "atributoNumero" - }, - "matcherType": "BETWEEN", - "negate": false, - "userDefinedSegmentMatcherData": null, - "whitelistMatcherData": null, - "unaryNumericMatcherData": null, - "betweenMatcherData": { - "dataType": "NUMBER", - "start": 90, - "end": 500 - }, - "booleanMatcherData": null, - "dependencyMatcherData": null, - "stringMatcherData": null - }] +[{ + "trafficTypeName": "custom", + "name": "SAMPLE_FEATURE0", + "prerequisites": [ + { "n": "flag1", "ts": ["on","v1"] }, + { "n": "flag2", "ts": ["off"] } + ], + "trafficAllocation": 100, + "trafficAllocationSeed": 1595297106, + "seed": -1332540447, + "status": "ACTIVE", + "killed": false, + "defaultTreatment": "off", + "changeNumber": 1, + "algo": 2, + "sets": ["set1", "set2"], + "impressionsDisabled": true, + "configurations": { + "t1": "{\"f1\": \"v1\"}", + "t2": "{\"f2\": \"v2\"}" + }, + "conditions": [{ + "conditionType": "ROLLOUT", + "matcherGroup": { + "combiner": "AND", + "matchers": [{ + "keySelector": { + "trafficType": "custom", + "attribute": "atributoNumero" + }, + "matcherType": "BETWEEN", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": null, + "unaryNumericMatcherData": null, + "betweenMatcherData": { + "dataType": "NUMBER", + "start": 90, + "end": 500 + }, + "booleanMatcherData": null, + "dependencyMatcherData": null, + "stringMatcherData": null + }] + }, + "partitions": [{ + "treatment": "t1_0", + "size": 0 + }, { + "treatment": "t2_0", + "size": 0 + }, { + "treatment": "t3_0", + "size": 0 + }, { + "treatment": "t4_0", + "size": 100 + }, { + "treatment": "t5_0", + "size": 0 + }, { + "treatment": "t6_0", + "size": 0 + }], + "label": "atributoNumero between 90 and 500" + }, { + "conditionType": "ROLLOUT", + "matcherGroup": { + "combiner": "AND", + "matchers": [{ + "keySelector": { + "trafficType": "custom", + "attribute": "atributo2" + }, + "matcherType": "EQUAL_TO_SET", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": { + "whitelist": ["salamin"] + }, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "booleanMatcherData": null, + "dependencyMatcherData": null, + "stringMatcherData": null + }] + }, + "partitions": [{ + "treatment": "t1_0", + "size": 100 + }, { + "treatment": "t2_0", + "size": 0 + }, { + "treatment": "t3_0", + "size": 0 + }, { + "treatment": "t4_0", + "size": 0 + }, { + "treatment": "t5_0", + "size": 0 + }, { + "treatment": "t6_0", + "size": 0 + }], + "label": "atributo2 exactly matches [salamin]" + }, { + "conditionType": "ROLLOUT", + "matcherGroup": { + "combiner": "AND", + "matchers": [{ + "keySelector": { + "trafficType": "custom", + "attribute": "atributo1" + }, + "matcherType": "MATCHES_STRING", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": null, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "booleanMatcherData": null, + "dependencyMatcherData": null, + "stringMatcherData": "mila" + }] + }, + "partitions": [{ + "treatment": "t1_0", + "size": 0 + }, { + "treatment": "t2_0", + "size": 0 + }, { + "treatment": "t3_0", + "size": 100 + }, { + "treatment": "t4_0", + "size": 0 + }, { + "treatment": "t5_0", + "size": 0 + }, { + "treatment": "t6_0", + "size": 0 + }], + "label": "atributo1 matches mila" + }, { + "conditionType": "ROLLOUT", + "matcherGroup": { + "combiner": "AND", + "matchers": [{ + "keySelector": { + "trafficType": "custom", + "attribute": "atribute" + }, + "matcherType": "EQUAL_TO_SET", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": { + "whitelist": ["papapa"] + }, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "booleanMatcherData": null, + "dependencyMatcherData": null, + "stringMatcherData": null + }] + }, + "partitions": [{ + "treatment": "t1_0", + "size": 0 + }, { + "treatment": "t2_0", + "size": 100 + }, { + "treatment": "t3_0", + "size": 0 + }, { + "treatment": "t4_0", + "size": 0 + }, { + "treatment": "t5_0", + "size": 0 + }, { + "treatment": "t6_0", + "size": 0 + }], + "label": "atribute exactly matches[papapa]" + }, { + "conditionType": "ROLLOUT", + "matcherGroup": { + "combiner": "AND", + "matchers": [{ + "keySelector": { + "trafficType": "custom", + "attribute": null + }, + "matcherType": "ALL_KEYS", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": null, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "booleanMatcherData": null, + "dependencyMatcherData": null, + "stringMatcherData": null + }] + }, + "partitions": [{ + "treatment": "t1_0", + "size": 0 + }, { + "treatment": "t2_0", + "size": 0 + }, { + "treatment": "t3_0", + "size": 0 + }, { + "treatment": "t4_0", + "size": 100 + }, { + "treatment": "t5_0", + "size": 0 + }, { + "treatment": "t6_0", + "size": 0 + }], + "label": "default rule" + }] + }, { + "trafficTypeName": "custom1", + "name": "SAMPLE_FEATURE1", + "trafficAllocation": 100, + "trafficAllocationSeed": 1595297106, + "seed": -1332540447, + "status": "ACTIVE", + "killed": true, + "defaultTreatment": "off", + "changeNumber": 1, + "algo": 2, + "conditions": [{ + "conditionType": "ROLLOUT", + "matcherGroup": { + "combiner": "AND", + "matchers": [{ + "keySelector": { + "trafficType": "custom", + "attribute": "atributoNumero" + }, + "matcherType": "BETWEEN", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": null, + "unaryNumericMatcherData": null, + "betweenMatcherData": { + "dataType": "NUMBER", + "start": 90, + "end": 500 + }, + "booleanMatcherData": null, + "dependencyMatcherData": null, + "stringMatcherData": null + }] + }, + "partitions": [{ + "treatment": "t1_1", + "size": 0 + }, { + "treatment": "t2_1", + "size": 0 + }, { + "treatment": "t3_1", + "size": 0 + }, { + "treatment": "t4_1", + "size": 100 + }, { + "treatment": "t5_1", + "size": 0 + }, { + "treatment": "t6_1", + "size": 0 + }], + "label": "atributoNumero between 90 and 500" + }, { + "conditionType": "ROLLOUT", + "matcherGroup": { + "combiner": "AND", + "matchers": [{ + "keySelector": { + "trafficType": "custom", + "attribute": "atributo2" + }, + "matcherType": "EQUAL_TO_SET", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": { + "whitelist": ["salamin"] + }, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "booleanMatcherData": null, + "dependencyMatcherData": null, + "stringMatcherData": null + }] + }, + "partitions": [{ + "treatment": "t1_1", + "size": 100 + }, { + "treatment": "t2_1", + "size": 0 + }, { + "treatment": "t3_1", + "size": 0 + }, { + "treatment": "t4_1", + "size": 0 + }, { + "treatment": "t5_1", + "size": 0 + }, { + "treatment": "t6_1", + "size": 0 + }], + "label": "atributo2 exactly matches [salamin]" + }, { + "conditionType": "ROLLOUT", + "matcherGroup": { + "combiner": "AND", + "matchers": [{ + "keySelector": { + "trafficType": "custom", + "attribute": "atributo1" + }, + "matcherType": "MATCHES_STRING", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": null, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "booleanMatcherData": null, + "dependencyMatcherData": null, + "stringMatcherData": "mila" + }] + }, + "partitions": [{ + "treatment": "t1_1", + "size": 0 + }, { + "treatment": "t2_1", + "size": 0 + }, { + "treatment": "t3_1", + "size": 0 + }, { + "treatment": "t4_1", + "size": 100 + }, { + "treatment": "t5_1", + "size": 0 + }, { + "treatment": "t6_1", + "size": 0 + }], + "label": "atributo1 matches mila" + }, { + "conditionType": "ROLLOUT", + "matcherGroup": { + "combiner": "AND", + "matchers": [{ + "keySelector": { + "trafficType": "custom", + "attribute": "atribute" + }, + "matcherType": "EQUAL_TO_SET", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": { + "whitelist": ["papapa"] + }, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "booleanMatcherData": null, + "dependencyMatcherData": null, + "stringMatcherData": null + }] + }, + "partitions": [{ + "treatment": "t1_1", + "size": 0 + }, { + "treatment": "t2_1", + "size": 100 + }, { + "treatment": "t3_1", + "size": 0 + }, { + "treatment": "t4_1", + "size": 0 + }, { + "treatment": "t5_1", + "size": 0 + }, { + "treatment": "t6_1", + "size": 0 + }], + "label": "atribute exactly matches[papapa]" + }, { + "conditionType": "ROLLOUT", + "matcherGroup": { + "combiner": "AND", + "matchers": [{ + "keySelector": { + "trafficType": "custom", + "attribute": null + }, + "matcherType": "ALL_KEYS", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": null, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "booleanMatcherData": null, + "dependencyMatcherData": null, + "stringMatcherData": null + }] + }, + "partitions": [{ + "treatment": "t1_1", + "size": 0 + }, { + "treatment": "t2_1", + "size": 0 + }, { + "treatment": "t3_1", + "size": 0 + }, { + "treatment": "t4_1", + "size": 100 + }, { + "treatment": "t5_1", + "size": 0 + }, { + "treatment": "t6_1", + "size": 0 + }], + "label": "default rule" + }] + }, { + "trafficTypeName": "custom", + "name": "SAMPLE_FEATURE2", + "trafficAllocation": 100, + "trafficAllocationSeed": 1595297106, + "seed": -1332540447, + "status": "ACTIVE", + "killed": false, + "defaultTreatment": "off", + "changeNumber": 1, + "algo": 2, + "configurations": { + "t1": "{\"f1\": \"v1\"}", + "t3": "{\"f3\": \"v3\"}", + "t2": "{\"f2\": \"v2\"}" + }, + "conditions": [{ + "conditionType": "ROLLOUT", + "matcherGroup": { + "combiner": "AND", + "matchers": [{ + "keySelector": { + "trafficType": "custom", + "attribute": "atributoNumero" + }, + "matcherType": "BETWEEN", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": null, + "unaryNumericMatcherData": null, + "betweenMatcherData": { + "dataType": "NUMBER", + "start": 90, + "end": 500 + }, + "booleanMatcherData": null, + "dependencyMatcherData": null, + "stringMatcherData": null + }] + }, + "partitions": [{ + "treatment": "t1_2", + "size": 0 + }, { + "treatment": "t2_2", + "size": 0 + }, { + "treatment": "t3_2", + "size": 0 + }, { + "treatment": "t4_2", + "size": 100 + }, { + "treatment": "t5_2", + "size": 0 + }, { + "treatment": "t6_2", + "size": 0 + }], + "label": "atributoNumero between 90 and 500" + }, { + "conditionType": "ROLLOUT", + "matcherGroup": { + "combiner": "AND", + "matchers": [{ + "keySelector": { + "trafficType": "custom", + "attribute": "atributo2" + }, + "matcherType": "EQUAL_TO_SET", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": { + "whitelist": ["salamin"] + }, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "booleanMatcherData": null, + "dependencyMatcherData": null, + "stringMatcherData": null + }] + }, + "partitions": [{ + "treatment": "t1_2", + "size": 100 + }, { + "treatment": "t2_2", + "size": 0 + }, { + "treatment": "t3_2", + "size": 0 + }, { + "treatment": "t4_2", + "size": 0 + }, { + "treatment": "t5_2", + "size": 0 + }, { + "treatment": "t6_2", + "size": 0 + }], + "label": "atributo2 exactly matches [salamin]" + }, { + "conditionType": "ROLLOUT", + "matcherGroup": { + "combiner": "AND", + "matchers": [{ + "keySelector": { + "trafficType": "custom", + "attribute": "atributo1" + }, + "matcherType": "MATCHES_STRING", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": null, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "booleanMatcherData": null, + "dependencyMatcherData": null, + "stringMatcherData": "mila" + }] + }, + "partitions": [{ + "treatment": "t1_2", + "size": 0 + }, { + "treatment": "t2_2", + "size": 0 + }, { + "treatment": "t3_2", + "size": 100 + }, { + "treatment": "t4_2", + "size": 0 + }, { + "treatment": "t5_2", + "size": 0 + }, { + "treatment": "t6_2", + "size": 0 + }], + "label": "atributo1 matches mila" + }, { + "conditionType": "ROLLOUT", + "matcherGroup": { + "combiner": "AND", + "matchers": [{ + "keySelector": { + "trafficType": "custom", + "attribute": "atribute" + }, + "matcherType": "EQUAL_TO_SET", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": { + "whitelist": ["papapa"] + }, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "booleanMatcherData": null, + "dependencyMatcherData": null, + "stringMatcherData": null + }] + }, + "partitions": [{ + "treatment": "t1_2", + "size": 0 + }, { + "treatment": "t2_2", + "size": 100 + }, { + "treatment": "t3_2", + "size": 0 + }, { + "treatment": "t4_2", + "size": 0 + }, { + "treatment": "t5_2", + "size": 0 + }, { + "treatment": "t6_2", + "size": 0 + }], + "label": "atribute exactly matches[papapa]" + }, { + "conditionType": "ROLLOUT", + "matcherGroup": { + "combiner": "AND", + "matchers": [{ + "keySelector": { + "trafficType": "custom", + "attribute": null + }, + "matcherType": "ALL_KEYS", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": null, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "booleanMatcherData": null, + "dependencyMatcherData": null, + "stringMatcherData": null + }] + }, + "partitions": [{ + "treatment": "t1_2", + "size": 0 + }, { + "treatment": "t2_2", + "size": 0 + }, { + "treatment": "t3_2", + "size": 0 + }, { + "treatment": "t4_2", + "size": 100 + }, { + "treatment": "t5_2", + "size": 0 + }, { + "treatment": "t6_2", + "size": 0 + }], + "label": "default rule" + }] + }, { + "trafficTypeName": "custom", + "name": "SAMPLE_FEATURE3", + "trafficAllocation": 100, + "trafficAllocationSeed": 1595297106, + "seed": -1332540447, + "status": "ACTIVE", + "killed": false, + "defaultTreatment": "off", + "changeNumber": 1, + "algo": 2, + "conditions": [{ + "conditionType": "ROLLOUT", + "matcherGroup": { + "combiner": "AND", + "matchers": [{ + "keySelector": { + "trafficType": "custom", + "attribute": "atributoNumero" + }, + "matcherType": "BETWEEN", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": null, + "unaryNumericMatcherData": null, + "betweenMatcherData": { + "dataType": "NUMBER", + "start": 90, + "end": 500 + }, + "booleanMatcherData": null, + "dependencyMatcherData": null, + "stringMatcherData": null + }] + }, + "partitions": [{ + "treatment": "t1_3", + "size": 0 + }, { + "treatment": "t2_3", + "size": 0 + }, { + "treatment": "t3_3", + "size": 0 + }, { + "treatment": "t4_3", + "size": 100 + }, { + "treatment": "t5_3", + "size": 0 + }, { + "treatment": "t6_3", + "size": 0 + }], + "label": "atributoNumero between 90 and 500" + }, { + "conditionType": "ROLLOUT", + "matcherGroup": { + "combiner": "AND", + "matchers": [{ + "keySelector": { + "trafficType": "custom", + "attribute": "atributo2" + }, + "matcherType": "EQUAL_TO_SET", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": { + "whitelist": ["salamin"] + }, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "booleanMatcherData": null, + "dependencyMatcherData": null, + "stringMatcherData": null + }] + }, + "partitions": [{ + "treatment": "t1_3", + "size": 100 + }, { + "treatment": "t2_3", + "size": 0 + }, { + "treatment": "t3_3", + "size": 0 + }, { + "treatment": "t4_3", + "size": 0 + }, { + "treatment": "t5_3", + "size": 0 + }, { + "treatment": "t6_3", + "size": 0 + }], + "label": "atributo2 exactly matches [salamin]" + }, { + "conditionType": "ROLLOUT", + "matcherGroup": { + "combiner": "AND", + "matchers": [{ + "keySelector": { + "trafficType": "custom", + "attribute": "atributo1" + }, + "matcherType": "MATCHES_STRING", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": null, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "booleanMatcherData": null, + "dependencyMatcherData": null, + "stringMatcherData": "mila" + }] + }, + "partitions": [{ + "treatment": "t1_3", + "size": 0 + }, { + "treatment": "t2_3", + "size": 0 + }, { + "treatment": "t3_3", + "size": 100 + }, { + "treatment": "t4_3", + "size": 0 + }, { + "treatment": "t5_3", + "size": 0 + }, { + "treatment": "t6_3", + "size": 0 + }], + "label": "atributo1 matches mila" + }, { + "conditionType": "ROLLOUT", + "matcherGroup": { + "combiner": "AND", + "matchers": [{ + "keySelector": { + "trafficType": "custom", + "attribute": "atribute" + }, + "matcherType": "EQUAL_TO_SET", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": { + "whitelist": ["papapa"] + }, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "booleanMatcherData": null, + "dependencyMatcherData": null, + "stringMatcherData": null + }] + }, + "partitions": [{ + "treatment": "t1_3", + "size": 0 + }, { + "treatment": "t2_3", + "size": 100 + }, { + "treatment": "t3_3", + "size": 0 + }, { + "treatment": "t4_3", + "size": 0 + }, { + "treatment": "t5_3", + "size": 0 + }, { + "treatment": "t6_3", + "size": 0 + }], + "label": "atribute exactly matches[papapa]" + }, { + "conditionType": "ROLLOUT", + "matcherGroup": { + "combiner": "AND", + "matchers": [{ + "keySelector": { + "trafficType": "custom", + "attribute": null + }, + "matcherType": "ALL_KEYS", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": null, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "booleanMatcherData": null, + "dependencyMatcherData": null, + "stringMatcherData": null + }] + }, + "partitions": [{ + "treatment": "t1_3", + "size": 0 + }, { + "treatment": "t2_3", + "size": 0 + }, { + "treatment": "t3_3", + "size": 0 + }, { + "treatment": "t4_3", + "size": 100 + }, { + "treatment": "t5_3", + "size": 0 + }, { + "treatment": "t6_3", + "size": 0 + }], + "label": "default rule" + }] + }, { + "trafficTypeName": "custom", + "name": "SAMPLE_FEATURE4", + "trafficAllocation": 100, + "trafficAllocationSeed": 1595297106, + "seed": -1332540447, + "status": "ACTIVE", + "killed": false, + "defaultTreatment": "off", + "changeNumber": 1, + "algo": 2, + "configurations": { + "t1": "{\"f1\": \"v1\"}", + "t3": "{\"f3\": \"v3\"}", + "t4": "{\"f4\": \"v4\"}", + "t2": "{\"f2\": \"v2\"}" + }, + "conditions": [{ + "conditionType": "ROLLOUT", + "matcherGroup": { + "combiner": "AND", + "matchers": [{ + "keySelector": { + "trafficType": "custom", + "attribute": "atributoNumero" + }, + "matcherType": "BETWEEN", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": null, + "unaryNumericMatcherData": null, + "betweenMatcherData": { + "dataType": "NUMBER", + "start": 90, + "end": 500 + }, + "booleanMatcherData": null, + "dependencyMatcherData": null, + "stringMatcherData": null + }] + }, + "partitions": [{ + "treatment": "t1_4", + "size": 0 + }, { + "treatment": "t2_4", + "size": 0 + }, { + "treatment": "t3_4", + "size": 0 + }, { + "treatment": "t4_4", + "size": 100 + }, { + "treatment": "t5_4", + "size": 0 + }, { + "treatment": "t6_4", + "size": 0 + }], + "label": "atributoNumero between 90 and 500" + }, { + "conditionType": "ROLLOUT", + "matcherGroup": { + "combiner": "AND", + "matchers": [{ + "keySelector": { + "trafficType": "custom", + "attribute": "atributo2" + }, + "matcherType": "EQUAL_TO_SET", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": { + "whitelist": ["salamin"] + }, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "booleanMatcherData": null, + "dependencyMatcherData": null, + "stringMatcherData": null + }] + }, + "partitions": [{ + "treatment": "t1_4", + "size": 100 + }, { + "treatment": "t2_4", + "size": 0 + }, { + "treatment": "t3_4", + "size": 0 + }, { + "treatment": "t4_4", + "size": 100 + }, { + "treatment": "t5_4", + "size": 0 + }, { + "treatment": "t6_4", + "size": 0 + }], + "label": "atributo2 exactly matches [salamin]" + }, { + "conditionType": "ROLLOUT", + "matcherGroup": { + "combiner": "AND", + "matchers": [{ + "keySelector": { + "trafficType": "custom", + "attribute": "atributo1" + }, + "matcherType": "MATCHES_STRING", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": null, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "booleanMatcherData": null, + "dependencyMatcherData": null, + "stringMatcherData": "mila" + }] + }, + "partitions": [{ + "treatment": "t1_4", + "size": 0 + }, { + "treatment": "t2_4", + "size": 0 + }, { + "treatment": "t3_4", + "size": 100 + }, { + "treatment": "t4_4", + "size": 0 + }, { + "treatment": "t5_4", + "size": 0 + }, { + "treatment": "t6_4", + "size": 0 + }], + "label": "atributo1 matches mila" + }, { + "conditionType": "ROLLOUT", + "matcherGroup": { + "combiner": "AND", + "matchers": [{ + "keySelector": { + "trafficType": "custom", + "attribute": "atribute" + }, + "matcherType": "EQUAL_TO_SET", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": { + "whitelist": ["papapa"] + }, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "booleanMatcherData": null, + "dependencyMatcherData": null, + "stringMatcherData": null + }] + }, + "partitions": [{ + "treatment": "t1_4", + "size": 0 + }, { + "treatment": "t2_4", + "size": 100 + }, { + "treatment": "t3_4", + "size": 0 + }, { + "treatment": "t4_4", + "size": 0 + }, { + "treatment": "t5_4", + "size": 0 + }, { + "treatment": "t6_4", + "size": 0 + }], + "label": "atribute exactly matches[papapa]" + }, { + "conditionType": "ROLLOUT", + "matcherGroup": { + "combiner": "AND", + "matchers": [{ + "keySelector": { + "trafficType": "custom", + "attribute": null + }, + "matcherType": "ALL_KEYS", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": null, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "booleanMatcherData": null, + "dependencyMatcherData": null, + "stringMatcherData": null + }] + }, + "partitions": [{ + "treatment": "t1_4", + "size": 0 + }, { + "treatment": "t2_4", + "size": 0 + }, { + "treatment": "t3_4", + "size": 0 + }, { + "treatment": "t4_4", + "size": 100 + }, { + "treatment": "t5_4", + "size": 0 + }, { + "treatment": "t6_4", + "size": 0 + }], + "label": "default rule" + }] + }, { + "trafficTypeName": "custom", + "name": "SAMPLE_FEATURE5", + "trafficAllocation": 100, + "trafficAllocationSeed": 1595297106, + "seed": -1332540447, + "status": "ACTIVE", + "killed": false, + "defaultTreatment": "off", + "changeNumber": 1, + "algo": 2, + "conditions": [{ + "conditionType": "ROLLOUT", + "matcherGroup": { + "combiner": "AND", + "matchers": [{ + "keySelector": { + "trafficType": "custom", + "attribute": "atributoNumero" + }, + "matcherType": "BETWEEN", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": null, + "unaryNumericMatcherData": null, + "betweenMatcherData": { + "dataType": "NUMBER", + "start": 90, + "end": 500 + }, + "booleanMatcherData": null, + "dependencyMatcherData": null, + "stringMatcherData": null + }] + }, + "partitions": [{ + "treatment": "t1_5", + "size": 100 + }, { + "treatment": "t2_5", + "size": 0 + }, { + "treatment": "t3_5", + "size": 0 + }, { + "treatment": "t4_5", + "size": 0 + }, { + "treatment": "t5_5", + "size": 0 + }, { + "treatment": "t6_5", + "size": 0 + }], + "label": "atributoNumero between 3 and 30" + }, { + "conditionType": "ROLLOUT", + "matcherGroup": { + "combiner": "AND", + "matchers": [{ + "keySelector": { + "trafficType": "custom", + "attribute": "atributo2" + }, + "matcherType": "EQUAL_TO_SET", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": { + "whitelist": ["salamin"] + }, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "booleanMatcherData": null, + "dependencyMatcherData": null, + "stringMatcherData": null + }] + }, + "partitions": [{ + "treatment": "t1_5", + "size": 0 + }, { + "treatment": "t2_5", + "size": 0 + }, { + "treatment": "t3_5", + "size": 0 + }, { + "treatment": "t4_5", + "size": 100 + }, { + "treatment": "t5_5", + "size": 0 + }, { + "treatment": "t6_5", + "size": 0 + }], + "label": "atributo2 exactly matches [salamin]" + }, { + "conditionType": "ROLLOUT", + "matcherGroup": { + "combiner": "AND", + "matchers": [{ + "keySelector": { + "trafficType": "custom", + "attribute": "atributo1" + }, + "matcherType": "MATCHES_STRING", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": null, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "booleanMatcherData": null, + "dependencyMatcherData": null, + "stringMatcherData": "mila" + }] + }, + "partitions": [{ + "treatment": "t1_5", + "size": 0 + }, { + "treatment": "t2_5", + "size": 0 + }, { + "treatment": "t3_5", + "size": 0 + }, { + "treatment": "t4_5", + "size": 0 + }, { + "treatment": "t5_5", + "size": 100 + }, { + "treatment": "t6_5", + "size": 0 + }], + "label": "atributo1 matches mila" + }, { + "conditionType": "ROLLOUT", + "matcherGroup": { + "combiner": "AND", + "matchers": [{ + "keySelector": { + "trafficType": "custom", + "attribute": "atribute" + }, + "matcherType": "EQUAL_TO_SET", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": { + "whitelist": ["papapa"] + }, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "booleanMatcherData": null, + "dependencyMatcherData": null, + "stringMatcherData": null + }] + }, + "partitions": [{ + "treatment": "t1_5", + "size": 0 + }, { + "treatment": "t2_5", + "size": 0 + }, { + "treatment": "t3_5", + "size": 100 + }, { + "treatment": "t4_5", + "size": 0 + }, { + "treatment": "t5_5", + "size": 0 + }, { + "treatment": "t6_5", + "size": 0 + }], + "label": "atribute exactly matches[papapa]" + }, { + "conditionType": "ROLLOUT", + "matcherGroup": { + "combiner": "AND", + "matchers": [{ + "keySelector": { + "trafficType": "custom", + "attribute": null + }, + "matcherType": "ALL_KEYS", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": null, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "booleanMatcherData": null, + "dependencyMatcherData": null, + "stringMatcherData": null + }] + }, + "partitions": [{ + "treatment": "t1_5", + "size": 0 + }, { + "treatment": "t2_5", + "size": 0 + }, { + "treatment": "t3_5", + "size": 0 + }, { + "treatment": "t4_5", + "size": 0 + }, { + "treatment": "t5_5", + "size": 0 + }, { + "treatment": "t6_5", + "size": 100 + }], + "label": "default rule" + }] + }, + { + "name": "always_on_if_prerequisite", + "trafficTypeName": "user", + "trafficAllocation": 100, + "trafficAllocationSeed": 1828377380, + "changeNumber": 5, + "seed": -790401604, + "status": "ACTIVE", + "killed": false, + "defaultTreatment": "off", + "prerequisites": [ + { + "n": "rbs_test_flag", + "ts": [ + "v1" + ] + } + ], + "conditions": [ + { + "conditionType": "ROLLOUT", + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "user", + "attribute": null }, - "partitions": [{ - "treatment": "t1_0", - "size": 0 - }, { - "treatment": "t2_0", - "size": 0 - }, { - "treatment": "t3_0", - "size": 0 - }, { - "treatment": "t4_0", - "size": 100 - }, { - "treatment": "t5_0", - "size": 0 - }, { - "treatment": "t6_0", - "size": 0 - }], - "label": "atributoNumero between 90 and 500" - }, { - "conditionType": "ROLLOUT", - "matcherGroup": { - "combiner": "AND", - "matchers": [{ - "keySelector": { - "trafficType": "custom", - "attribute": "atributo2" - }, - "matcherType": "EQUAL_TO_SET", - "negate": false, - "userDefinedSegmentMatcherData": null, - "whitelistMatcherData": { - "whitelist": ["salamin"] - }, - "unaryNumericMatcherData": null, - "betweenMatcherData": null, - "booleanMatcherData": null, - "dependencyMatcherData": null, - "stringMatcherData": null - }] + "matcherType": "ALL_KEYS", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": null, + "unaryNumericMatcherData": null, + "betweenMatcherData": null + } + ] + }, + "partitions": [ + { + "treatment": "on", + "size": 100 + } + ], + "label": "always_on_if_prerequisite label" + } + ] + }, + { + "name": "rbs_test_flag", + "trafficTypeName": "user", + "trafficAllocation": 100, + "trafficAllocationSeed": 1828377380, + "seed": -286617921, + "status": "ACTIVE", + "killed": false, + "defaultTreatment": "off", + "algo": 2, + "conditions": [ + { + "conditionType": "ROLLOUT", + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "user" }, - "partitions": [{ - "treatment": "t1_0", - "size": 100 - }, { - "treatment": "t2_0", - "size": 0 - }, { - "treatment": "t3_0", - "size": 0 - }, { - "treatment": "t4_0", - "size": 0 - }, { - "treatment": "t5_0", - "size": 0 - }, { - "treatment": "t6_0", - "size": 0 - }], - "label": "atributo2 exactly matches [salamin]" - }, { - "conditionType": "ROLLOUT", - "matcherGroup": { - "combiner": "AND", - "matchers": [{ - "keySelector": { - "trafficType": "custom", - "attribute": "atributo1" - }, - "matcherType": "MATCHES_STRING", - "negate": false, - "userDefinedSegmentMatcherData": null, - "whitelistMatcherData": null, - "unaryNumericMatcherData": null, - "betweenMatcherData": null, - "booleanMatcherData": null, - "dependencyMatcherData": null, - "stringMatcherData": "mila" - }] + "matcherType": "IN_RULE_BASED_SEGMENT", + "negate": false, + "userDefinedSegmentMatcherData": { + "segmentName": "test_rule_based_segment" + } + } + ] + }, + "partitions": [ + { + "treatment": "v1", + "size": 100 + }, + { + "treatment": "v2", + "size": 0 + } + ], + "label": "in rule based segment test_rule_based_segment" + }, + { + "conditionType": "ROLLOUT", + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "user" }, - "partitions": [{ - "treatment": "t1_0", - "size": 0 - }, { - "treatment": "t2_0", - "size": 0 - }, { - "treatment": "t3_0", - "size": 100 - }, { - "treatment": "t4_0", - "size": 0 - }, { - "treatment": "t5_0", - "size": 0 - }, { - "treatment": "t6_0", - "size": 0 - }], - "label": "atributo1 matches mila" - }, { - "conditionType": "ROLLOUT", - "matcherGroup": { - "combiner": "AND", - "matchers": [{ - "keySelector": { - "trafficType": "custom", - "attribute": "atribute" - }, - "matcherType": "EQUAL_TO_SET", - "negate": false, - "userDefinedSegmentMatcherData": null, - "whitelistMatcherData": { - "whitelist": ["papapa"] - }, - "unaryNumericMatcherData": null, - "betweenMatcherData": null, - "booleanMatcherData": null, - "dependencyMatcherData": null, - "stringMatcherData": null - }] + "matcherType": "ALL_KEYS", + "negate": false + } + ] + }, + "partitions": [ + { + "treatment": "v1", + "size": 0 + }, + { + "treatment": "v2", + "size": 100 + } + ], + "label": "default rule" + } + ], + "configurations": {}, + "sets": [], + "impressionsDisabled": false + } + ,{ + "rbs": { + "s": -1, + "t": 100, + "d": [ + { + "name": "test_rule_based_segment", + "status": "ACTIVE", + "trafficTypeName": "user", + "excluded": { + "keys": [ + "mauro@split.io", + "gaston@split.io" + ], + "segments": [] + }, + "conditions": [ + { + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "user" }, - "partitions": [{ - "treatment": "t1_0", - "size": 0 - }, { - "treatment": "t2_0", - "size": 100 - }, { - "treatment": "t3_0", - "size": 0 - }, { - "treatment": "t4_0", - "size": 0 - }, { - "treatment": "t5_0", - "size": 0 - }, { - "treatment": "t6_0", - "size": 0 - }], - "label": "atribute exactly matches[papapa]" - }, { - "conditionType": "ROLLOUT", - "matcherGroup": { - "combiner": "AND", - "matchers": [{ - "keySelector": { - "trafficType": "custom", - "attribute": null - }, - "matcherType": "ALL_KEYS", - "negate": false, - "userDefinedSegmentMatcherData": null, - "whitelistMatcherData": null, - "unaryNumericMatcherData": null, - "betweenMatcherData": null, - "booleanMatcherData": null, - "dependencyMatcherData": null, - "stringMatcherData": null - }] - }, - "partitions": [{ - "treatment": "t1_0", - "size": 0 - }, { - "treatment": "t2_0", - "size": 0 - }, { - "treatment": "t3_0", - "size": 0 - }, { - "treatment": "t4_0", - "size": 100 - }, { - "treatment": "t5_0", - "size": 0 - }, { - "treatment": "t6_0", - "size": 0 - }], - "label": "default rule" - }] - }, { - "trafficTypeName": "custom1", - "name": "SAMPLE_FEATURE1", - "trafficAllocation": 100, - "trafficAllocationSeed": 1595297106, - "seed": -1332540447, - "status": "ACTIVE", - "killed": true, - "defaultTreatment": "off", - "changeNumber": 1, - "algo": 2, - "conditions": [{ - "conditionType": "ROLLOUT", - "matcherGroup": { - "combiner": "AND", - "matchers": [{ - "keySelector": { - "trafficType": "custom", - "attribute": "atributoNumero" - }, - "matcherType": "BETWEEN", - "negate": false, - "userDefinedSegmentMatcherData": null, - "whitelistMatcherData": null, - "unaryNumericMatcherData": null, - "betweenMatcherData": { - "dataType": "NUMBER", - "start": 90, - "end": 500 - }, - "booleanMatcherData": null, - "dependencyMatcherData": null, - "stringMatcherData": null - }] - }, - "partitions": [{ - "treatment": "t1_1", - "size": 0 - }, { - "treatment": "t2_1", - "size": 0 - }, { - "treatment": "t3_1", - "size": 0 - }, { - "treatment": "t4_1", - "size": 100 - }, { - "treatment": "t5_1", - "size": 0 - }, { - "treatment": "t6_1", - "size": 0 - }], - "label": "atributoNumero between 90 and 500" - }, { - "conditionType": "ROLLOUT", - "matcherGroup": { - "combiner": "AND", - "matchers": [{ - "keySelector": { - "trafficType": "custom", - "attribute": "atributo2" - }, - "matcherType": "EQUAL_TO_SET", - "negate": false, - "userDefinedSegmentMatcherData": null, - "whitelistMatcherData": { - "whitelist": ["salamin"] - }, - "unaryNumericMatcherData": null, - "betweenMatcherData": null, - "booleanMatcherData": null, - "dependencyMatcherData": null, - "stringMatcherData": null - }] - }, - "partitions": [{ - "treatment": "t1_1", - "size": 100 - }, { - "treatment": "t2_1", - "size": 0 - }, { - "treatment": "t3_1", - "size": 0 - }, { - "treatment": "t4_1", - "size": 0 - }, { - "treatment": "t5_1", - "size": 0 - }, { - "treatment": "t6_1", - "size": 0 - }], - "label": "atributo2 exactly matches [salamin]" - }, { - "conditionType": "ROLLOUT", - "matcherGroup": { - "combiner": "AND", - "matchers": [{ - "keySelector": { - "trafficType": "custom", - "attribute": "atributo1" - }, - "matcherType": "MATCHES_STRING", - "negate": false, - "userDefinedSegmentMatcherData": null, - "whitelistMatcherData": null, - "unaryNumericMatcherData": null, - "betweenMatcherData": null, - "booleanMatcherData": null, - "dependencyMatcherData": null, - "stringMatcherData": "mila" - }] - }, - "partitions": [{ - "treatment": "t1_1", - "size": 0 - }, { - "treatment": "t2_1", - "size": 0 - }, { - "treatment": "t3_1", - "size": 0 - }, { - "treatment": "t4_1", - "size": 100 - }, { - "treatment": "t5_1", - "size": 0 - }, { - "treatment": "t6_1", - "size": 0 - }], - "label": "atributo1 matches mila" - }, { - "conditionType": "ROLLOUT", - "matcherGroup": { - "combiner": "AND", - "matchers": [{ - "keySelector": { - "trafficType": "custom", - "attribute": "atribute" - }, - "matcherType": "EQUAL_TO_SET", - "negate": false, - "userDefinedSegmentMatcherData": null, - "whitelistMatcherData": { - "whitelist": ["papapa"] - }, - "unaryNumericMatcherData": null, - "betweenMatcherData": null, - "booleanMatcherData": null, - "dependencyMatcherData": null, - "stringMatcherData": null - }] - }, - "partitions": [{ - "treatment": "t1_1", - "size": 0 - }, { - "treatment": "t2_1", - "size": 100 - }, { - "treatment": "t3_1", - "size": 0 - }, { - "treatment": "t4_1", - "size": 0 - }, { - "treatment": "t5_1", - "size": 0 - }, { - "treatment": "t6_1", - "size": 0 - }], - "label": "atribute exactly matches[papapa]" - }, { - "conditionType": "ROLLOUT", - "matcherGroup": { - "combiner": "AND", - "matchers": [{ - "keySelector": { - "trafficType": "custom", - "attribute": null - }, - "matcherType": "ALL_KEYS", - "negate": false, - "userDefinedSegmentMatcherData": null, - "whitelistMatcherData": null, - "unaryNumericMatcherData": null, - "betweenMatcherData": null, - "booleanMatcherData": null, - "dependencyMatcherData": null, - "stringMatcherData": null - }] - }, - "partitions": [{ - "treatment": "t1_1", - "size": 0 - }, { - "treatment": "t2_1", - "size": 0 - }, { - "treatment": "t3_1", - "size": 0 - }, { - "treatment": "t4_1", - "size": 100 - }, { - "treatment": "t5_1", - "size": 0 - }, { - "treatment": "t6_1", - "size": 0 - }], - "label": "default rule" - }] - }, { - "trafficTypeName": "custom", - "name": "SAMPLE_FEATURE2", - "trafficAllocation": 100, - "trafficAllocationSeed": 1595297106, - "seed": -1332540447, - "status": "ACTIVE", - "killed": false, - "defaultTreatment": "off", - "changeNumber": 1, - "algo": 2, - "configurations": { - "t1": "{\"f1\": \"v1\"}", - "t3": "{\"f3\": \"v3\"}", - "t2": "{\"f2\": \"v2\"}" - }, - "conditions": [{ - "conditionType": "ROLLOUT", - "matcherGroup": { - "combiner": "AND", - "matchers": [{ - "keySelector": { - "trafficType": "custom", - "attribute": "atributoNumero" - }, - "matcherType": "BETWEEN", - "negate": false, - "userDefinedSegmentMatcherData": null, - "whitelistMatcherData": null, - "unaryNumericMatcherData": null, - "betweenMatcherData": { - "dataType": "NUMBER", - "start": 90, - "end": 500 - }, - "booleanMatcherData": null, - "dependencyMatcherData": null, - "stringMatcherData": null - }] - }, - "partitions": [{ - "treatment": "t1_2", - "size": 0 - }, { - "treatment": "t2_2", - "size": 0 - }, { - "treatment": "t3_2", - "size": 0 - }, { - "treatment": "t4_2", - "size": 100 - }, { - "treatment": "t5_2", - "size": 0 - }, { - "treatment": "t6_2", - "size": 0 - }], - "label": "atributoNumero between 90 and 500" - }, { - "conditionType": "ROLLOUT", - "matcherGroup": { - "combiner": "AND", - "matchers": [{ - "keySelector": { - "trafficType": "custom", - "attribute": "atributo2" - }, - "matcherType": "EQUAL_TO_SET", - "negate": false, - "userDefinedSegmentMatcherData": null, - "whitelistMatcherData": { - "whitelist": ["salamin"] - }, - "unaryNumericMatcherData": null, - "betweenMatcherData": null, - "booleanMatcherData": null, - "dependencyMatcherData": null, - "stringMatcherData": null - }] - }, - "partitions": [{ - "treatment": "t1_2", - "size": 100 - }, { - "treatment": "t2_2", - "size": 0 - }, { - "treatment": "t3_2", - "size": 0 - }, { - "treatment": "t4_2", - "size": 0 - }, { - "treatment": "t5_2", - "size": 0 - }, { - "treatment": "t6_2", - "size": 0 - }], - "label": "atributo2 exactly matches [salamin]" - }, { - "conditionType": "ROLLOUT", - "matcherGroup": { - "combiner": "AND", - "matchers": [{ - "keySelector": { - "trafficType": "custom", - "attribute": "atributo1" - }, - "matcherType": "MATCHES_STRING", - "negate": false, - "userDefinedSegmentMatcherData": null, - "whitelistMatcherData": null, - "unaryNumericMatcherData": null, - "betweenMatcherData": null, - "booleanMatcherData": null, - "dependencyMatcherData": null, - "stringMatcherData": "mila" - }] - }, - "partitions": [{ - "treatment": "t1_2", - "size": 0 - }, { - "treatment": "t2_2", - "size": 0 - }, { - "treatment": "t3_2", - "size": 100 - }, { - "treatment": "t4_2", - "size": 0 - }, { - "treatment": "t5_2", - "size": 0 - }, { - "treatment": "t6_2", - "size": 0 - }], - "label": "atributo1 matches mila" - }, { - "conditionType": "ROLLOUT", - "matcherGroup": { - "combiner": "AND", - "matchers": [{ - "keySelector": { - "trafficType": "custom", - "attribute": "atribute" - }, - "matcherType": "EQUAL_TO_SET", - "negate": false, - "userDefinedSegmentMatcherData": null, - "whitelistMatcherData": { - "whitelist": ["papapa"] - }, - "unaryNumericMatcherData": null, - "betweenMatcherData": null, - "booleanMatcherData": null, - "dependencyMatcherData": null, - "stringMatcherData": null - }] - }, - "partitions": [{ - "treatment": "t1_2", - "size": 0 - }, { - "treatment": "t2_2", - "size": 100 - }, { - "treatment": "t3_2", - "size": 0 - }, { - "treatment": "t4_2", - "size": 0 - }, { - "treatment": "t5_2", - "size": 0 - }, { - "treatment": "t6_2", - "size": 0 - }], - "label": "atribute exactly matches[papapa]" - }, { - "conditionType": "ROLLOUT", - "matcherGroup": { - "combiner": "AND", - "matchers": [{ - "keySelector": { - "trafficType": "custom", - "attribute": null - }, - "matcherType": "ALL_KEYS", - "negate": false, - "userDefinedSegmentMatcherData": null, - "whitelistMatcherData": null, - "unaryNumericMatcherData": null, - "betweenMatcherData": null, - "booleanMatcherData": null, - "dependencyMatcherData": null, - "stringMatcherData": null - }] - }, - "partitions": [{ - "treatment": "t1_2", - "size": 0 - }, { - "treatment": "t2_2", - "size": 0 - }, { - "treatment": "t3_2", - "size": 0 - }, { - "treatment": "t4_2", - "size": 100 - }, { - "treatment": "t5_2", - "size": 0 - }, { - "treatment": "t6_2", - "size": 0 - }], - "label": "default rule" - }] - }, { - "trafficTypeName": "custom", - "name": "SAMPLE_FEATURE3", - "trafficAllocation": 100, - "trafficAllocationSeed": 1595297106, - "seed": -1332540447, - "status": "ACTIVE", - "killed": false, - "defaultTreatment": "off", - "changeNumber": 1, - "algo": 2, - "conditions": [{ - "conditionType": "ROLLOUT", - "matcherGroup": { - "combiner": "AND", - "matchers": [{ - "keySelector": { - "trafficType": "custom", - "attribute": "atributoNumero" - }, - "matcherType": "BETWEEN", - "negate": false, - "userDefinedSegmentMatcherData": null, - "whitelistMatcherData": null, - "unaryNumericMatcherData": null, - "betweenMatcherData": { - "dataType": "NUMBER", - "start": 90, - "end": 500 - }, - "booleanMatcherData": null, - "dependencyMatcherData": null, - "stringMatcherData": null - }] - }, - "partitions": [{ - "treatment": "t1_3", - "size": 0 - }, { - "treatment": "t2_3", - "size": 0 - }, { - "treatment": "t3_3", - "size": 0 - }, { - "treatment": "t4_3", - "size": 100 - }, { - "treatment": "t5_3", - "size": 0 - }, { - "treatment": "t6_3", - "size": 0 - }], - "label": "atributoNumero between 90 and 500" - }, { - "conditionType": "ROLLOUT", - "matcherGroup": { - "combiner": "AND", - "matchers": [{ - "keySelector": { - "trafficType": "custom", - "attribute": "atributo2" - }, - "matcherType": "EQUAL_TO_SET", - "negate": false, - "userDefinedSegmentMatcherData": null, - "whitelistMatcherData": { - "whitelist": ["salamin"] - }, - "unaryNumericMatcherData": null, - "betweenMatcherData": null, - "booleanMatcherData": null, - "dependencyMatcherData": null, - "stringMatcherData": null - }] - }, - "partitions": [{ - "treatment": "t1_3", - "size": 100 - }, { - "treatment": "t2_3", - "size": 0 - }, { - "treatment": "t3_3", - "size": 0 - }, { - "treatment": "t4_3", - "size": 0 - }, { - "treatment": "t5_3", - "size": 0 - }, { - "treatment": "t6_3", - "size": 0 - }], - "label": "atributo2 exactly matches [salamin]" - }, { - "conditionType": "ROLLOUT", - "matcherGroup": { - "combiner": "AND", - "matchers": [{ - "keySelector": { - "trafficType": "custom", - "attribute": "atributo1" - }, - "matcherType": "MATCHES_STRING", - "negate": false, - "userDefinedSegmentMatcherData": null, - "whitelistMatcherData": null, - "unaryNumericMatcherData": null, - "betweenMatcherData": null, - "booleanMatcherData": null, - "dependencyMatcherData": null, - "stringMatcherData": "mila" - }] - }, - "partitions": [{ - "treatment": "t1_3", - "size": 0 - }, { - "treatment": "t2_3", - "size": 0 - }, { - "treatment": "t3_3", - "size": 100 - }, { - "treatment": "t4_3", - "size": 0 - }, { - "treatment": "t5_3", - "size": 0 - }, { - "treatment": "t6_3", - "size": 0 - }], - "label": "atributo1 matches mila" - }, { - "conditionType": "ROLLOUT", - "matcherGroup": { - "combiner": "AND", - "matchers": [{ - "keySelector": { - "trafficType": "custom", - "attribute": "atribute" - }, - "matcherType": "EQUAL_TO_SET", - "negate": false, - "userDefinedSegmentMatcherData": null, - "whitelistMatcherData": { - "whitelist": ["papapa"] - }, - "unaryNumericMatcherData": null, - "betweenMatcherData": null, - "booleanMatcherData": null, - "dependencyMatcherData": null, - "stringMatcherData": null - }] - }, - "partitions": [{ - "treatment": "t1_3", - "size": 0 - }, { - "treatment": "t2_3", - "size": 100 - }, { - "treatment": "t3_3", - "size": 0 - }, { - "treatment": "t4_3", - "size": 0 - }, { - "treatment": "t5_3", - "size": 0 - }, { - "treatment": "t6_3", - "size": 0 - }], - "label": "atribute exactly matches[papapa]" - }, { - "conditionType": "ROLLOUT", - "matcherGroup": { - "combiner": "AND", - "matchers": [{ - "keySelector": { - "trafficType": "custom", - "attribute": null - }, - "matcherType": "ALL_KEYS", - "negate": false, - "userDefinedSegmentMatcherData": null, - "whitelistMatcherData": null, - "unaryNumericMatcherData": null, - "betweenMatcherData": null, - "booleanMatcherData": null, - "dependencyMatcherData": null, - "stringMatcherData": null - }] - }, - "partitions": [{ - "treatment": "t1_3", - "size": 0 - }, { - "treatment": "t2_3", - "size": 0 - }, { - "treatment": "t3_3", - "size": 0 - }, { - "treatment": "t4_3", - "size": 100 - }, { - "treatment": "t5_3", - "size": 0 - }, { - "treatment": "t6_3", - "size": 0 - }], - "label": "default rule" - }] - }, { - "trafficTypeName": "custom", - "name": "SAMPLE_FEATURE4", - "trafficAllocation": 100, - "trafficAllocationSeed": 1595297106, - "seed": -1332540447, - "status": "ACTIVE", - "killed": false, - "defaultTreatment": "off", - "changeNumber": 1, - "algo": 2, - "configurations": { - "t1": "{\"f1\": \"v1\"}", - "t3": "{\"f3\": \"v3\"}", - "t4": "{\"f4\": \"v4\"}", - "t2": "{\"f2\": \"v2\"}" - }, - "conditions": [{ - "conditionType": "ROLLOUT", - "matcherGroup": { - "combiner": "AND", - "matchers": [{ - "keySelector": { - "trafficType": "custom", - "attribute": "atributoNumero" - }, - "matcherType": "BETWEEN", - "negate": false, - "userDefinedSegmentMatcherData": null, - "whitelistMatcherData": null, - "unaryNumericMatcherData": null, - "betweenMatcherData": { - "dataType": "NUMBER", - "start": 90, - "end": 500 - }, - "booleanMatcherData": null, - "dependencyMatcherData": null, - "stringMatcherData": null - }] - }, - "partitions": [{ - "treatment": "t1_4", - "size": 0 - }, { - "treatment": "t2_4", - "size": 0 - }, { - "treatment": "t3_4", - "size": 0 - }, { - "treatment": "t4_4", - "size": 100 - }, { - "treatment": "t5_4", - "size": 0 - }, { - "treatment": "t6_4", - "size": 0 - }], - "label": "atributoNumero between 90 and 500" - }, { - "conditionType": "ROLLOUT", - "matcherGroup": { - "combiner": "AND", - "matchers": [{ - "keySelector": { - "trafficType": "custom", - "attribute": "atributo2" - }, - "matcherType": "EQUAL_TO_SET", - "negate": false, - "userDefinedSegmentMatcherData": null, - "whitelistMatcherData": { - "whitelist": ["salamin"] - }, - "unaryNumericMatcherData": null, - "betweenMatcherData": null, - "booleanMatcherData": null, - "dependencyMatcherData": null, - "stringMatcherData": null - }] - }, - "partitions": [{ - "treatment": "t1_4", - "size": 100 - }, { - "treatment": "t2_4", - "size": 0 - }, { - "treatment": "t3_4", - "size": 0 - }, { - "treatment": "t4_4", - "size": 100 - }, { - "treatment": "t5_4", - "size": 0 - }, { - "treatment": "t6_4", - "size": 0 - }], - "label": "atributo2 exactly matches [salamin]" - }, { - "conditionType": "ROLLOUT", - "matcherGroup": { - "combiner": "AND", - "matchers": [{ - "keySelector": { - "trafficType": "custom", - "attribute": "atributo1" - }, - "matcherType": "MATCHES_STRING", - "negate": false, - "userDefinedSegmentMatcherData": null, - "whitelistMatcherData": null, - "unaryNumericMatcherData": null, - "betweenMatcherData": null, - "booleanMatcherData": null, - "dependencyMatcherData": null, - "stringMatcherData": "mila" - }] - }, - "partitions": [{ - "treatment": "t1_4", - "size": 0 - }, { - "treatment": "t2_4", - "size": 0 - }, { - "treatment": "t3_4", - "size": 100 - }, { - "treatment": "t4_4", - "size": 0 - }, { - "treatment": "t5_4", - "size": 0 - }, { - "treatment": "t6_4", - "size": 0 - }], - "label": "atributo1 matches mila" - }, { - "conditionType": "ROLLOUT", - "matcherGroup": { - "combiner": "AND", - "matchers": [{ - "keySelector": { - "trafficType": "custom", - "attribute": "atribute" - }, - "matcherType": "EQUAL_TO_SET", - "negate": false, - "userDefinedSegmentMatcherData": null, - "whitelistMatcherData": { - "whitelist": ["papapa"] - }, - "unaryNumericMatcherData": null, - "betweenMatcherData": null, - "booleanMatcherData": null, - "dependencyMatcherData": null, - "stringMatcherData": null - }] - }, - "partitions": [{ - "treatment": "t1_4", - "size": 0 - }, { - "treatment": "t2_4", - "size": 100 - }, { - "treatment": "t3_4", - "size": 0 - }, { - "treatment": "t4_4", - "size": 0 - }, { - "treatment": "t5_4", - "size": 0 - }, { - "treatment": "t6_4", - "size": 0 - }], - "label": "atribute exactly matches[papapa]" - }, { - "conditionType": "ROLLOUT", - "matcherGroup": { - "combiner": "AND", - "matchers": [{ - "keySelector": { - "trafficType": "custom", - "attribute": null - }, - "matcherType": "ALL_KEYS", - "negate": false, - "userDefinedSegmentMatcherData": null, - "whitelistMatcherData": null, - "unaryNumericMatcherData": null, - "betweenMatcherData": null, - "booleanMatcherData": null, - "dependencyMatcherData": null, - "stringMatcherData": null - }] - }, - "partitions": [{ - "treatment": "t1_4", - "size": 0 - }, { - "treatment": "t2_4", - "size": 0 - }, { - "treatment": "t3_4", - "size": 0 - }, { - "treatment": "t4_4", - "size": 100 - }, { - "treatment": "t5_4", - "size": 0 - }, { - "treatment": "t6_4", - "size": 0 - }], - "label": "default rule" - }] - }, { - "trafficTypeName": "custom", - "name": "SAMPLE_FEATURE5", - "trafficAllocation": 100, - "trafficAllocationSeed": 1595297106, - "seed": -1332540447, - "status": "ACTIVE", - "killed": false, - "defaultTreatment": "off", - "changeNumber": 1, - "algo": 2, - "conditions": [{ - "conditionType": "ROLLOUT", - "matcherGroup": { - "combiner": "AND", - "matchers": [{ - "keySelector": { - "trafficType": "custom", - "attribute": "atributoNumero" - }, - "matcherType": "BETWEEN", - "negate": false, - "userDefinedSegmentMatcherData": null, - "whitelistMatcherData": null, - "unaryNumericMatcherData": null, - "betweenMatcherData": { - "dataType": "NUMBER", - "start": 90, - "end": 500 - }, - "booleanMatcherData": null, - "dependencyMatcherData": null, - "stringMatcherData": null - }] - }, - "partitions": [{ - "treatment": "t1_5", - "size": 100 - }, { - "treatment": "t2_5", - "size": 0 - }, { - "treatment": "t3_5", - "size": 0 - }, { - "treatment": "t4_5", - "size": 0 - }, { - "treatment": "t5_5", - "size": 0 - }, { - "treatment": "t6_5", - "size": 0 - }], - "label": "atributoNumero between 3 and 30" - }, { - "conditionType": "ROLLOUT", - "matcherGroup": { - "combiner": "AND", - "matchers": [{ - "keySelector": { - "trafficType": "custom", - "attribute": "atributo2" - }, - "matcherType": "EQUAL_TO_SET", - "negate": false, - "userDefinedSegmentMatcherData": null, - "whitelistMatcherData": { - "whitelist": ["salamin"] - }, - "unaryNumericMatcherData": null, - "betweenMatcherData": null, - "booleanMatcherData": null, - "dependencyMatcherData": null, - "stringMatcherData": null - }] - }, - "partitions": [{ - "treatment": "t1_5", - "size": 0 - }, { - "treatment": "t2_5", - "size": 0 - }, { - "treatment": "t3_5", - "size": 0 - }, { - "treatment": "t4_5", - "size": 100 - }, { - "treatment": "t5_5", - "size": 0 - }, { - "treatment": "t6_5", - "size": 0 - }], - "label": "atributo2 exactly matches [salamin]" - }, { - "conditionType": "ROLLOUT", - "matcherGroup": { - "combiner": "AND", - "matchers": [{ - "keySelector": { - "trafficType": "custom", - "attribute": "atributo1" - }, - "matcherType": "MATCHES_STRING", - "negate": false, - "userDefinedSegmentMatcherData": null, - "whitelistMatcherData": null, - "unaryNumericMatcherData": null, - "betweenMatcherData": null, - "booleanMatcherData": null, - "dependencyMatcherData": null, - "stringMatcherData": "mila" - }] - }, - "partitions": [{ - "treatment": "t1_5", - "size": 0 - }, { - "treatment": "t2_5", - "size": 0 - }, { - "treatment": "t3_5", - "size": 0 - }, { - "treatment": "t4_5", - "size": 0 - }, { - "treatment": "t5_5", - "size": 100 - }, { - "treatment": "t6_5", - "size": 0 - }], - "label": "atributo1 matches mila" - }, { - "conditionType": "ROLLOUT", - "matcherGroup": { - "combiner": "AND", - "matchers": [{ - "keySelector": { - "trafficType": "custom", - "attribute": "atribute" - }, - "matcherType": "EQUAL_TO_SET", - "negate": false, - "userDefinedSegmentMatcherData": null, - "whitelistMatcherData": { - "whitelist": ["papapa"] - }, - "unaryNumericMatcherData": null, - "betweenMatcherData": null, - "booleanMatcherData": null, - "dependencyMatcherData": null, - "stringMatcherData": null - }] - }, - "partitions": [{ - "treatment": "t1_5", - "size": 0 - }, { - "treatment": "t2_5", - "size": 0 - }, { - "treatment": "t3_5", - "size": 100 - }, { - "treatment": "t4_5", - "size": 0 - }, { - "treatment": "t5_5", - "size": 0 - }, { - "treatment": "t6_5", - "size": 0 - }], - "label": "atribute exactly matches[papapa]" - }, { - "conditionType": "ROLLOUT", - "matcherGroup": { - "combiner": "AND", - "matchers": [{ - "keySelector": { - "trafficType": "custom", - "attribute": null - }, - "matcherType": "ALL_KEYS", - "negate": false, - "userDefinedSegmentMatcherData": null, - "whitelistMatcherData": null, - "unaryNumericMatcherData": null, - "betweenMatcherData": null, - "booleanMatcherData": null, - "dependencyMatcherData": null, - "stringMatcherData": null - }] - }, - "partitions": [{ - "treatment": "t1_5", - "size": 0 - }, { - "treatment": "t2_5", - "size": 0 - }, { - "treatment": "t3_5", - "size": 0 - }, { - "treatment": "t4_5", - "size": 0 - }, { - "treatment": "t5_5", - "size": 0 - }, { - "treatment": "t6_5", - "size": 100 - }], - "label": "default rule" - }] - } + "matcherType": "ENDS_WITH", + "negate": false, + "whitelistMatcherData": { + "whitelist": [ + "@split.io" + ] + } + } + ] + } + } + ] + } ] +}}] From ac821b5105bd6fba84af088b90e40b879007c588 Mon Sep 17 00:00:00 2001 From: Gaston Thea Date: Fri, 30 May 2025 12:37:20 -0300 Subject: [PATCH 38/46] Return full TargetingRules in test mock --- .../Api/SplitIntegrationTest.swift | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/SplitTests/Integration/Api/SplitIntegrationTest.swift b/SplitTests/Integration/Api/SplitIntegrationTest.swift index 70b54cfd8..bba8a9776 100644 --- a/SplitTests/Integration/Api/SplitIntegrationTest.swift +++ b/SplitTests/Integration/Api/SplitIntegrationTest.swift @@ -16,7 +16,7 @@ class SplitIntegrationTests: XCTestCase { let matchingKey = "CUSTOMER_ID" let trafficType = "account" let kNeverRefreshRate = 9999999 - var splitChange: SplitChange? + var splitChange: TargetingRulesChange? var serverUrl = "localhost" var trackReqIndex = 0 var largeSegmentsError = false @@ -54,9 +54,7 @@ class SplitIntegrationTests: XCTestCase { private func buildTestDispatcher() -> HttpClientTestDispatcher { return { request in if request.isSplitEndpoint() { - return TestDispatcherResponse(code: 200, data: try? Json.encodeToJsonData( - TargetingRulesChange(featureFlags: self.splitChange!, ruleBasedSegments: RuleBasedSegmentChange(segments: [], since: -1, till: -1 - )))) + return TestDispatcherResponse(code: 200, data: try? Json.encodeToJsonData(self.splitChange)) } if request.isMySegmentsEndpoint() { @@ -222,12 +220,12 @@ class SplitIntegrationTests: XCTestCase { wait(for: [sdkReadyExpectation], timeout: 5) // Ensures healthy JSON data, tests SplitView and default treatment - XCTAssertEqual(client?.getTreatment("always_on_if_prerequisite"), "off") + XCTAssertEqual(client?.getTreatment("always_on_if_prerequisite"), "on") XCTAssertEqual(manager?.split(featureName: "always_on_if_prerequisite")!.prerequisites![0].flagName, "rbs_test_flag") XCTAssertEqual(manager?.split(featureName: "always_on_if_prerequisite")!.prerequisites![0].treatments[0], "v1") // Tests Prerequisites Filtering - XCTAssertEqual(client?.getTreatment("always_on_if_prerequisite"), "off") + XCTAssertEqual(client?.getTreatment("always_on_if_prerequisite"), "on") } func testImpressionsCount() throws { @@ -365,16 +363,18 @@ class SplitIntegrationTests: XCTestCase { } - private func loadSplitsChangeFile() -> SplitChange? { + private func loadSplitsChangeFile() -> TargetingRulesChange? { let change = loadSplitChangeFile(name: "splitchanges_1") - change?.since = change?.till ?? -1 + change?.featureFlags.since = change?.featureFlags.till ?? -1 + change?.ruleBasedSegments.since = change?.ruleBasedSegments.till ?? -1 + return change } - private func loadSplitChangeFile(name fileName: String) -> SplitChange? { + private func loadSplitChangeFile(name fileName: String) -> TargetingRulesChange? { if let file = FileHelper.readDataFromFile(sourceClass: self, name: fileName, type: "json"), let change = try? Json.decodeFrom(json: file, to: TargetingRulesChange.self) { - return change.featureFlags + return change } return nil } From b10ec716fd28c0ae844f8953aa4c022047192b53 Mon Sep 17 00:00:00 2001 From: Martin Cardozo Date: Fri, 30 May 2025 12:53:34 -0300 Subject: [PATCH 39/46] Tests added --- SplitTests/Integration/Api/DestroyTest.swift | 4 +- .../Api/SplitIntegrationTest.swift | 79 +++++++++++++++++-- 2 files changed, 73 insertions(+), 10 deletions(-) diff --git a/SplitTests/Integration/Api/DestroyTest.swift b/SplitTests/Integration/Api/DestroyTest.swift index ee6942ec8..dcc70f091 100644 --- a/SplitTests/Integration/Api/DestroyTest.swift +++ b/SplitTests/Integration/Api/DestroyTest.swift @@ -232,8 +232,8 @@ class DestroyTests: XCTestCase { XCTAssertEqual("off", treatmentsWithConfigBeforeDestroy?[splitName]?.treatment) XCTAssertEqual(true, trackBeforeDestroy) XCTAssertEqual(splitName, splitBeforeDestroy?.name) - XCTAssertEqual(33, splitCountBeforeDestroy) - XCTAssertEqual(33, splitNamesCountBeforeDestroy) + XCTAssertEqual(35, splitCountBeforeDestroy) + XCTAssertEqual(35, splitNamesCountBeforeDestroy) XCTAssertEqual(SplitConstants.control, treatmentAfterDestroy) XCTAssertEqual(SplitConstants.control, treatmentWithConfigAfterDestroy?.treatment) diff --git a/SplitTests/Integration/Api/SplitIntegrationTest.swift b/SplitTests/Integration/Api/SplitIntegrationTest.swift index bba8a9776..4891d1bd2 100644 --- a/SplitTests/Integration/Api/SplitIntegrationTest.swift +++ b/SplitTests/Integration/Api/SplitIntegrationTest.swift @@ -170,7 +170,7 @@ class SplitIntegrationTests: XCTestCase { XCTAssertEqual(SplitConstants.control, ts1?["NO_EXISTING_FEATURE1"]) XCTAssertEqual(SplitConstants.control, ts1?["NO_EXISTING_FEATURE2"]) - XCTAssertEqual(33, splits?.count) + XCTAssertEqual(35, splits?.count) XCTAssertNotNil(s1) XCTAssertNil(s2) XCTAssertNotNil(i1) @@ -192,8 +192,9 @@ class SplitIntegrationTests: XCTestCase { factory = nil } - func testPrerequisitesTreatment() throws { + func testPrerequisitesSplitView() throws { + // Setup let splitConfig: SplitClientConfig = SplitClientConfig() splitConfig.featuresRefreshRate = 30 splitConfig.segmentsRefreshRate = 30 @@ -209,23 +210,85 @@ class SplitIntegrationTests: XCTestCase { splitConfig.serviceEndpoints = ServiceEndpoints.builder() .set(sdkEndpoint: serverUrl).set(eventsEndpoint: serverUrl).build() - let key: Key = Key(matchingKey: "bilal@split.io", bucketingKey: nil) + let key: Key = Key(matchingKey: "mauro@split.io", bucketingKey: nil) let factory = DefaultSplitFactoryBuilder().setHttpClient(httpClient).setApiKey(apiKey).setKey(key).setConfig(splitConfig).build() let client = factory?.client let manager = factory?.manager - // SDK Ready + // Wait for SDK Ready.. let sdkReadyExpectation = XCTestExpectation(description: "SDK READY Expectation") client?.on(event: SplitEvent.sdkReady) { sdkReadyExpectation.fulfill() } wait(for: [sdkReadyExpectation], timeout: 5) - // Ensures healthy JSON data, tests SplitView and default treatment - XCTAssertEqual(client?.getTreatment("always_on_if_prerequisite"), "on") + // Test XCTAssertEqual(manager?.split(featureName: "always_on_if_prerequisite")!.prerequisites![0].flagName, "rbs_test_flag") XCTAssertEqual(manager?.split(featureName: "always_on_if_prerequisite")!.prerequisites![0].treatments[0], "v1") + } + + func testPrerequisitesTreatmentPass() throws { + + let user = "bilal@split.io" // User IN segment + + // Setup + let splitConfig: SplitClientConfig = SplitClientConfig() + splitConfig.featuresRefreshRate = 30 + splitConfig.segmentsRefreshRate = 30 + splitConfig.impressionRefreshRate = 30 + splitConfig.sdkReadyTimeOut = 60000 + splitConfig.trafficType = trafficType + splitConfig.eventsPerPush = 10 + splitConfig.eventsQueueSize = 100 + splitConfig.eventsPushRate = 999999 + splitConfig.eventsFirstPushWindow = 999 + splitConfig.logLevel = TestingHelper.testLogLevel + splitConfig.impressionsMode = "DEBUG" + splitConfig.serviceEndpoints = ServiceEndpoints.builder() + .set(sdkEndpoint: serverUrl).set(eventsEndpoint: serverUrl).build() + + let key: Key = Key(matchingKey: user, bucketingKey: nil) + let factory = DefaultSplitFactoryBuilder().setHttpClient(httpClient).setApiKey(apiKey).setKey(key).setConfig(splitConfig).build() + let client = factory?.client + + // Wait for SDK Ready.. + let sdkReadyExpectation = XCTestExpectation(description: "SDK READY Expectation") + client?.on(event: SplitEvent.sdkReady) { sdkReadyExpectation.fulfill() } + wait(for: [sdkReadyExpectation], timeout: 5) + + // Test + XCTAssertEqual(client?.getTreatment("always_on_if_prerequisite"), "on", "'\(user)' is part of the segment in the JSON, so it met the prerequisite, therefore it should return 'on'") + } + + func testPrerequisitesTreatmentNotPass() throws { + + let user = "mauro@split.io" // User EXCLUDED from segment + + // Setup + let splitConfig: SplitClientConfig = SplitClientConfig() + splitConfig.featuresRefreshRate = 30 + splitConfig.segmentsRefreshRate = 30 + splitConfig.impressionRefreshRate = 30 + splitConfig.sdkReadyTimeOut = 60000 + splitConfig.trafficType = trafficType + splitConfig.eventsPerPush = 10 + splitConfig.eventsQueueSize = 100 + splitConfig.eventsPushRate = 999999 + splitConfig.eventsFirstPushWindow = 999 + splitConfig.logLevel = TestingHelper.testLogLevel + splitConfig.impressionsMode = "DEBUG" + splitConfig.serviceEndpoints = ServiceEndpoints.builder() + .set(sdkEndpoint: serverUrl).set(eventsEndpoint: serverUrl).build() + + let key: Key = Key(matchingKey: user, bucketingKey: nil) + let factory = DefaultSplitFactoryBuilder().setHttpClient(httpClient).setApiKey(apiKey).setKey(key).setConfig(splitConfig).build() + let client = factory?.client + + // Wait for SDK Ready.. + let sdkReadyExpectation = XCTestExpectation(description: "SDK READY Expectation") + client?.on(event: SplitEvent.sdkReady) { sdkReadyExpectation.fulfill() } + wait(for: [sdkReadyExpectation], timeout: 5) - // Tests Prerequisites Filtering - XCTAssertEqual(client?.getTreatment("always_on_if_prerequisite"), "on") + // Test + XCTAssertEqual(client?.getTreatment("always_on_if_prerequisite"), "off", "'\(user)' is excluded from the segment in the JSON, so it does not met the prerequisite, and should return 'off'") } func testImpressionsCount() throws { From 5e749204473545b4fe03edfc6e26bec83bbf9899 Mon Sep 17 00:00:00 2001 From: Martin Cardozo Date: Fri, 30 May 2025 12:57:32 -0300 Subject: [PATCH 40/46] Deleting unnecessary file --- Split.xcodeproj/project.pbxproj | 4 - SplitTests/EvaluatorTests.swift | 15 -- .../Resources/split_prerequisites_test.json | 166 ------------------ 3 files changed, 185 deletions(-) delete mode 100644 SplitTests/Resources/split_prerequisites_test.json diff --git a/Split.xcodeproj/project.pbxproj b/Split.xcodeproj/project.pbxproj index 94d940988..444269d41 100644 --- a/Split.xcodeproj/project.pbxproj +++ b/Split.xcodeproj/project.pbxproj @@ -353,7 +353,6 @@ 59FB7C35220329B900ECC96A /* SplitFactoryBuilderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 59FB7C34220329B900ECC96A /* SplitFactoryBuilderTests.swift */; }; 59FB7C3C2203795F00ECC96A /* LocalhostSplitsParser.swift in Sources */ = {isa = PBXBuildFile; fileRef = 59FB7C3B2203795F00ECC96A /* LocalhostSplitsParser.swift */; }; 59FB7C3E22037B9400ECC96A /* SpaceDelimitedLocalhostSplitsParser.swift in Sources */ = {isa = PBXBuildFile; fileRef = 59FB7C3D22037B9400ECC96A /* SpaceDelimitedLocalhostSplitsParser.swift */; }; - 5B64AB8E2DE8BEE000B29864 /* split_prerequisites_test.json in Resources */ = {isa = PBXBuildFile; fileRef = 5B64AB8D2DE8BED700B29864 /* split_prerequisites_test.json */; }; 5B91B8392DDE4A3B000510F0 /* SplitDTOTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B91B8382DDE4A30000510F0 /* SplitDTOTests.swift */; }; 5BF52DF72DE0B60700FEDAFE /* PrerequisitesMatcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BF52DF52DE0B60300FEDAFE /* PrerequisitesMatcher.swift */; }; 5BF52DF92DE4B8D400FEDAFE /* PrerequisitesMatcherTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BF52DF82DE4B8CA00FEDAFE /* PrerequisitesMatcherTest.swift */; }; @@ -1557,7 +1556,6 @@ 59FB7C34220329B900ECC96A /* SplitFactoryBuilderTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SplitFactoryBuilderTests.swift; sourceTree = ""; }; 59FB7C3B2203795F00ECC96A /* LocalhostSplitsParser.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocalhostSplitsParser.swift; sourceTree = ""; }; 59FB7C3D22037B9400ECC96A /* SpaceDelimitedLocalhostSplitsParser.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SpaceDelimitedLocalhostSplitsParser.swift; sourceTree = ""; }; - 5B64AB8D2DE8BED700B29864 /* split_prerequisites_test.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = split_prerequisites_test.json; sourceTree = ""; }; 5B91B8382DDE4A30000510F0 /* SplitDTOTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SplitDTOTests.swift; sourceTree = ""; }; 5BF52DF52DE0B60300FEDAFE /* PrerequisitesMatcher.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PrerequisitesMatcher.swift; sourceTree = ""; }; 5BF52DF82DE4B8CA00FEDAFE /* PrerequisitesMatcherTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PrerequisitesMatcherTest.swift; sourceTree = ""; }; @@ -2940,7 +2938,6 @@ 592C6ADB211CBB86002D120C /* Resources */ = { isa = PBXGroup; children = ( - 5B64AB8D2DE8BED700B29864 /* split_prerequisites_test.json */, C5A7D5562DDBD4280081D190 /* split_changes_rbs.json */, 95F7BBD32C1A273900C5F2E4 /* Cert */, C5977C052BF273A3003E293A /* between_semver.csv */, @@ -3977,7 +3974,6 @@ 95F7BBDB2C1A27C200C5F2E4 /* rsa_4096_public_01.pem in Resources */, 5982D92F219C83D000230F44 /* legacy_1_short.csv in Resources */, 95DF58FA2BEE5140009220B8 /* SplitiOSUnit_4.xctestplan in Resources */, - 5B64AB8E2DE8BEE000B29864 /* split_prerequisites_test.json in Resources */, 599EDAF32270A15B00D7DACB /* localhost.splits in Resources */, 590DF9D1213F07400082B94F /* split_sample_feature6.json in Resources */, 95F7BBEF2C1B413500C5F2E4 /* ec_apple_public_key.der in Resources */, diff --git a/SplitTests/EvaluatorTests.swift b/SplitTests/EvaluatorTests.swift index 0c48cad14..b3c3a11e6 100644 --- a/SplitTests/EvaluatorTests.swift +++ b/SplitTests/EvaluatorTests.swift @@ -369,21 +369,6 @@ class EvaluatorTests: XCTestCase { treatment = result!.treatment XCTAssertEqual(treatment, "on", "Result should be 'on'") } - - func testNewFlag() { - var result: EvaluationResult! - var evaluator: Evaluator! - guard let split = loadSplit(splitName: "split_prerequisites_test") else { - XCTFail("Test flag not found"); return - } - -// evaluator = customEvaluator(split: split) -// result = try? evaluator.evalTreatment(matchingKey: matchingKey, bucketingKey: nil, splitName: split.name!, attributes: nil) -// XCTAssertNotNil(result) -// XCTAssertEqual("t4_6", result?.treatment) -// XCTAssertNotNil(result?.configuration) -// XCTAssertEqual("default rule", result?.label) - } func testInLargeSegmentWhitelist() { inLargeSegmentWhiteListTest(key: matchingKey) diff --git a/SplitTests/Resources/split_prerequisites_test.json b/SplitTests/Resources/split_prerequisites_test.json deleted file mode 100644 index 5c2464289..000000000 --- a/SplitTests/Resources/split_prerequisites_test.json +++ /dev/null @@ -1,166 +0,0 @@ -{ - "ff": { - "s": -1, - "t": 100, - "d": [ - { - "name": "always_on_if_prerequisite", - "trafficTypeName": "user", - "trafficAllocation": 100, - "trafficAllocationSeed": 1828377380, - "changeNumber": 5, - "seed": -790401604, - "status": "ACTIVE", - "killed": false, - "defaultTreatment": "off", - "prerequisites": [ - { - "n": "rbs_test_flag", - "ts": [ - "v1" - ] - } - ], - "conditions": [ - { - "conditionType": "ROLLOUT", - "matcherGroup": { - "combiner": "AND", - "matchers": [ - { - "keySelector": { - "trafficType": "user", - "attribute": null - }, - "matcherType": "ALL_KEYS", - "negate": false, - "userDefinedSegmentMatcherData": null, - "whitelistMatcherData": null, - "unaryNumericMatcherData": null, - "betweenMatcherData": null - } - ] - }, - "partitions": [ - { - "treatment": "on", - "size": 100 - } - ], - "label": "always_on_if_prerequisite label" - } - ] - }, - { - "name": "rbs_test_flag", - "trafficTypeName": "user", - "trafficAllocation": 100, - "trafficAllocationSeed": 1828377380, - "seed": -286617921, - "status": "ACTIVE", - "killed": false, - "defaultTreatment": "off", - "algo": 2, - "conditions": [ - { - "conditionType": "ROLLOUT", - "matcherGroup": { - "combiner": "AND", - "matchers": [ - { - "keySelector": { - "trafficType": "user" - }, - "matcherType": "IN_RULE_BASED_SEGMENT", - "negate": false, - "userDefinedSegmentMatcherData": { - "segmentName": "test_rule_based_segment" - } - } - ] - }, - "partitions": [ - { - "treatment": "v1", - "size": 100 - }, - { - "treatment": "v2", - "size": 0 - } - ], - "label": "in rule based segment test_rule_based_segment" - }, - { - "conditionType": "ROLLOUT", - "matcherGroup": { - "combiner": "AND", - "matchers": [ - { - "keySelector": { - "trafficType": "user" - }, - "matcherType": "ALL_KEYS", - "negate": false - } - ] - }, - "partitions": [ - { - "treatment": "v1", - "size": 0 - }, - { - "treatment": "v2", - "size": 100 - } - ], - "label": "default rule" - } - ], - "configurations": {}, - "sets": [], - "impressionsDisabled": false - } - ] - }, - "rbs": { - "s": -1, - "t": 100, - "d": [ - { - "name": "test_rule_based_segment", - "status": "ACTIVE", - "trafficTypeName": "user", - "excluded": { - "keys": [ - "mauro@split.io", - "gaston@split.io" - ], - "segments": [] - }, - "conditions": [ - { - "matcherGroup": { - "combiner": "AND", - "matchers": [ - { - "keySelector": { - "trafficType": "user" - }, - "matcherType": "ENDS_WITH", - "negate": false, - "whitelistMatcherData": { - "whitelist": [ - "@split.io" - ] - } - } - ] - } - } - ] - } - ] - } -} From fd5c5e1b94df3d0ea9ab9bab856f840fc3360ea9 Mon Sep 17 00:00:00 2001 From: Martin Cardozo Date: Fri, 30 May 2025 13:41:36 -0300 Subject: [PATCH 41/46] Adapting to GitHub actions --- Split/Engine/Evaluator.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Split/Engine/Evaluator.swift b/Split/Engine/Evaluator.swift index 66dc48943..569a56e12 100644 --- a/Split/Engine/Evaluator.swift +++ b/Split/Engine/Evaluator.swift @@ -110,7 +110,7 @@ class DefaultEvaluator: Evaluator { } private func selectBucketKey(matchingKey: String, bucketingKey: String?) -> String { - if let bucketingKey = bucketingKey, !bucketingKey.isEmpty { return bucketingKey } + if let key = bucketingKey, !key.isEmpty { return key } return matchingKey } From 425cf5354524656a11931766ce7267d7da193cd9 Mon Sep 17 00:00:00 2001 From: Martin Cardozo Date: Fri, 30 May 2025 13:44:18 -0300 Subject: [PATCH 42/46] Solving conflict --- Split/Engine/Evaluator.swift | 2 -- 1 file changed, 2 deletions(-) diff --git a/Split/Engine/Evaluator.swift b/Split/Engine/Evaluator.swift index 6d1119e2b..f93478af7 100644 --- a/Split/Engine/Evaluator.swift +++ b/Split/Engine/Evaluator.swift @@ -110,8 +110,6 @@ class DefaultEvaluator: Evaluator { splitAlgo = algo } - let bucketKey = selectBucketKey(matchingKey: matchingKey, bucketingKey: bucketingKey) - guard let conditions: [Condition] = split.conditions, let trafficAllocationSeed = split.trafficAllocationSeed, let seed = split.seed else { From 4b979ff98ebff3a73762d82eaadb24712dfdb967 Mon Sep 17 00:00:00 2001 From: Martin Cardozo Date: Fri, 30 May 2025 15:10:14 -0300 Subject: [PATCH 43/46] Wrong label corrected. Test adapted --- Split/Engine/Evaluator.swift | 2 +- SplitTests/SplitManagerTest.swift | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/Split/Engine/Evaluator.swift b/Split/Engine/Evaluator.swift index f93478af7..8d2098103 100644 --- a/Split/Engine/Evaluator.swift +++ b/Split/Engine/Evaluator.swift @@ -97,7 +97,7 @@ class DefaultEvaluator: Evaluator { let matcher = prerequisitesMatcherFactory(split.prerequisites ?? []) if !matcher.evaluate(values: values, context: getContext()) { return EvaluationResult(treatment: defaultTreatment, - label: ImpressionsConstants.killed, + label: ImpressionsConstants.prerequisitesNotMet, changeNumber: changeNumber, configuration: split.configurations?[defaultTreatment], impressionsDisabled: split.isImpressionsDisabled()) diff --git a/SplitTests/SplitManagerTest.swift b/SplitTests/SplitManagerTest.swift index ff3fa6d74..c78364105 100644 --- a/SplitTests/SplitManagerTest.swift +++ b/SplitTests/SplitManagerTest.swift @@ -36,7 +36,10 @@ class SplitManagerTest: XCTestCase { for i in 0...5 { expectedSplitNames.append("sample_feature\(i)") } - XCTAssertEqual(splits.count, 6, "Split count should be 6") + expectedSplitNames.append("always_on_if_prerequisite") + expectedSplitNames.append("rbs_test_flag") + + XCTAssertEqual(splits.count, 8, "Split count should be 8") XCTAssertEqual(names.sorted().joined(separator: ",").lowercased(), expectedSplitNames.joined(separator: ","), "Loaded splits names are not correct") let splitLowercase = manager.split(featureName: "sample_feature0") From 302abed4d4fa1727ad17031978baf5a5679f8c07 Mon Sep 17 00:00:00 2001 From: Martin Cardozo Date: Fri, 30 May 2025 15:14:32 -0300 Subject: [PATCH 44/46] Adding Matcher to WatchOS target --- Split.xcodeproj/project.pbxproj | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Split.xcodeproj/project.pbxproj b/Split.xcodeproj/project.pbxproj index 444269d41..3994189dc 100644 --- a/Split.xcodeproj/project.pbxproj +++ b/Split.xcodeproj/project.pbxproj @@ -353,6 +353,7 @@ 59FB7C35220329B900ECC96A /* SplitFactoryBuilderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 59FB7C34220329B900ECC96A /* SplitFactoryBuilderTests.swift */; }; 59FB7C3C2203795F00ECC96A /* LocalhostSplitsParser.swift in Sources */ = {isa = PBXBuildFile; fileRef = 59FB7C3B2203795F00ECC96A /* LocalhostSplitsParser.swift */; }; 59FB7C3E22037B9400ECC96A /* SpaceDelimitedLocalhostSplitsParser.swift in Sources */ = {isa = PBXBuildFile; fileRef = 59FB7C3D22037B9400ECC96A /* SpaceDelimitedLocalhostSplitsParser.swift */; }; + 5B48D8172DEA2CED00351925 /* PrerequisitesMatcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BF52DF52DE0B60300FEDAFE /* PrerequisitesMatcher.swift */; }; 5B91B8392DDE4A3B000510F0 /* SplitDTOTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B91B8382DDE4A30000510F0 /* SplitDTOTests.swift */; }; 5BF52DF72DE0B60700FEDAFE /* PrerequisitesMatcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BF52DF52DE0B60300FEDAFE /* PrerequisitesMatcher.swift */; }; 5BF52DF92DE4B8D400FEDAFE /* PrerequisitesMatcherTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BF52DF82DE4B8CA00FEDAFE /* PrerequisitesMatcherTest.swift */; }; @@ -4835,6 +4836,7 @@ 95B02CC628D0BDC10030EC8B /* CompressionUtil.swift in Sources */, 95B02CC728D0BDC10030EC8B /* Stopwatch.swift in Sources */, 95B02CC828D0BDC10030EC8B /* ConcurrentArrayQueue.swift in Sources */, + 5B48D8172DEA2CED00351925 /* PrerequisitesMatcher.swift in Sources */, 95B02CC928D0BDC10030EC8B /* SynchronizedList.swift in Sources */, 95B02CCA28D0BDC10030EC8B /* ConcurrentSet.swift in Sources */, C539CADC2D93229D0050C732 /* EvaluationOptions.swift in Sources */, From 3159187316e606dc6e2edea2fc8ce99cff9d2a24 Mon Sep 17 00:00:00 2001 From: Martin Cardozo Date: Fri, 30 May 2025 17:05:19 -0300 Subject: [PATCH 45/46] Test added --- .../Api/SplitIntegrationTest.swift | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/SplitTests/Integration/Api/SplitIntegrationTest.swift b/SplitTests/Integration/Api/SplitIntegrationTest.swift index 4891d1bd2..0b47bd5a5 100644 --- a/SplitTests/Integration/Api/SplitIntegrationTest.swift +++ b/SplitTests/Integration/Api/SplitIntegrationTest.swift @@ -192,6 +192,38 @@ class SplitIntegrationTests: XCTestCase { factory = nil } + func testPrerequisites() throws { + + // Setup + let splitConfig: SplitClientConfig = SplitClientConfig() + splitConfig.featuresRefreshRate = 30 + splitConfig.segmentsRefreshRate = 30 + splitConfig.impressionRefreshRate = 30 + splitConfig.sdkReadyTimeOut = 60000 + splitConfig.trafficType = trafficType + splitConfig.eventsPerPush = 10 + splitConfig.eventsQueueSize = 100 + splitConfig.eventsPushRate = 999999 + splitConfig.eventsFirstPushWindow = 999 + splitConfig.logLevel = TestingHelper.testLogLevel + splitConfig.impressionsMode = "DEBUG" + splitConfig.serviceEndpoints = ServiceEndpoints.builder() + .set(sdkEndpoint: serverUrl).set(eventsEndpoint: serverUrl).build() + + let key: Key = Key(matchingKey: "123", bucketingKey: nil) + let factory = DefaultSplitFactoryBuilder().setHttpClient(httpClient).setApiKey(apiKey).setKey(key).setConfig(splitConfig).build() + let client = factory?.client + let manager = factory?.manager + + // Wait for SDK Ready.. + let sdkReadyExpectation = XCTestExpectation(description: "SDK READY Expectation") + client?.on(event: SplitEvent.sdkReady) { sdkReadyExpectation.fulfill() } + wait(for: [sdkReadyExpectation], timeout: 5) + + // Test + XCTAssertEqual(client?.getTreatment("always_on_if_prerequisite"), "off") + } + func testPrerequisitesSplitView() throws { // Setup From 71fa7b1007b9a4a7ec1125df80aa99b4114a387b Mon Sep 17 00:00:00 2001 From: Martin Cardozo Date: Fri, 30 May 2025 17:08:58 -0300 Subject: [PATCH 46/46] Marks --- SplitTests/Integration/Api/SplitIntegrationTest.swift | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/SplitTests/Integration/Api/SplitIntegrationTest.swift b/SplitTests/Integration/Api/SplitIntegrationTest.swift index 0b47bd5a5..148cd5cd6 100644 --- a/SplitTests/Integration/Api/SplitIntegrationTest.swift +++ b/SplitTests/Integration/Api/SplitIntegrationTest.swift @@ -192,6 +192,7 @@ class SplitIntegrationTests: XCTestCase { factory = nil } + // MARK: Prerequisites func testPrerequisites() throws { // Setup @@ -323,6 +324,7 @@ class SplitIntegrationTests: XCTestCase { XCTAssertEqual(client?.getTreatment("always_on_if_prerequisite"), "off", "'\(user)' is excluded from the segment in the JSON, so it does not met the prerequisite, and should return 'off'") } + // MARK: Impressions func testImpressionsCount() throws { let splitConfig: SplitClientConfig = SplitClientConfig() @@ -407,6 +409,7 @@ class SplitIntegrationTests: XCTestCase { XCTAssertTrue(readyFired) } + // MARK: Segments func testReadyMyLargeSegmentsEnabledWaitMls() throws { readyMySegmentsEnabledTest(waitMls: true) } @@ -457,7 +460,7 @@ class SplitIntegrationTests: XCTestCase { XCTAssertTrue(timeOutFired == endpointError) } - + // MARK: Class Helpers private func loadSplitsChangeFile() -> TargetingRulesChange? { let change = loadSplitChangeFile(name: "splitchanges_1") change?.featureFlags.since = change?.featureFlags.till ?? -1