Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 2 additions & 14 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,18 +24,6 @@ jobs:
notifications:
email: false

- stage: 'Lint markdown files'
os: linux
language: generic
before_install: skip
install:
- npm i -g markdown-spellcheck
before_script:
- wget --quiet https://raw.githubusercontent.com/optimizely/mdspell-config/master/.spelling
script:
- mdspell -a -n -r --en-us '**/*.md'
after_success: skip

- stage: 'Trigger Integration Tests'
language: minimal
os: linux
Expand Down Expand Up @@ -107,7 +95,7 @@ jobs:
os: osx
osx_image: xcode11.3
env:
- VERSION=3.4.0
- VERSION=3.4.1
install:
# install hub
- wget https://github.com/github/hub/releases/download/v2.11.2/hub-darwin-amd64-2.11.2.tgz -O /tmp/hub-darwin-amd64-2.11.2.tgz && tar -xvf /tmp/hub-darwin-amd64-2.11.2.tgz -C /usr/local/opt && ln -s /usr/local/opt/hub-darwin-amd64-2.11.2/bin/hub /usr/local/bin/hub
Expand All @@ -124,7 +112,7 @@ jobs:
os: osx
osx_image: xcode11.3
env:
- VERSION=3.4.0
- VERSION=3.4.1
install:
# install hub
- wget https://github.com/github/hub/releases/download/v2.11.2/hub-darwin-amd64-2.11.2.tgz -O /tmp/hub-darwin-amd64-2.11.2.tgz && tar -xvf /tmp/hub-darwin-amd64-2.11.2.tgz -C /usr/local/opt && ln -s /usr/local/opt/hub-darwin-amd64-2.11.2/bin/hub /usr/local/bin/hub
Expand Down
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
# Optimizely Swift SDK Changelog

## 3.4.1
September 22, 2020

### Bug Fixes
* Fix a bucketing error at traffic allocation boundaries. ([#365](https://github.com/optimizely/swift-sdk/pull/365))
* Update DataStore directory on macOS. ([#355](https://github.com/optimizely/swift-sdk/pull/355))

## 3.4.0
July 9, 2020

Expand Down
170 changes: 98 additions & 72 deletions OptimizelySwiftSDK.xcodeproj/project.pbxproj

Large diffs are not rendered by default.

6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,22 +34,22 @@ Please note below that _\<platform\>_ is used to represent the platform on which
```
dependencies: [
.package(url: "https://github.com/optimizely/swift-sdk.git",
.upToNextMinor(from: “3.4.0”))
.upToNextMinor(from: “3.4.1”))
]
```

#### CocoaPods
1. Add the following lines to the _Podfile_:<pre>
```use_frameworks!```
```pod 'OptimizelySwiftSDK', '~> 3.4.0'```
```pod 'OptimizelySwiftSDK', '~> 3.4.1'```
</pre>

2. Run the following command: <pre>``` pod install ```</pre>

Further installation instructions for Cocoapods: https://guides.cocoapods.org/using/getting-started.html

#### Carthage
1. Add the following lines to the _Cartfile_:<pre>```github "optimizely/swift-sdk" ~> 3.4.0```</pre>
1. Add the following lines to the _Cartfile_:<pre>```github "optimizely/swift-sdk" ~> 3.4.1```</pre>

2. Run the following command:<pre>```carthage update```</pre>

Expand Down
2 changes: 1 addition & 1 deletion Sources/Implementation/Datastore/DataStoreFile.swift
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ public class DataStoreFile<T>: OPTDataStore where T: Codable {
self.async = async
dataStoreName = storeName
lock = DispatchQueue(label: storeName)
#if os(tvOS)
#if os(tvOS) || os(macOS)
let directory = FileManager.SearchPathDirectory.cachesDirectory
#else
let directory = FileManager.SearchPathDirectory.documentDirectory
Expand Down
24 changes: 14 additions & 10 deletions Sources/Implementation/DefaultBucketer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -79,10 +79,7 @@ class DefaultBucketer: OPTBucketer {
return nil
}

for trafficAllocation in group.trafficAllocation where bucketValue <= trafficAllocation.endOfRange {
let experimentId = trafficAllocation.entityId

// propagate errors and logs for unknown experiment
if let experimentId = allocateTraffic(trafficAllocation: group.trafficAllocation, bucketValue: bucketValue) {
if let experiment = config.getExperiment(id: experimentId) {
return experiment
} else {
Expand All @@ -93,7 +90,7 @@ class DefaultBucketer: OPTBucketer {

return nil
}

func bucketToVariation(experiment: Experiment, bucketingId: String) -> Variation? {
let hashId = makeHashIdFromBucketingId(bucketingId: bucketingId, entityId: experiment.id)
let bucketValue = generateBucketValue(bucketingId: hashId)
Expand All @@ -103,17 +100,24 @@ class DefaultBucketer: OPTBucketer {
logger.e(.experimentHasNoTrafficAllocation(experiment.key))
return nil
}

for trafficAllocation in experiment.trafficAllocation where bucketValue <= trafficAllocation.endOfRange {
let variationId = trafficAllocation.entityId

// propagate errors and logs for unknown variation

if let variationId = allocateTraffic(trafficAllocation: experiment.trafficAllocation, bucketValue: bucketValue) {
if let variation = experiment.getVariation(id: variationId) {
return variation
} else {
logger.e(.userBucketedIntoInvalidVariation(variationId))
return nil
}
} else {
return nil
}
}

func allocateTraffic(trafficAllocation: [TrafficAllocation], bucketValue: Int) -> String? {
for bucket in trafficAllocation {
if bucketValue < bucket.endOfRange {
return bucket.entityId
}
}

return nil
Expand Down
55 changes: 55 additions & 0 deletions Tests/OptimizelyTests-Common/BucketTests_Base.swift
Original file line number Diff line number Diff line change
Expand Up @@ -41,4 +41,59 @@ class BucketTests_Base: XCTestCase {
}
}

func testAllocateExperimentTraffic() {
var experimentData: [String: Any] { return
[
"status": "Running",
"id": "12345",
"key": "experimentA",
"layerId": "10420273888",
"trafficAllocation": [
[
"entityId": "1000",
"endOfRange": 0
],
[
"entityId": "1001",
"endOfRange": 3000
],
[
"entityId": "1002",
"endOfRange": 6000
]
],
"audienceIds": [],
"variations": [
[
"variables": [],
"id": "1000",
"key": "a"
],
[
"variables": [],
"id": "1001",
"key": "b"
],
[
"variables": [],
"id": "1002",
"key": "c"
]
],
"forcedVariations": [:]
]
}

let bucketer = DefaultBucketer()
let experiment: Experiment = try! OTUtils.model(from: experimentData)
let trafficAllocation = experiment.trafficAllocation

XCTAssert(bucketer.allocateTraffic(trafficAllocation: trafficAllocation, bucketValue: 0) == "1001")
XCTAssert(bucketer.allocateTraffic(trafficAllocation: trafficAllocation, bucketValue: 2999) == "1001")
XCTAssert(bucketer.allocateTraffic(trafficAllocation: trafficAllocation, bucketValue: 3000) == "1002")
XCTAssert(bucketer.allocateTraffic(trafficAllocation: trafficAllocation, bucketValue: 5999) == "1002")
XCTAssertNil(bucketer.allocateTraffic(trafficAllocation: trafficAllocation, bucketValue: 6000))
XCTAssertNil(bucketer.allocateTraffic(trafficAllocation: trafficAllocation, bucketValue: 7000))
}

}
22 changes: 17 additions & 5 deletions Tests/OptimizelyTests-Common/BucketTests_Others.swift
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,7 @@ extension BucketTests_Others {
extension BucketTests_Others {

func testBucketExperimentInMutexGroup() {
let optimizely = OTUtils.createOptimizely(datafileName: "BucketerTestsDatafile", clearUserProfileService: true)!
let optimizely = OTUtils.createOptimizely(datafileName: "bucketer_test", clearUserProfileService: true)!
let group = optimizely.config!.getGroup(id: "1886780721")!

let bucketer = DefaultBucketer()
Expand All @@ -200,7 +200,7 @@ extension BucketTests_Others {
}

func testBucketReturnsNilWhenExperimentIsExcludedFromMutex() {
let optimizely = OTUtils.createOptimizely(datafileName: "BucketerTestsDatafile", clearUserProfileService: true)!
let optimizely = OTUtils.createOptimizely(datafileName: "bucketer_test", clearUserProfileService: true)!
let config = optimizely.config!
let bucketer = DefaultBucketer()

Expand Down Expand Up @@ -237,7 +237,7 @@ extension BucketTests_Others {
}

func testBucketExperimentWithMutexDoesNotChangeExperimentReference() {
let optimizely = OTUtils.createOptimizely(datafileName: "BucketerTestsDatafile", clearUserProfileService: true)!
let optimizely = OTUtils.createOptimizely(datafileName: "bucketer_test", clearUserProfileService: true)!
let config = optimizely.config!
let bucketer = DefaultBucketer()

Expand All @@ -248,7 +248,7 @@ extension BucketTests_Others {
}

func testBucketWithBucketingId() {
let optimizely = OTUtils.createOptimizely(datafileName: "BucketerTestsDatafile2", clearUserProfileService: true)!
let optimizely = OTUtils.createOptimizely(datafileName: "bucketer_test2", clearUserProfileService: true)!
let config = optimizely.config!
let bucketer = DefaultBucketer()

Expand All @@ -269,7 +269,7 @@ extension BucketTests_Others {
func testBucketVariationGroupedExperimentsWithBucketingId() {
// make sure that bucketing works with experiments in group

let optimizely = OTUtils.createOptimizely(datafileName: "BucketerTestsDatafile2", clearUserProfileService: true)!
let optimizely = OTUtils.createOptimizely(datafileName: "bucketer_test2", clearUserProfileService: true)!
let config = optimizely.config!
let bucketer = DefaultBucketer()

Expand All @@ -296,4 +296,16 @@ extension BucketTests_Others {
XCTAssertNil(variation)
}

func testBucketVariationAtBoundaries() {
// testing #OASIS-7150
// userId(2113143589306368718) + experId(18513703488) = “211314358930636871818513703488” creates a hash value of 0

let optimizely = OTUtils.createOptimizely(datafileName: "bucketer_test3", clearUserProfileService: true)!

XCTAssertFalse(optimizely.isFeatureEnabled(featureKey: "async_payments", userId: "2113143589306368718"))
XCTAssertFalse(optimizely.isFeatureEnabled(featureKey: "async_payments", userId: "2113143589306368719"))
XCTAssertFalse(optimizely.isFeatureEnabled(featureKey: "async_payments", userId: "2113143589306368710"))
XCTAssertFalse(optimizely.isFeatureEnabled(featureKey: "async_payments", userId: "2113143589306368711"))
XCTAssertFalse(optimizely.isFeatureEnabled(featureKey: "async_payments", userId: "2113143589306368712"))
}
}
51 changes: 51 additions & 0 deletions Tests/TestData/bucketer_test3.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
{
"version": "4",
"rollouts": [
{
"experiments": [
{
"status": "Running",
"audienceIds": [ ],
"variations": [
{
"variables": [ ],
"id": "18532971919",
"key": "18532971919",
"featureEnabled": true
}
],
"id": "18513703488",
"key": "18513703488",
"layerId": "18513932701",
"trafficAllocation": [
{
"entityId": "18532971919",
"endOfRange": 0
}
],
"forcedVariations": { }
}
],
"id": "18513932701"
}
],
"anonymizeIP": true,
"projectId": "10431130345",
"variables": [],
"featureFlags": [
{
"experimentIds": [],
"rolloutId": "18513932701",
"variables": [],
"id": "18533552281",
"key": "async_payments"
}
],
"experiments": [],
"audiences": [],
"groups": [],
"attributes": [],
"accountId": "10367498574",
"events": [],
"revision": "100"
}