From 404fe4d8d988c410ada0cdcc01519be29ddaff52 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=96=B9=E5=8D=93?= Date: Mon, 5 Jul 2021 14:42:15 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=AE=9E=E7=8E=B0=20Java=20=E8=B0=83?= =?UTF-8?q?=E7=94=A8=20A/B=20Test=20=E6=9C=8D=E5=8A=A1=E6=8E=A5=E5=8F=A3?= =?UTF-8?q?=E8=BF=94=E5=9B=9E=E8=AF=95=E9=AA=8C=E6=95=B0=E6=8D=AE=E8=BF=9B?= =?UTF-8?q?=E8=A1=8C=E5=88=86=E6=9E=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 68 + README.md | 63 +- pom.xml | 56 + .../analytics/javasdk/ISensorsABTest.java | 273 +++ .../analytics/javasdk/SensorsABTest.java | 148 ++ .../analytics/javasdk/SensorsABTestConst.java | 44 + .../javasdk/SensorsABTestWorker.java | 272 +++ .../javasdk/bean/ABGlobalConfig.java | 181 ++ .../analytics/javasdk/bean/Experiment.java | 113 + .../javasdk/cache/EventCacheManager.java | 71 + .../javasdk/cache/ExperimentCacheManager.java | 87 + .../exception/HttpStatusException.java | 27 + .../analytics/javasdk/util/ABTestUtil.java | 28 + .../analytics/javasdk/util/HttpUtil.java | 65 + .../analytics/javasdk/HttpUtilTest.java | 46 + .../analytics/javasdk/SensorsTest.java | 2140 +++++++++++++++++ 16 files changed, 3680 insertions(+), 2 deletions(-) create mode 100644 .gitignore create mode 100644 pom.xml create mode 100644 src/main/java/com/sensorsdata/analytics/javasdk/ISensorsABTest.java create mode 100644 src/main/java/com/sensorsdata/analytics/javasdk/SensorsABTest.java create mode 100644 src/main/java/com/sensorsdata/analytics/javasdk/SensorsABTestConst.java create mode 100644 src/main/java/com/sensorsdata/analytics/javasdk/SensorsABTestWorker.java create mode 100644 src/main/java/com/sensorsdata/analytics/javasdk/bean/ABGlobalConfig.java create mode 100644 src/main/java/com/sensorsdata/analytics/javasdk/bean/Experiment.java create mode 100644 src/main/java/com/sensorsdata/analytics/javasdk/cache/EventCacheManager.java create mode 100644 src/main/java/com/sensorsdata/analytics/javasdk/cache/ExperimentCacheManager.java create mode 100644 src/main/java/com/sensorsdata/analytics/javasdk/exception/HttpStatusException.java create mode 100644 src/main/java/com/sensorsdata/analytics/javasdk/util/ABTestUtil.java create mode 100644 src/main/java/com/sensorsdata/analytics/javasdk/util/HttpUtil.java create mode 100644 src/test/java/com/sensorsdata/analytics/javasdk/HttpUtilTest.java create mode 100644 src/test/java/com/sensorsdata/analytics/javasdk/SensorsTest.java diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e75beff --- /dev/null +++ b/.gitignore @@ -0,0 +1,68 @@ +.DS_Store +git.properties + +### Intellij ### +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm + +*.iml + +## Directory-based project format: +.idea/ + +## File-based project format: +*.ipr +*.iws + +## Plugin-specific files: + +# IntelliJ +/out/ + +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Python +__pycache__ +*.pyc + +# Crashlytics plugin (for Android Studio and IntelliJ) +com_crashlytics_export_strings.xml +crashlytics.properties +crashlytics-build.properties + +# java build files +target +ui/node_modules +gulpfile.js + +# debug scripts. +deploy.sh +ftpsync.settings + +*.swp +.DS_Store + +# Xcode +build/* +*.pbxuser +!default.pbxuser +*.mode1v3 +!default.mode1v3 +*.mode2v3 +!default.mode2v3 +*.perspectivev3 +!default.perspectivev3 +*.xcworkspace +!default.xcworkspace +xcuserdata +profile +*.moved-aside +*.cer +*.p12 +*.mobileprovision + +# AppCode +.idea diff --git a/README.md b/README.md index eb5b091..56ad3f6 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,61 @@ -# abtesting-sdk-java -abtesting-sdk-java +![logo](https://opensource.sensorsdata.cn/wp-content/uploads/logo.png) +

+ +## 神策简介 + +[**神策数据**](https://www.sensorsdata.cn/) +(Sensors Data),隶属于神策网络科技(北京)有限公司,是一家专业的大数据分析服务公司,大数据分析行业开拓者,为客户提供深度用户行为分析平台、以及专业的咨询服务和行业解决方案,致力于帮助客户实现数据驱动。神策数据立足大数据及用户行为分析的技术与实践前沿,业务现已覆盖以互联网、金融、零售快消、高科技、制造等为代表的十多个主要行业、并可支持企业多个职能部门。公司总部在北京,并在上海、深圳、合肥、武汉等地拥有本地化的服务团队,覆盖东区及南区市场;公司拥有专业的服务团队,为客户提供一对一的客户服务。公司在大数据领域积累的核心关键技术,包括在海量数据采集、存储、清洗、分析挖掘、可视化、智能应用、安全与隐私保护等领域。 [**More**](https://www.sensorsdata.cn/about/aboutus.html) + + +## SDK 简介 + +A/B Testing SDK 是一款用于 A/B 测试的 SDK。 + +## 集成文档 + +请参考神策官网 [Java SDK 集成(A/B Testing)](https://manual.sensorsdata.cn/sa/latest/tech_ab_testing_go-27726801.html)。 + + +## 贡献 + +* 1. 在您的 GitHub 账户下 fork abtesting-sdk-java 开源项目; +* 2. 根据您的需求在本地 clone 一份 abtesting-sdk-java 源码; +* 3. 您修改或者新增功能后,push 到您 fork 的远程分支; +* 4. 创建 pull request,向 abtesting-sdk-java 官方开发分支提交合入请求; +* 5. 神策 SDK 研发团队会及时 review 代码,测试通过后合入。 + + +## 讨论 + +| 扫码加入神策数据开源社区 QQ 群
群号:785122381 | 扫码加入神策数据开源社区微信群 | +| ------ | ------ | +|![ QQ 讨论群](https://opensource.sensorsdata.cn/wp-content/uploads/ContentCommonPic_1.png) | ![ 微信讨论群 ](https://opensource.sensorsdata.cn/wp-content/uploads/ContentCommonPic_2.png) | + +## 公众号 + +| 扫码关注
神策数据开源社区 | 扫码关注
神策数据开源社区服务号 | +| ------ | ------ | +|![ 微信订阅号 ](https://opensource.sensorsdata.cn/wp-content/uploads/ContentCommonPic_3.png) | ![ 微信服务号 ](https://opensource.sensorsdata.cn/wp-content/uploads/ContentCommonPic_4.png) | + + +## 新书推荐 + +| 《数据驱动:从方法到实践》 | 《Android 全埋点解决方案》 | 《iOS 全埋点解决方案》 +| ------ | ------ | ------ | +| [![《数据驱动:从方法到实践》](https://opensource.sensorsdata.cn/wp-content/uploads/data_driven_book_1.jpg)](https://item.jd.com/12322322.html) | [![《Android 全埋点解决方案》](https://opensource.sensorsdata.cn/wp-content/uploads/Android-全埋点thumbnail_1.png)](https://item.jd.com/12574672.html) | [![《iOS 全埋点解决方案》](https://opensource.sensorsdata.cn/wp-content/uploads/iOS-全埋点thumbnail_1.png)](https://item.jd.com/12867068.html) + +## License + +Copyright 2015-2021 Sensors Data Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..229a9eb --- /dev/null +++ b/pom.xml @@ -0,0 +1,56 @@ + + + + 4.0.0 + + com.sensorsdata.analytics.javasdk.abtest + SensorsABTesting + 0.0.1 + + SensorsABTesting + http://sensorsdata.cn + + + UTF-8 + + + + + + + com.sensorsdata.analytics.javasdk + SensorsAnalyticsSDK + 3.2.0 + provided + + + + com.google.guava + guava + 18.0 + + + + junit + junit + 4.11 + test + + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + 7 + 7 + utf-8 + + + + + diff --git a/src/main/java/com/sensorsdata/analytics/javasdk/ISensorsABTest.java b/src/main/java/com/sensorsdata/analytics/javasdk/ISensorsABTest.java new file mode 100644 index 0000000..9f3f15d --- /dev/null +++ b/src/main/java/com/sensorsdata/analytics/javasdk/ISensorsABTest.java @@ -0,0 +1,273 @@ +package com.sensorsdata.analytics.javasdk; + +import com.sensorsdata.analytics.javasdk.bean.Experiment; +import com.sensorsdata.analytics.javasdk.exceptions.InvalidArgumentException; + +import java.util.Map; + +/** + * @author fz + * @version 1.0.0 + * @since 2021/06/09 17:30 + */ +public interface ISensorsABTest { + + /** + * 立即从服务端请求,忽略内存缓存 + * + * @param distinctId 匿名ID/用户业务ID + * @param isLoginId 是否为登录ID true:是登录ID,false:匿名ID + * @param experimentVariableName 试验变量名称 + * @param defaultValue 未命中试验,返回默认值(支持数据类型:number|boolean|String|json) + * @param enableAutoTrackEvent 是否开启自动上报 $ABTestTrigger 事件 + * @param timeoutMilliseconds 请求超时设置 ms + * @param properties 请求参数 + */ + Experiment asyncFetchABTest(String distinctId, boolean isLoginId, String experimentVariableName, + T defaultValue, boolean enableAutoTrackEvent, int timeoutMilliseconds, Map properties); + + /** + * 立即从服务端请求,忽略内存缓存 + *

+ * 默认开启上报 $ABTestTrigger 事件,默认请求超时事件为 3000 ms,properties 为空 + *

+ * + * @param distinctId 匿名ID/用户业务ID + * @param isLoginId 是否为登录ID true:是登录ID,false:匿名ID + * @param experimentVariableName 试验变量名称 + * @param defaultValue 未命中试验,返回默认值(支持数据类型:number|boolean|String|json) + */ + Experiment asyncFetchABTest(String distinctId, boolean isLoginId, String experimentVariableName, + T defaultValue); + + /** + * 立即从服务端请求,忽略内存缓存 + *

+ * 默认请求超时事件为 3000 ms,properties为空 + *

+ * + * @param distinctId 匿名ID/用户业务ID + * @param isLoginId 是否为登录ID true:是登录ID,false:匿名ID + * @param experimentVariableName 试验变量名称 + * @param defaultValue 未命中试验,返回默认值(支持数据类型:number|boolean|String|json) + * @param enableAutoTrackEvent 是否开启自动上报 $ABTestTrigger 事件 + */ + Experiment asyncFetchABTest(String distinctId, boolean isLoginId, String experimentVariableName, + T defaultValue, boolean enableAutoTrackEvent); + + /** + * 立即从服务端请求,忽略内存缓存 + *

+ * 默认开启自动上报 $ABTestTrigger 事件,properties为空 + *

+ * + * @param distinctId 匿名ID/用户业务ID + * @param isLoginId 是否为登录ID true:是登录ID,false:匿名ID + * @param experimentVariableName 试验变量名称 + * @param defaultValue 未命中试验,返回默认值(支持数据类型:number|boolean|String|json) + * @param timeoutMilliseconds 请求超时设置 ms + */ + Experiment asyncFetchABTest(String distinctId, boolean isLoginId, String experimentVariableName, + T defaultValue, int timeoutMilliseconds); + + /** + * 立即从服务端请求,忽略内存缓存 + *

+ * 默认开启自动上报 $ABTestTrigger 事件,默认请求超时事件为 3000 ms + *

+ * + * @param distinctId 匿名ID/用户业务ID + * @param isLoginId 是否为登录ID true:是登录ID,false:匿名ID + * @param experimentVariableName 试验变量名称 + * @param defaultValue 未命中试验,返回默认值(支持数据类型:number|boolean|String|json) + * @param properties 请求参数 + */ + Experiment asyncFetchABTest(String distinctId, boolean isLoginId, String experimentVariableName, + T defaultValue, Map properties); + + /** + * 立即从服务端请求,忽略内存缓存 + *

+ * 默认请求超时事件为 3000 ms + *

+ * + * @param distinctId 匿名ID/用户业务ID + * @param isLoginId 是否为登录ID true:是登录ID,false:匿名ID + * @param experimentVariableName 试验变量名称 + * @param defaultValue 未命中试验,返回默认值(支持数据类型:number|boolean|String|json) + * @param enableAutoTrackEvent 是否开启自动上报 $ABTestTrigger 事件 + * @param properties 请求参数 + */ + Experiment asyncFetchABTest(String distinctId, boolean isLoginId, String experimentVariableName, + T defaultValue, boolean enableAutoTrackEvent, Map properties); + + /** + * 立即从服务端请求,忽略内存缓存 + *

+ * 默认开启自动上报 $ABTestTrigger 事件 + *

+ * + * @param distinctId 匿名ID/用户业务ID + * @param isLoginId 是否为登录ID true:是登录ID,false:匿名ID + * @param experimentVariableName 试验变量名称 + * @param defaultValue 未命中试验,返回默认值(支持数据类型:number|boolean|String|json) + * @param timeoutMilliseconds 请求超时设置 ms + * @param properties 请求参数 + */ + Experiment asyncFetchABTest(String distinctId, boolean isLoginId, String experimentVariableName, + T defaultValue, int timeoutMilliseconds, Map properties); + + /** + * 立即从服务端请求,忽略内存缓存 + *

+ * 默认 properties 为空 + *

+ * + * @param distinctId 匿名ID/用户业务ID + * @param isLoginId 是否为登录ID true:是登录ID,false:匿名ID + * @param experimentVariableName 试验变量名称 + * @param defaultValue 未命中试验,返回默认值(支持数据类型:number|boolean|String|json) + * @param timeoutMilliseconds 请求超时设置 ms + * @param enableAutoTrackEvent 是否开启自动上报 $ABTestTrigger 事件 + */ + Experiment asyncFetchABTest(String distinctId, boolean isLoginId, String experimentVariableName, + T defaultValue, boolean enableAutoTrackEvent, int timeoutMilliseconds) + ; + + /** + * 优先读取内存缓存,缓存不存在时从再服务端获取试验数据 + * + * @param distinctId 匿名ID/用户业务ID + * @param isLoginId 是否为登录ID true:是登录ID,false:匿名ID + * @param experimentVariableName 试验变量名称 + * @param defaultValue 未命中试验,返回默认值(支持数据类型:number|boolean|String|json) + * @param enableAutoTrackEvent 是否开启自动上报 $ABTestTrigger 事件 + * @param timeoutMilliseconds 请求超时设置 ms + * @param properties 请求参数 + */ + Experiment fastFetchABTest(String distinctId, boolean isLoginId, String experimentVariableName, + T defaultValue, boolean enableAutoTrackEvent, int timeoutMilliseconds, Map properties); + + /** + * 优先读取内存缓存,缓存不存在时从再服务端获取试验数据 + *

+ * 默认开启上报 $ABTestTrigger 事件,默认请求超时事件为 3000 ms + *

+ * + * @param distinctId 匿名ID/用户业务ID + * @param isLoginId 是否为登录ID true:是登录ID,false:匿名ID + * @param experimentVariableName 试验变量名称 + * @param defaultValue 未命中试验,返回默认值(支持数据类型:number|boolean|String|json) + */ + Experiment fastFetchABTest(String distinctId, boolean isLoginId, String experimentVariableName, + T defaultValue); + + /** + * 优先读取内存缓存,缓存不存在时从再服务端获取试验数据 + *

+ * 默认请求超时事件为 3000 ms,properties 为空 + *

+ * + * @param distinctId 匿名ID/用户业务ID + * @param isLoginId 是否为登录ID true:是登录ID,false:匿名ID + * @param experimentVariableName 试验变量名称 + * @param defaultValue 未命中试验,返回默认值(支持数据类型:number|boolean|String|json) + * @param enableAutoTrackEvent 是否开启自动上报 $ABTestTrigger 事件 + */ + Experiment fastFetchABTest(String distinctId, boolean isLoginId, String experimentVariableName, + T defaultValue, boolean enableAutoTrackEvent); + + /** + * 优先读取内存缓存,缓存不存在时从再服务端获取试验数据 + *

+ * 默认开启上报 $ABTestTrigger 事件, properties 为空 + *

+ * + * @param distinctId 匿名ID/用户业务ID + * @param isLoginId 是否为登录ID true:是登录ID,false:匿名ID + * @param experimentVariableName 试验变量名称 + * @param defaultValue 未命中试验,返回默认值(支持数据类型:number|boolean|String|json) + * @param timeoutMilliseconds 请求超时设置 ms + */ + Experiment fastFetchABTest(String distinctId, boolean isLoginId, String experimentVariableName, + T defaultValue, int timeoutMilliseconds); + + /** + * 优先读取内存缓存,缓存不存在时从再服务端获取试验数据 + *

+ * 默认开启上报 $ABTestTrigger 事件,默认请求超时事件为 3000 ms + *

+ * + * @param distinctId 匿名ID/用户业务ID + * @param isLoginId 是否为登录ID true:是登录ID,false:匿名ID + * @param experimentVariableName 试验变量名称 + * @param defaultValue 未命中试验,返回默认值(支持数据类型:number|boolean|String|json) + * @param properties 请求参数 + */ + Experiment fastFetchABTest(String distinctId, boolean isLoginId, String experimentVariableName, + T defaultValue, Map properties); + + /** + * 优先读取内存缓存,缓存不存在时从再服务端获取试验数据 + *

+ * 默认请求超时事件为 3000 ms + *

+ * + * @param distinctId 匿名ID/用户业务ID + * @param isLoginId 是否为登录ID true:是登录ID,false:匿名ID + * @param experimentVariableName 试验变量名称 + * @param defaultValue 未命中试验,返回默认值(支持数据类型:number|boolean|String|json) + * @param enableAutoTrackEvent 是否开启自动上报 $ABTestTrigger 事件 + * @param properties 请求参数 + */ + Experiment fastFetchABTest(String distinctId, boolean isLoginId, String experimentVariableName, + T defaultValue, boolean enableAutoTrackEvent, Map properties); + + /** + * 优先读取内存缓存,缓存不存在时从再服务端获取试验数据 + *

+ * 默认开启上报 $ABTestTrigger 事件 + *

+ * + * @param distinctId 匿名ID/用户业务ID + * @param isLoginId 是否为登录ID true:是登录ID,false:匿名ID + * @param experimentVariableName 试验变量名称 + * @param defaultValue 未命中试验,返回默认值(支持数据类型:number|boolean|String|json) + * @param timeoutMilliseconds 请求超时设置 ms + * @param properties 请求参数 + */ + Experiment fastFetchABTest(String distinctId, boolean isLoginId, String experimentVariableName, + T defaultValue, int timeoutMilliseconds, Map properties); + + /** + * 优先读取内存缓存,缓存不存在时从再服务端获取试验数据 + *

+ * 默认 properties 为空 + *

+ * + * @param distinctId 匿名ID/用户业务ID + * @param isLoginId 是否为登录ID true:是登录ID,false:匿名ID + * @param experimentVariableName 试验变量名称 + * @param defaultValue 未命中试验,返回默认值(支持数据类型:number|boolean|String|json) + * @param timeoutMilliseconds 请求超时设置 ms + * @param enableAutoTrackEvent 是否开启自动上报 $ABTestTrigger 事件 + * @return ExperimentResult + */ + Experiment fastFetchABTest(String distinctId, boolean isLoginId, String experimentVariableName, + T defaultValue, boolean enableAutoTrackEvent, int timeoutMilliseconds); + + /** + * 手动上报 $ABTestTrigger 事件 + * + * @param experiment 试验结果 + */ + void trackABTestTrigger(Experiment experiment) throws InvalidArgumentException; + + /** + * 手动上报 $ABTestTrigger 事件 + * + * @param experiment 试验结果 + * @param properties 请求参数 + */ + void trackABTestTrigger(Experiment experiment, Map properties) throws InvalidArgumentException; +} diff --git a/src/main/java/com/sensorsdata/analytics/javasdk/SensorsABTest.java b/src/main/java/com/sensorsdata/analytics/javasdk/SensorsABTest.java new file mode 100644 index 0000000..73bfa5a --- /dev/null +++ b/src/main/java/com/sensorsdata/analytics/javasdk/SensorsABTest.java @@ -0,0 +1,148 @@ +package com.sensorsdata.analytics.javasdk; + +import com.sensorsdata.analytics.javasdk.bean.ABGlobalConfig; +import com.sensorsdata.analytics.javasdk.bean.Experiment; +import com.sensorsdata.analytics.javasdk.exceptions.InvalidArgumentException; + +import java.util.Map; + +/** + * @author fz + * @version 1.0.0 + * @since 2021/06/09 18:50 + */ +public class SensorsABTest implements ISensorsABTest { + + private final SensorsABTestWorker worker; + + public SensorsABTest(ABGlobalConfig config) { + this.worker = new SensorsABTestWorker(config); + } + + @Override + public Experiment asyncFetchABTest(String distinctId, boolean isLoginId, String experimentVariableName, + T defaultValue, boolean enableAutoTrackEvent, int timeoutMilliseconds, Map properties) { + return worker.fetchABTest(distinctId, isLoginId, experimentVariableName, defaultValue, enableAutoTrackEvent, + timeoutMilliseconds, properties, false); + } + + + @Override + public Experiment asyncFetchABTest(String distinctId, boolean isLoginId, String experimentVariableName, + T defaultValue) { + return worker.fetchABTest(distinctId, isLoginId, experimentVariableName, defaultValue, true, + 3000, null, false); + } + + @Override + public Experiment asyncFetchABTest(String distinctId, boolean isLoginId, String experimentVariableName, + T defaultValue, boolean enableAutoTrackEvent) { + return worker.fetchABTest(distinctId, isLoginId, experimentVariableName, defaultValue, enableAutoTrackEvent, + 3000, null, false); + } + + @Override + public Experiment asyncFetchABTest(String distinctId, boolean isLoginId, String experimentVariableName, + T defaultValue, int timeoutMilliseconds) { + return worker.fetchABTest(distinctId, isLoginId, experimentVariableName, defaultValue, true, + timeoutMilliseconds, null, false); + } + + @Override + public Experiment asyncFetchABTest(String distinctId, boolean isLoginId, String experimentVariableName, + T defaultValue, Map properties) { + return worker.fetchABTest(distinctId, isLoginId, experimentVariableName, defaultValue, true, + 3000, properties, false); + } + + + @Override + public Experiment asyncFetchABTest(String distinctId, boolean isLoginId, String experimentVariableName, + T defaultValue, boolean enableAutoTrackEvent, Map properties) { + return worker.fetchABTest(distinctId, isLoginId, experimentVariableName, defaultValue, enableAutoTrackEvent, + 3000, properties, false); + } + + + @Override + public Experiment asyncFetchABTest(String distinctId, boolean isLoginId, String experimentVariableName, + T defaultValue, int timeoutMilliseconds, Map properties) { + return worker.fetchABTest(distinctId, isLoginId, experimentVariableName, defaultValue, true, + timeoutMilliseconds, properties, false); + } + + @Override + public Experiment asyncFetchABTest(String distinctId, boolean isLoginId, String experimentVariableName, + T defaultValue, boolean enableAutoTrackEvent, int timeoutMilliseconds) { + return worker.fetchABTest(distinctId, isLoginId, experimentVariableName, defaultValue, enableAutoTrackEvent, + timeoutMilliseconds, null, false); + } + + @Override + public Experiment fastFetchABTest(String distinctId, boolean isLoginId, String experimentVariableName, + T defaultValue, boolean enableAutoTrackEvent, int timeoutMilliseconds, Map properties) { + return worker.fetchABTest(distinctId, isLoginId, experimentVariableName, defaultValue, enableAutoTrackEvent, + timeoutMilliseconds, properties, true); + } + + @Override + public Experiment fastFetchABTest(String distinctId, boolean isLoginId, String experimentVariableName, + T defaultValue) { + return worker.fetchABTest(distinctId, isLoginId, experimentVariableName, defaultValue, true, + 3000, null, true); + } + + @Override + public Experiment fastFetchABTest(String distinctId, boolean isLoginId, String experimentVariableName, + T defaultValue, boolean enableAutoTrackEvent) { + return worker.fetchABTest(distinctId, isLoginId, experimentVariableName, defaultValue, enableAutoTrackEvent, + 3000, null, true); + } + + @Override + public Experiment fastFetchABTest(String distinctId, boolean isLoginId, String experimentVariableName, + T defaultValue, int timeoutMilliseconds) { + return worker.fetchABTest(distinctId, isLoginId, experimentVariableName, defaultValue, true, + 3000, null, true); + } + + @Override + public Experiment fastFetchABTest(String distinctId, boolean isLoginId, String experimentVariableName, + T defaultValue, Map properties) { + return worker.fetchABTest(distinctId, isLoginId, experimentVariableName, defaultValue, true, + 3000, properties, true); + } + + @Override + public Experiment fastFetchABTest(String distinctId, boolean isLoginId, String experimentVariableName, + T defaultValue, boolean enableAutoTrackEvent, Map properties) { + return worker.fetchABTest(distinctId, isLoginId, experimentVariableName, defaultValue, enableAutoTrackEvent, + 3000, properties, true); + } + + @Override + public Experiment fastFetchABTest(String distinctId, boolean isLoginId, String experimentVariableName, + T defaultValue, int timeoutMilliseconds, Map properties) { + return worker.fetchABTest(distinctId, isLoginId, experimentVariableName, defaultValue, true, + timeoutMilliseconds, properties, true); + } + + @Override + public Experiment fastFetchABTest(String distinctId, boolean isLoginId, String experimentVariableName, + T defaultValue, boolean enableAutoTrackEvent, int timeoutMilliseconds) { + return worker.fetchABTest(distinctId, isLoginId, experimentVariableName, defaultValue, enableAutoTrackEvent, + timeoutMilliseconds, null, true); + } + + @Override + public void trackABTestTrigger(Experiment experiment) throws InvalidArgumentException { + this.trackABTestTrigger(experiment, null); + } + + + @Override + public void trackABTestTrigger(Experiment experiment, Map properties) + throws InvalidArgumentException { + this.worker.trackABTestTrigger(experiment, properties); + } +} diff --git a/src/main/java/com/sensorsdata/analytics/javasdk/SensorsABTestConst.java b/src/main/java/com/sensorsdata/analytics/javasdk/SensorsABTestConst.java new file mode 100644 index 0000000..2aaece9 --- /dev/null +++ b/src/main/java/com/sensorsdata/analytics/javasdk/SensorsABTestConst.java @@ -0,0 +1,44 @@ +package com.sensorsdata.analytics.javasdk; + +/** + * 常量类 + * + * @author fz + * @version 1.0.0 + * @since 2021/06/15 14:39 + */ +public class SensorsABTestConst { + + private SensorsABTestConst() { + } + + static final String PLATFORM = "platform"; + + static final String JAVA = "Java"; + + static final String VERSION = "0.0.1"; + + static final String VERSION_KEY = "abtest_lib_version"; + + /** + * 上报事件使用的key + */ + static final String EXPERIMENT_ID = "$abtest_experiment_id"; + static final String EXPERIMENT_GROUP_ID = "$abtest_experiment_group_id"; + static final String EVENT_TYPE = "$ABTestTrigger"; + static final String LIB_PLUGIN_VERSION = "$lib_plugin_version"; + static final String AB_TEST_EVENT_LIB_VERSION = "java_abtesting"; + /** + * 返回json里面的key + */ + static final String EXPERIMENT_ID_KEY = "abtest_experiment_id"; + static final String EXPERIMENT_GROUP_ID_KEY = "abtest_experiment_group_id"; + static final String IS_CONTROL_GROUP_KEY = "is_control_group"; + static final String IS_WHITE_LIST_KEY = "is_white_list"; + public static final String VARIABLES_KEY = "variables"; + public static final String RESULTS_KEY = "results"; + public static final String STATUS_KEY = "status"; + public static final String SUCCESS = "SUCCESS"; + + +} diff --git a/src/main/java/com/sensorsdata/analytics/javasdk/SensorsABTestWorker.java b/src/main/java/com/sensorsdata/analytics/javasdk/SensorsABTestWorker.java new file mode 100644 index 0000000..00cd04d --- /dev/null +++ b/src/main/java/com/sensorsdata/analytics/javasdk/SensorsABTestWorker.java @@ -0,0 +1,272 @@ +package com.sensorsdata.analytics.javasdk; + +import com.sensorsdata.analytics.javasdk.bean.ABGlobalConfig; +import com.sensorsdata.analytics.javasdk.bean.Experiment; +import com.sensorsdata.analytics.javasdk.cache.EventCacheManager; +import com.sensorsdata.analytics.javasdk.cache.ExperimentCacheManager; +import com.sensorsdata.analytics.javasdk.exception.HttpStatusException; +import com.sensorsdata.analytics.javasdk.exceptions.InvalidArgumentException; +import com.sensorsdata.analytics.javasdk.util.ABTestUtil; +import com.sensorsdata.analytics.javasdk.util.HttpUtil; +import com.sensorsdata.analytics.javasdk.util.SensorsAnalyticsUtil; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; + +import java.io.IOException; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Calendar; +import java.util.Date; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +/** + * AB Test 逻辑处理 + * + * @author fz + * @version 1.0.0 + * @since 2021/06/16 15:12 + */ +class SensorsABTestWorker { + + private final ObjectMapper objectMapper; + /** + * 全局配置信息 + */ + private final ABGlobalConfig config; + /** + * 试验结果缓存管理器 + */ + private final ExperimentCacheManager experimentCacheManager; + /** + * 试验上报事件缓存 + */ + private final EventCacheManager eventCacheManager; + /** + * 是否当天首次触发 + */ + private String trigger; + + SensorsABTestWorker(ABGlobalConfig config) { + this.config = config; + this.objectMapper = SensorsAnalyticsUtil.getJsonObjectMapper(); + this.experimentCacheManager = + ExperimentCacheManager.getInstance(config.getExperimentCacheTime(), config.getExperimentCacheSize()); + this.eventCacheManager = + EventCacheManager.getInstance(config.getEventCacheTime(), config.getEventCacheSize()); + } + + /** + * 处理AB Test 结果 + * + * @param distinctId 匿名ID/业务ID + * @param isLoginId 是否为登录ID + * @param experimentVariableName 试验名称 + * @param defaultValue 默认值 + * @param enableAutoTrackEvent 是否开启自动上报事件 + * @param enableCache 是否开启优先查询缓存 + * @param timeoutMilliseconds 接口超时设置 + * @param properties 接口请求参数 + * @return Experiment + */ + Experiment fetchABTest(String distinctId, boolean isLoginId, String experimentVariableName, T defaultValue, + boolean enableAutoTrackEvent, int timeoutMilliseconds, Map properties, boolean enableCache) { + if (distinctId == null || distinctId.isEmpty()) { + ABTestUtil.printLog(config.getEnableLog(), "info message: distinctId is empty or null,return defaultValue"); + return new Experiment<>(distinctId, isLoginId, defaultValue); + } + if (experimentVariableName == null || experimentVariableName.isEmpty()) { + ABTestUtil.printLog(config.getEnableLog(), "info message: experimentVariableName is empty or null,return defaultValue"); + return new Experiment<>(distinctId, isLoginId, defaultValue); + } + if (!ABTestUtil.assertDefaultValueType(defaultValue)) { + ABTestUtil.printLog(config.getEnableLog(), "info message: the type of defaultValue is not Integer,String,Boolean or Json of String,return defaultValue"); + return new Experiment<>(distinctId, isLoginId, defaultValue); + } + JsonNode experiment; + //判断是否需要读取结果缓存 + if (enableCache) { + experiment = experimentCacheManager.getExperimentResultByCache(distinctId, isLoginId, experimentVariableName); + //未命中缓存 + if (experiment == null) { + experiment = getABTestByHttp(distinctId, isLoginId, timeoutMilliseconds, properties); + experimentCacheManager.setExperimentResultCache(distinctId, isLoginId, experiment); + } + } else { + experiment = getABTestByHttp(distinctId, isLoginId, timeoutMilliseconds, properties); + } + Experiment result = convertExperiment(experiment, distinctId, isLoginId, experimentVariableName, defaultValue); + //判断是否需要自动触发上报事件 + if (enableAutoTrackEvent) { + try { + this.trackABTestTrigger(result, null); + } catch (InvalidArgumentException e) { + ABTestUtil.printLog(config.getEnableLog(), String.format("error message: auto track ABTest event %s", e.getMessage())); + } + } + return result; + } + + /** + * 触发 $ABTestTrigger 事件上报 + * + * @param result 试验结果 + * @param properties 请求参数 + * @throws InvalidArgumentException 上报事件的时候对上报事件参数进行校验,如果参数不合法,则抛出异常 InvalidArgumentException + */ + void trackABTestTrigger(Experiment result, Map properties) throws InvalidArgumentException { + if (result == null) { + ABTestUtil.printLog(config.getEnableLog(), "info message: track ABTest event Experiment is null"); + return; + } + if (result.getWhiteList() == null || result.getWhiteList() || result.getAbTestExperimentId() == null) { + ABTestUtil.printLog(config.getEnableLog(), "info message: track ABTest event user not hit experiment or in the whiteList"); + return; + } + //缓存中存在 A/B 事件 + if (eventCacheManager.judgeEventCacheExist(result.getDistinctId(), result.getAbTestExperimentId())) { + ABTestUtil.printLog(config.getEnableLog(), "info message: track ABTest event user trigger event have been cached"); + return; + } + if (properties == null) { + properties = new HashMap<>(16); + } + properties.put(SensorsABTestConst.EXPERIMENT_ID, result.getAbTestExperimentId()); + properties.put(SensorsABTestConst.EXPERIMENT_GROUP_ID, result.getAbTestExperimentGroupId()); + //判断是否为当天首次上传,重启服务,升级 JDK 版本都会触发 + String day = this.formatDate(Calendar.getInstance().getTime()); + if (trigger == null || trigger.isEmpty() || !day.equals(trigger)) { + trigger = day; + List versions = new ArrayList<>(); + versions.add(String.format("%s:%s", SensorsABTestConst.AB_TEST_EVENT_LIB_VERSION, SensorsABTestConst.VERSION)); + properties.put(SensorsABTestConst.LIB_PLUGIN_VERSION, versions); + } + this.config.getSensorsAnalytics().track(result.getDistinctId(), result.getIsLoginId(), + SensorsABTestConst.EVENT_TYPE, properties); + //判断是否需要缓存上报事件 + if (config.getEnableEventCache() == null || config.getEnableEventCache()) { + eventCacheManager.setEventCache(result.getDistinctId(), result.getAbTestExperimentId()); + } + } + + /** + * 组合请求参数,然后进行网络请求 + * + * @return 网络请求成功, 并且返回对象状态为 SUCCESS 和 results 有值,则返回 JsonNode;否则返回 null + */ + private JsonNode getABTestByHttp(String distinctId, boolean isLoginId, int timeoutMilliseconds, + Map properties) { + Map params = new HashMap<>(16); + if (isLoginId) { + params.put("login_id", distinctId); + } else { + params.put("anonymous_id", distinctId); + } + params.put(SensorsABTestConst.PLATFORM, SensorsABTestConst.JAVA); + params.put(SensorsABTestConst.VERSION_KEY, SensorsABTestConst.VERSION); + if (properties == null) { + properties = new HashMap<>(16); + } + params.put("properties", properties); + try { + String strJson = objectMapper.writeValueAsString(params); + String result = HttpUtil.postABTest(config.getApiUrl(), strJson, timeoutMilliseconds); + JsonNode res = objectMapper.readTree(result); + if (res != null && SensorsABTestConst.SUCCESS.equals(res.findValue(SensorsABTestConst.STATUS_KEY).asText()) + && res.findValue(SensorsABTestConst.RESULTS_KEY).size() > 0) { + return res; + } + return null; + } catch (HttpStatusException | IOException e) { + ABTestUtil.printLog(config.getEnableLog(), String.format("error message: %s", e.getMessage())); + return null; + } + } + + + /** + * 将请求返回结果解析成标准返回对象 + * + * @return ExperimentResult + */ + private Experiment convertExperiment(JsonNode message, String distinctId, boolean isLoginId, + String experimentVariableName, T defaultValue) { + if (message == null) { + ABTestUtil.printLog(config.getEnableLog(),"info message: request result was not obtained,return defaultValue"); + return new Experiment<>(distinctId, isLoginId, defaultValue); + } + JsonNode results = message.findValue(SensorsABTestConst.RESULTS_KEY); + Iterator iterator = results.elements(); + while (iterator.hasNext()) { + JsonNode experiment = iterator.next(); + Iterator varIterator = experiment.findValue(SensorsABTestConst.VARIABLES_KEY).elements(); + while (varIterator.hasNext()) { + JsonNode variable = varIterator.next(); + T value = hitExperimentValue(variable, experimentVariableName, defaultValue); + if (value != null) { + return new Experiment<>(distinctId, isLoginId, + experiment.findValue(SensorsABTestConst.EXPERIMENT_ID_KEY).asText(), + experiment.findValue(SensorsABTestConst.EXPERIMENT_GROUP_ID_KEY).asText(), + experiment.findValue(SensorsABTestConst.IS_CONTROL_GROUP_KEY).asBoolean(), + experiment.findValue(SensorsABTestConst.IS_WHITE_LIST_KEY).asBoolean(), value); + } + } + } + ABTestUtil.printLog(config.getEnableLog(),"info message: missing experiment,return defaultValue"); + return new Experiment<>(distinctId, isLoginId, defaultValue); + } + + /** + * 判断是否命中试验变量值 + * + * @param variable 返回试验变量 + * @param experimentVariableName 试验名 + * @param defaultValue 默认值 + * @return 默认值与返回值类型匹配,则返回结果;默认值与返回值类型不匹配则返回null + */ + @SuppressWarnings("unchecked") + private T hitExperimentValue(JsonNode variable, String experimentVariableName, T defaultValue) { + if (!experimentVariableName.equals(variable.findValue("name").asText())) { + return null; + } + JsonNode value = variable.findValue("value"); + if (value == null) { + return null; + } + switch (variable.findValue("type").asText()) { + case "STRING": + if (defaultValue instanceof String) { + return (T) value.asText(); + } + break; + case "INTEGER": + if (defaultValue instanceof Integer) { + return (T) Integer.valueOf(value.asInt()); + } + break; + case "JSON": + if (defaultValue instanceof String && ((String) defaultValue).startsWith("{") + && ((String) defaultValue).endsWith("}")) { + return (T) value.asText(); + } + break; + case "BOOLEAN": + if (defaultValue instanceof Boolean) { + return (T) Boolean.valueOf(value.asBoolean()); + } + break; + //未命中类型 + default: + break; + } + return null; + } + + private String formatDate(Date date) { + return new SimpleDateFormat("yyyy-MM-dd").format(date); + } + +} diff --git a/src/main/java/com/sensorsdata/analytics/javasdk/bean/ABGlobalConfig.java b/src/main/java/com/sensorsdata/analytics/javasdk/bean/ABGlobalConfig.java new file mode 100644 index 0000000..a86f9d2 --- /dev/null +++ b/src/main/java/com/sensorsdata/analytics/javasdk/bean/ABGlobalConfig.java @@ -0,0 +1,181 @@ +package com.sensorsdata.analytics.javasdk.bean; + +import com.sensorsdata.analytics.javasdk.ISensorsAnalytics; +import com.sensorsdata.analytics.javasdk.exceptions.InvalidArgumentException; + +import java.io.Serializable; + +/** + * AB Testing 全局参数配置 + * + * @author fz + * @version 1.0.0 + * @since 2021/06/09 16:16 + */ +public class ABGlobalConfig implements Serializable { + private static final long serialVersionUID = 3285160955772051586L; + /** + * 单用户事件缓存 + */ + private final Integer eventCacheTime; + /** + * 事件总缓存用户量 + */ + private final Integer eventCacheSize; + /** + * 试验总缓存用户量 + */ + private final Integer experimentCacheSize; + /** + * 单用户试验缓存时间 + */ + private final Integer experimentCacheTime; + /** + * 是否开启 ABTestTrigger 事件 + */ + private final Boolean enableEventCache; + /** + * 分流试验地址 + */ + private final String apiUrl; + /** + * 神策分析 sa(要求使用3.2.0及以上版本) + */ + private final ISensorsAnalytics sensorsAnalytics; + /** + * 是否开启运行日志 + */ + private final Boolean enableLog; + + private ABGlobalConfig(Integer eventCacheTime, Integer eventCacheSize, Integer experimentCacheSize, + Integer experimentCacheTime, Boolean enableEventCache, String apiUrl, ISensorsAnalytics sensorsAnalytics, + Boolean enableLog) { + this.eventCacheTime = eventCacheTime; + this.eventCacheSize = eventCacheSize; + this.experimentCacheSize = experimentCacheSize; + this.experimentCacheTime = experimentCacheTime; + this.enableEventCache = enableEventCache; + this.apiUrl = apiUrl; + this.sensorsAnalytics = sensorsAnalytics; + this.enableLog = enableLog; + } + + public static Builder builder() { + return new Builder(); + } + + public Integer getEventCacheTime() { + return eventCacheTime; + } + + public Integer getEventCacheSize() { + return eventCacheSize; + } + + public Integer getExperimentCacheSize() { + return experimentCacheSize; + } + + public Integer getExperimentCacheTime() { + return experimentCacheTime; + } + + public Boolean getEnableEventCache() { + return enableEventCache; + } + + public String getApiUrl() { + return apiUrl; + } + + public ISensorsAnalytics getSensorsAnalytics() { + return sensorsAnalytics; + } + + public Boolean getEnableLog() { + return enableLog; + } + + public static class Builder { + private Integer eventCacheTime; + private Integer eventCacheSize; + private Integer experimentCacheSize; + private Integer experimentCacheTime; + private Boolean enableEventCache; + private String apiUrl; + private ISensorsAnalytics sensorsAnalytics; + private Boolean enableLog; + + private Builder() { + } + + public ABGlobalConfig build() throws InvalidArgumentException { + if (apiUrl == null || apiUrl.length() == 0) { + throw new InvalidArgumentException("The apiUrl is empty."); + } + if (sensorsAnalytics == null) { + throw new InvalidArgumentException("The Sensors Analysis SDK is empty."); + } + if (eventCacheTime == null || eventCacheTime > 1440 || eventCacheTime < 0) { + eventCacheTime = 1440; + } + if (eventCacheSize == null || eventCacheSize < 0) { + eventCacheSize = 4096; + } + if (experimentCacheSize == null || experimentCacheSize < 0) { + experimentCacheSize = 4096; + } + if (experimentCacheTime == null || experimentCacheTime > 1440 || experimentCacheTime < 0) { + experimentCacheTime = 1440; + } + if (enableEventCache == null) { + enableEventCache = true; + } + if (enableLog == null) { + enableLog = false; + } + return new ABGlobalConfig(eventCacheTime, eventCacheSize, experimentCacheSize, + experimentCacheTime, enableEventCache, apiUrl, sensorsAnalytics, enableLog); + } + + public ABGlobalConfig.Builder setEventCacheTime(Integer eventCacheTime) { + this.eventCacheTime = eventCacheTime; + return this; + } + + public ABGlobalConfig.Builder setEventCacheSize(Integer eventCacheSize) { + this.eventCacheSize = eventCacheSize; + return this; + } + + public ABGlobalConfig.Builder setExperimentCacheSize(Integer experimentCacheSize) { + this.experimentCacheSize = experimentCacheSize; + return this; + } + + public ABGlobalConfig.Builder setExperimentCacheTime(Integer experimentCacheTime) { + this.experimentCacheTime = experimentCacheTime; + return this; + } + + public ABGlobalConfig.Builder enableEventCache(Boolean enableEventCache) { + this.enableEventCache = enableEventCache; + return this; + } + + public ABGlobalConfig.Builder setApiUrl(String apiUrl) { + this.apiUrl = apiUrl; + return this; + } + + public ABGlobalConfig.Builder setSensorsAnalytics(ISensorsAnalytics sensorsAnalytics) { + this.sensorsAnalytics = sensorsAnalytics; + return this; + } + + public ABGlobalConfig.Builder enableLog(Boolean enableLog) { + this.enableLog = enableLog; + return this; + } + } +} diff --git a/src/main/java/com/sensorsdata/analytics/javasdk/bean/Experiment.java b/src/main/java/com/sensorsdata/analytics/javasdk/bean/Experiment.java new file mode 100644 index 0000000..9ac33b1 --- /dev/null +++ b/src/main/java/com/sensorsdata/analytics/javasdk/bean/Experiment.java @@ -0,0 +1,113 @@ +package com.sensorsdata.analytics.javasdk.bean; + +import java.io.Serializable; + +/** + * @author fz + * @version 1.0.0 + * @since 2021/06/09 21:47 + */ +public class Experiment implements Serializable { + private static final long serialVersionUID = -236604631513293247L; + /** + * 匿名ID或登录ID + */ + private String distinctId; + /** + * 是否为登录ID + */ + private Boolean isLoginId; + /** + * 试验ID + */ + private String abTestExperimentId; + /** + * 试验分组ID + */ + private String abTestExperimentGroupId; + /** + * 是否对照组 + */ + private Boolean isControlGroup; + /** + * 是否在白名单 + */ + private Boolean isWhiteList; + /** + * 命中值 + */ + private T result; + + public Experiment(String distinctId, Boolean isLoginId, String abTestExperimentId, + String abTestExperimentGroupId, Boolean isControlGroup, Boolean isWhiteList, T value) { + this.distinctId = distinctId; + this.isLoginId = isLoginId; + this.abTestExperimentId = abTestExperimentId; + this.abTestExperimentGroupId = abTestExperimentGroupId; + this.isControlGroup = isControlGroup; + this.isWhiteList = isWhiteList; + this.result = value; + } + + public Experiment(String distinctId, Boolean isLoginId, T value) { + this.distinctId = distinctId; + this.isLoginId = isLoginId; + this.result = value; + } + + public String getDistinctId() { + return distinctId; + } + + public void setDistinctId(String distinctId) { + this.distinctId = distinctId; + } + + public Boolean getIsLoginId() { + return isLoginId; + } + + public void isLoginId(Boolean loginId) { + isLoginId = loginId; + } + + public String getAbTestExperimentId() { + return abTestExperimentId; + } + + public void setAbTestExperimentId(String abTestExperimentId) { + this.abTestExperimentId = abTestExperimentId; + } + + public String getAbTestExperimentGroupId() { + return abTestExperimentGroupId; + } + + public void setAbTestExperimentGroupId(String abTestExperimentGroupId) { + this.abTestExperimentGroupId = abTestExperimentGroupId; + } + + public Boolean getControlGroup() { + return isControlGroup; + } + + public void setControlGroup(Boolean controlGroup) { + isControlGroup = controlGroup; + } + + public Boolean getWhiteList() { + return isWhiteList; + } + + public void setWhiteList(Boolean whiteList) { + isWhiteList = whiteList; + } + + public T getResult() { + return result; + } + + public void setResult(T result) { + this.result = result; + } +} diff --git a/src/main/java/com/sensorsdata/analytics/javasdk/cache/EventCacheManager.java b/src/main/java/com/sensorsdata/analytics/javasdk/cache/EventCacheManager.java new file mode 100644 index 0000000..8bcd2c3 --- /dev/null +++ b/src/main/java/com/sensorsdata/analytics/javasdk/cache/EventCacheManager.java @@ -0,0 +1,71 @@ +package com.sensorsdata.analytics.javasdk.cache; + +import com.fasterxml.jackson.databind.JsonNode; +import com.google.common.cache.CacheBuilder; +import com.google.common.cache.CacheLoader; +import com.google.common.cache.LoadingCache; + +import java.util.concurrent.TimeUnit; + +/** + * AB Test 事件缓存管理器 + * + * @author fz + * @version 1.0.0 + * @since 2021/06/17 11:18 + */ +public class EventCacheManager { + /** + * 上报事件缓存 + */ + private final LoadingCache eventCache; + /** + * 缓存过期时间 + */ + private static long duration; + /** + * 缓存最大容量 + */ + private static int size; + + private static final Object OBJ = new Object(); + + private EventCacheManager() { + this.eventCache = CacheBuilder.newBuilder() + .expireAfterWrite(duration, TimeUnit.MINUTES) + .maximumSize(size) + .build(new CacheLoader() { + @Override + public JsonNode load(String s) { + return null; + } + }); + } + + private static class SensorsABTestCacheManagerStaticNestedClass { + private static final EventCacheManager INSTANCE = new EventCacheManager(); + } + + public static EventCacheManager getInstance(int cacheTime, int cacheSize) { + duration = cacheTime; + size = cacheSize; + return EventCacheManager.SensorsABTestCacheManagerStaticNestedClass.INSTANCE; + } + + public boolean judgeEventCacheExist(String distinctId, String experimentId) { + return this.eventCache.getIfPresent(generateKey(distinctId, experimentId)) + != null; + } + + public void setEventCache(String distinctId, String experimentId) { + this.eventCache.put(generateKey(distinctId, experimentId), OBJ); + } + + public long getCacheSize() { + return this.eventCache.size(); + } + + private String generateKey(String distinctId, String experimentId) { + return String.format("%s_%s", distinctId, experimentId); + } +} diff --git a/src/main/java/com/sensorsdata/analytics/javasdk/cache/ExperimentCacheManager.java b/src/main/java/com/sensorsdata/analytics/javasdk/cache/ExperimentCacheManager.java new file mode 100644 index 0000000..5520713 --- /dev/null +++ b/src/main/java/com/sensorsdata/analytics/javasdk/cache/ExperimentCacheManager.java @@ -0,0 +1,87 @@ +package com.sensorsdata.analytics.javasdk.cache; + +import com.sensorsdata.analytics.javasdk.SensorsABTestConst; + +import com.fasterxml.jackson.databind.JsonNode; +import com.google.common.cache.CacheBuilder; +import com.google.common.cache.CacheLoader; +import com.google.common.cache.LoadingCache; + +import java.util.Iterator; +import java.util.concurrent.TimeUnit; + +/** + * AB Test 试验结果缓存管理器 + * + * @author fz + * @version 1.0.0 + * @since 2021/06/16 15:41 + */ +public class ExperimentCacheManager { + + private final LoadingCache experimentResultCache; + /** + * 缓存过期时间 + */ + private static long duration; + /** + * 缓存最大容量 + */ + private static int size; + + private ExperimentCacheManager() { + this.experimentResultCache = CacheBuilder.newBuilder() + .expireAfterWrite(duration, TimeUnit.MINUTES) + .maximumSize(size) + .build(new CacheLoader() { + @Override + public JsonNode load(String s) { + return null; + } + }); + } + + private static class SensorsABTestCacheManagerStaticNestedClass { + private static final ExperimentCacheManager INSTANCE = new ExperimentCacheManager(); + } + + public static ExperimentCacheManager getInstance(int cacheTime, int cacheSize) { + duration = cacheTime; + size = cacheSize; + return SensorsABTestCacheManagerStaticNestedClass.INSTANCE; + } + + public JsonNode getExperimentResultByCache(String distinctId, boolean isLoginId, String experimentName) { + JsonNode experimentResult = this.experimentResultCache.getIfPresent(generateKey(distinctId, isLoginId)); + if (experimentResult == null) { + return null; + } + JsonNode results = experimentResult.findValue(SensorsABTestConst.RESULTS_KEY); + Iterator resIterator = results.elements(); + while (resIterator.hasNext()) { + JsonNode experiment = resIterator.next(); + Iterator varIterator = experiment.findValue(SensorsABTestConst.VARIABLES_KEY).elements(); + while (varIterator.hasNext()) { + JsonNode variable = varIterator.next(); + if (variable.findValue("name").asText().equals(experimentName)) { + return experimentResult; + } + } + } + return null; + } + + public void setExperimentResultCache(String distinctId, boolean isLoginId, JsonNode experiment) { + if (experiment != null) { + this.experimentResultCache.put(generateKey(distinctId, isLoginId), experiment); + } + } + + public long getCacheSize() { + return this.experimentResultCache.size(); + } + + private String generateKey(String distinctId, boolean isLoginId) { + return String.format("%s_%b", distinctId, isLoginId); + } +} diff --git a/src/main/java/com/sensorsdata/analytics/javasdk/exception/HttpStatusException.java b/src/main/java/com/sensorsdata/analytics/javasdk/exception/HttpStatusException.java new file mode 100644 index 0000000..4e976b9 --- /dev/null +++ b/src/main/java/com/sensorsdata/analytics/javasdk/exception/HttpStatusException.java @@ -0,0 +1,27 @@ +package com.sensorsdata.analytics.javasdk.exception; + +/** + * 网络请求异常 + * + * @author fz + * @version 1.0.0 + * @since 2021/06/15 14:04 + */ +public class HttpStatusException extends Exception { + private static final long serialVersionUID = -167873860788060801L; + + private final int httpStatusCode; + + private final String httpContent; + + /** + * Constructs a new exception with {@code null} as its detail message. + * The cause is not initialized, and may subsequently be initialized by a + * call to {@link #initCause}. + */ + public HttpStatusException(String error, int httpStatusCode, String httpContent) { + super(error); + this.httpStatusCode = httpStatusCode; + this.httpContent = httpContent; + } +} diff --git a/src/main/java/com/sensorsdata/analytics/javasdk/util/ABTestUtil.java b/src/main/java/com/sensorsdata/analytics/javasdk/util/ABTestUtil.java new file mode 100644 index 0000000..fccf8cb --- /dev/null +++ b/src/main/java/com/sensorsdata/analytics/javasdk/util/ABTestUtil.java @@ -0,0 +1,28 @@ +package com.sensorsdata.analytics.javasdk.util; + +/** + * AB Test 工具类 + * + * @author fz + * @version 1.0.0 + * @since 2021/06/15 10:28 + */ +public class ABTestUtil { + + private ABTestUtil() { + } + + public static boolean assertDefaultValueType(T value) { + if (value instanceof Integer || value instanceof String || value instanceof Boolean) { + return true; + } + return false; + } + + public static void printLog(boolean enableLog, String errorMessage) { + if (enableLog) { + System.out.printf("%s.%n", errorMessage); + } + } + +} diff --git a/src/main/java/com/sensorsdata/analytics/javasdk/util/HttpUtil.java b/src/main/java/com/sensorsdata/analytics/javasdk/util/HttpUtil.java new file mode 100644 index 0000000..49c3658 --- /dev/null +++ b/src/main/java/com/sensorsdata/analytics/javasdk/util/HttpUtil.java @@ -0,0 +1,65 @@ +package com.sensorsdata.analytics.javasdk.util; + +import com.sensorsdata.analytics.javasdk.exception.HttpStatusException; + +import org.apache.http.client.config.RequestConfig; +import org.apache.http.client.methods.CloseableHttpResponse; +import org.apache.http.client.methods.HttpPost; +import org.apache.http.entity.StringEntity; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.impl.client.HttpClients; +import org.apache.http.util.EntityUtils; + +import java.io.IOException; +import java.nio.charset.StandardCharsets; + +/** + * 网络请求工具类 + * + * @author fz + * @version 1.0.0 + * @since 2021/06/15 18:28 + */ +public class HttpUtil { + + private HttpUtil() { + } + + public static String postABTest(String url, String strJson, int timeoutMilliseconds) + throws IOException, HttpStatusException { + if (timeoutMilliseconds < 0) { + timeoutMilliseconds = 3000; + } + RequestConfig requestConfig = RequestConfig.custom().setConnectTimeout(timeoutMilliseconds) + .setConnectionRequestTimeout(timeoutMilliseconds) + .setSocketTimeout(timeoutMilliseconds) + .setStaleConnectionCheckEnabled(true) + .build(); + CloseableHttpClient httpClient = HttpClients.custom().setDefaultRequestConfig(requestConfig).build(); + HttpPost httpPost = new HttpPost(url); + //设置header + httpPost.setHeader("Content-type", "application/json"); + if (strJson != null && strJson.length() != 0) { + StringEntity param = new StringEntity(strJson, StandardCharsets.UTF_8); + httpPost.setEntity(param); + } + CloseableHttpResponse response = null; + try { + response = httpClient.execute(httpPost); + int httpStatusCode = response.getStatusLine().getStatusCode(); + String httpContent = new String(EntityUtils.toByteArray(response.getEntity()), StandardCharsets.UTF_8); + if (httpStatusCode == 200) { + return httpContent; + } else { + throw new HttpStatusException( + String.format("Unexpected response %d from Sensors AB Test: %s", httpStatusCode, httpContent), + httpStatusCode, httpContent); + } + } finally { + if (response != null) { + response.close(); + } + httpClient.close(); + } + } +} diff --git a/src/test/java/com/sensorsdata/analytics/javasdk/HttpUtilTest.java b/src/test/java/com/sensorsdata/analytics/javasdk/HttpUtilTest.java new file mode 100644 index 0000000..a96809a --- /dev/null +++ b/src/test/java/com/sensorsdata/analytics/javasdk/HttpUtilTest.java @@ -0,0 +1,46 @@ +package com.sensorsdata.analytics.javasdk; + +import static org.junit.Assert.assertEquals; + +import com.sensorsdata.analytics.javasdk.exception.HttpStatusException; +import com.sensorsdata.analytics.javasdk.util.HttpUtil; +import com.sensorsdata.analytics.javasdk.util.SensorsAnalyticsUtil; + +import com.fasterxml.jackson.databind.JsonNode; +import org.junit.Test; + +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; + +/** + * 网络请求工具类测试 + * + * @author fz + * @version 1.0.0 + * @since 2021/06/18 09:54 + */ +public class HttpUtilTest { + + String url = + "http://abtesting.saas.debugbox.sensorsdata.cn/api/v2/abtest/online/results?project-key=438B9364C98D54371751BA82F6484A1A03A5155E"; + + /*** + * 网络请求带参数测试 + * @throws IOException + * @throws com.sensorsdata.analytics.javasdk.exception.HttpStatusException + */ + @Test(timeout = 3000) + public void postABTestWithParams() throws IOException, HttpStatusException { + Map params = new HashMap(); + params.put("login_id", "AB123456"); + params.put("platform", SensorsABTestConst.PLATFORM); + params.put("abtest_lib_version", SensorsABTestConst.VERSION); + String str = + HttpUtil.postABTest(url, SensorsAnalyticsUtil.getJsonObjectMapper().writeValueAsString(params), 3000); + JsonNode jsonNode = SensorsAnalyticsUtil.getJsonObjectMapper().readTree(str); + assertEquals(SensorsABTestConst.SUCCESS, jsonNode.findValue(SensorsABTestConst.STATUS_KEY).asText()); + } + + +} diff --git a/src/test/java/com/sensorsdata/analytics/javasdk/SensorsTest.java b/src/test/java/com/sensorsdata/analytics/javasdk/SensorsTest.java new file mode 100644 index 0000000..c52fbb5 --- /dev/null +++ b/src/test/java/com/sensorsdata/analytics/javasdk/SensorsTest.java @@ -0,0 +1,2140 @@ +package com.sensorsdata.analytics.javasdk; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; + +import com.google.common.cache.LoadingCache; +import com.sensorsdata.analytics.javasdk.bean.ABGlobalConfig; +import com.sensorsdata.analytics.javasdk.bean.Experiment; +import com.sensorsdata.analytics.javasdk.cache.EventCacheManager; +import com.sensorsdata.analytics.javasdk.cache.ExperimentCacheManager; +import com.sensorsdata.analytics.javasdk.consumer.BatchConsumer; +import com.sensorsdata.analytics.javasdk.consumer.ConcurrentLoggingConsumer; +import com.sensorsdata.analytics.javasdk.exceptions.InvalidArgumentException; + +import com.fasterxml.jackson.databind.ObjectMapper; +import org.junit.*; +import org.junit.runner.JUnitCore; +import org.junit.runner.Result; +import org.junit.runner.RunWith; +import org.junit.runner.notification.Failure; +import org.junit.runners.Suite; + +import java.io.IOException; +import java.lang.reflect.Field; +import java.util.HashMap; +import java.util.Map; + +/** + * 顶层接口,对外暴露的方法测试类 + * + * @author fz + * @version 1.0.0 + * @since 2021/06/18 09:54 + */ + + +public class SensorsTest { + + public SensorsTest() throws IOException, InvalidArgumentException { + } + + //初始化神策分析 SDK + final ISensorsAnalytics sa = new SensorsAnalytics(new ConcurrentLoggingConsumer("file.log")); +// String serverUrl = "http://localhost/sa?project=default"; +// final ISensorsAnalytics sa = new SensorsAnalytics(new BatchConsumer(serverUrl)); + + String url = "http://localhost/sa?project=default"; + + //构建配置 AB Testing 试验全局参数 + ABGlobalConfig abGlobalConfig = ABGlobalConfig.builder() + .setApiUrl(url) //分流试验地址 + .setSensorsAnalytics(sa) //神策分析 SDK 实例 + .enableEventCache(true) + .build(); + //匿名ID 或者用户登录 ID,配合 isLoginId 使用 +// String distinctId = "xc123456"; + String distinctId = "AB123456"; + //试验名 + String experimentName = "int"; + + + //execute for each test, before executing test + @Before + public void before() throws NoSuchFieldException, IllegalAccessException { + System.out.println("in before"); + final ISensorsABTest abTest = new SensorsABTest(abGlobalConfig); + ExperimentCacheManager cacheManager = + getExperimentCacheManagerByReflect(getSensorsABTestWorkerByReflect(abTest)); + getExperimentResultCacheByReflect(cacheManager).invalidateAll(); //强制清除实验缓存 + + EventCacheManager eventCacheManager = + getEventCacheManagerByReflect(getSensorsABTestWorkerByReflect(abTest)); + + getEventCacheByReflect(eventCacheManager).invalidateAll(); //强制清除事件缓存 + + } + + //execute for each test, after executing test + @After + public void after() { + System.out.println("in after"); + } + + /** + * 判断全局默认参数设置 + */ + @Test + public void globalParam() { + assertTrue(abGlobalConfig.getEventCacheSize() == 4096); + assertTrue(abGlobalConfig.getEventCacheTime() == 1440); + assertTrue(abGlobalConfig.getExperimentCacheSize() == 4096); + assertTrue(abGlobalConfig.getExperimentCacheTime() == 1440); + assertTrue(abGlobalConfig.getEnableEventCache()); + } + + /** + * 立即调用,返回试验JSON类型,SDK JSON类型为JSON 字符串类型。 + */ + @Test + public void asyncFetchABTestReturnRightResult() throws IOException, InvalidArgumentException { + + //初始化 AB Testing SDK + final ISensorsABTest abTest = new SensorsABTest(abGlobalConfig); +// Experiment result = abTest.asyncFetchABTest(distinctId, false, experimentName, "{\"color\":\"grey\"}"); + Experiment result = abTest.asyncFetchABTest(distinctId, false, experimentName, -1); + System.out.println(" ========== " + result.getResult()); + assertNotNull(result); + assertNotNull(result.getDistinctId()); + assertNotNull(result.getIsLoginId()); + assertNotNull(result.getResult()); + assertTrue(result.getResult() != null); + } + + /** + * 立即调用,返回试验JSON类型,SDK JSON类型为JSON 字符串类型。 + */ + @Test + public void asyncFetchABTestReturnTimeoutResult() throws IOException, InvalidArgumentException { + //构建配置 AB Testing 试验全局参数 + ABGlobalConfig abGlobalConfig = ABGlobalConfig.builder() + .setApiUrl("http://localhost:8080/timeoutTest") //分流试验地址 + .setSensorsAnalytics(sa) //神策分析 SDK 实例 + .enableEventCache(true) + .build(); + + //初始化 AB Testing SDK + final ISensorsABTest abTest = new SensorsABTest(abGlobalConfig); +// Experiment result = abTest.asyncFetchABTest(distinctId, false, experimentName, "{\"color\":\"grey\"}"); + Experiment result = abTest.asyncFetchABTest(distinctId, false, experimentName, -1); + System.out.println(" ========== " + result.getResult()); + assertNotNull(result); + assertNotNull(result.getDistinctId()); + assertNotNull(result.getIsLoginId()); + assertNotNull(result.getResult()); + assertTrue(result.getResult() != null); + } + + /** + * 立即调用,返回试验JSON类型,SDK JSON类型为JSON 字符串类型。 + */ + @Test + public void asyncFetchABTestReturnTimeout_1() throws IOException, InvalidArgumentException { + //构建配置 AB Testing 试验全局参数 + ABGlobalConfig abGlobalConfig = ABGlobalConfig.builder() + .setApiUrl("http://localhost:8080/timeoutTest") //分流试验地址 + .setSensorsAnalytics(sa) //神策分析 SDK 实例 + .enableEventCache(true) + .build(); + + //初始化 AB Testing SDK + final ISensorsABTest abTest = new SensorsABTest(abGlobalConfig); +// Experiment result = abTest.asyncFetchABTest(distinctId, false, experimentName, "{\"color\":\"grey\"}"); + Experiment result = abTest.asyncFetchABTest(distinctId, false, experimentName, -1, -1); + System.out.println(" ========== " + result.getResult()); + assertNotNull(result); + assertNotNull(result.getDistinctId()); + assertNotNull(result.getIsLoginId()); + assertNotNull(result.getResult()); + assertTrue(result.getResult() != null); + } + + /** + * 立即调用,返回试验JSON类型,SDK JSON类型为JSON 字符串类型。 + */ + @Test + public void fastFetchABTest_asyncFetchABTest() throws IOException, InvalidArgumentException { + //构建配置 AB Testing 试验全局参数 + ABGlobalConfig abGlobalConfig = ABGlobalConfig.builder() + .setApiUrl(url) //分流试验地址 + .setSensorsAnalytics(sa) //神策分析 SDK 实例 + .enableEventCache(true) + .build(); + + //初始化 AB Testing SDK + final ISensorsABTest abTest = new SensorsABTest(abGlobalConfig); + + Experiment result = abTest.fastFetchABTest(distinctId, false, experimentName, -1); + System.out.println("========== " + result.getResult()); + + result = abTest.asyncFetchABTest(distinctId, false, experimentName, -1); + System.out.println("========== " + result.getResult()); + } + + /** + * 验证配置有效值后,初始化正确后调用 AsyncFetchABTest 且缓存不存储 + */ + @Test + public void asyncFetchABTesReturnResultJson() throws InvalidArgumentException, IOException, NoSuchFieldException, IllegalAccessException{ + //初始化 AB Testing SDK + final ISensorsABTest abTest = new SensorsABTest(abGlobalConfig); +// Experiment result = abTest.asyncFetchABTest(distinctId, false, experimentName, "grey", false); + String experimentName = "color"; + Experiment result = abTest.asyncFetchABTest(distinctId, false, experimentName, "{\"color\":\"grey\"}", false); + System.out.println("======= " + result.getResult()); + + assertEquals(distinctId, result.getDistinctId()); + assertEquals(false, result.getIsLoginId()); + assertEquals("102",result.getAbTestExperimentId()); + assertEquals("0",result.getAbTestExperimentGroupId()); + Assert.assertTrue(result.getControlGroup()); + Assert.assertTrue(result.getWhiteList()); + + // + ExperimentCacheManager cacheManager = + getExperimentCacheManagerByReflect(getSensorsABTestWorkerByReflect(abTest)); + + assertEquals(0, cacheManager.getCacheSize()); + + assertNotNull(result.getResult()); + } + + /** + * 验证配置有效值后,初始化正确后调用 fastFetchABTest 且缓存不存储 + */ + @Test + public void fastFetchABTestReturnResultJson() throws InvalidArgumentException, IOException, NoSuchFieldException, IllegalAccessException{ + //初始化 AB Testing SDK + final ISensorsABTest abTest = new SensorsABTest(abGlobalConfig); +// Experiment result = abTest.asyncFetchABTest(distinctId, false, experimentName, "grey", false); + String experimentName = "color"; + Experiment result = abTest.fastFetchABTest(distinctId, false, experimentName, "{\"color\":\"grey\"}", false); + System.out.println("======= result: " + result.getResult()); + + assertEquals(distinctId, result.getDistinctId()); + assertEquals(false, result.getIsLoginId()); + assertEquals("102",result.getAbTestExperimentId()); + assertEquals("0",result.getAbTestExperimentGroupId()); + Assert.assertTrue(result.getControlGroup()); + Assert.assertTrue(result.getWhiteList()); + + // + ExperimentCacheManager cacheManager = + getExperimentCacheManagerByReflect(getSensorsABTestWorkerByReflect(abTest)); + + assertEquals(1, cacheManager.getCacheSize()); + + assertNotNull(result.getResult()); + } + + /** + * 验证配置有效值后,初始化正确后调用 AsyncFetchABTest 且缓存不存储 + */ + @Test + public void asyncFetchABTesReturnResultInt() throws InvalidArgumentException, IOException, NoSuchFieldException, IllegalAccessException{ + //初始化 AB Testing SDK + final ISensorsABTest abTest = new SensorsABTest(abGlobalConfig); +// Experiment result = abTest.asyncFetchABTest(distinctId, false, experimentName, "grey", false); + String experimentName = "test_int"; + Experiment result = abTest.asyncFetchABTest(distinctId, false, experimentName, -1, false); + System.out.println("======= result: " + result.getResult()); + + assertEquals(distinctId, result.getDistinctId()); + assertEquals(false, result.getIsLoginId()); + assertEquals("102",result.getAbTestExperimentId()); + assertEquals("0",result.getAbTestExperimentGroupId()); + Assert.assertTrue(result.getControlGroup()); + Assert.assertTrue(result.getWhiteList()); + + // + ExperimentCacheManager cacheManager = + getExperimentCacheManagerByReflect(getSensorsABTestWorkerByReflect(abTest)); + + assertEquals(0, cacheManager.getCacheSize()); + + assertNotNull(result.getResult()); + } + + /** + * 验证配置有效值后,初始化正确后调用 fastFetchABTest 且缓存不存储 + */ + @Test + public void fastFetchABTestReturnResultInt() throws InvalidArgumentException, IOException, NoSuchFieldException, IllegalAccessException{ + //初始化 AB Testing SDK + final ISensorsABTest abTest = new SensorsABTest(abGlobalConfig); +// Experiment result = abTest.asyncFetchABTest(distinctId, false, experimentName, "grey", false); + String experimentName = "test_int"; + Experiment result = abTest.fastFetchABTest(distinctId, false, experimentName, -1, false); + System.out.println("======= result: " + result.getResult()); + + assertEquals(distinctId, result.getDistinctId()); + assertEquals(false, result.getIsLoginId()); + assertEquals("102",result.getAbTestExperimentId()); + assertEquals("0",result.getAbTestExperimentGroupId()); + Assert.assertTrue(result.getControlGroup()); + Assert.assertTrue(result.getWhiteList()); + + // + ExperimentCacheManager cacheManager = + getExperimentCacheManagerByReflect(getSensorsABTestWorkerByReflect(abTest)); + + assertEquals(1, cacheManager.getCacheSize()); + + assertNotNull(result.getResult()); + } + + /** + * 验证配置有效值后,初始化正确后调用 AsyncFetchABTest 且缓存不存储 + */ + @Test + public void asyncFetchABTesReturnResultStr() throws InvalidArgumentException, IOException, NoSuchFieldException, IllegalAccessException{ + //初始化 AB Testing SDK + final ISensorsABTest abTest = new SensorsABTest(abGlobalConfig); +// Experiment result = abTest.asyncFetchABTest(distinctId, false, experimentName, "grey", false); + String experimentName = "test_str"; + Experiment result = abTest.asyncFetchABTest(distinctId, false, experimentName, "...", false); + System.out.println("======= result: " + result.getResult()); + + assertEquals(distinctId, result.getDistinctId()); + assertEquals(false, result.getIsLoginId()); + assertEquals("102",result.getAbTestExperimentId()); + assertEquals("0",result.getAbTestExperimentGroupId()); + Assert.assertTrue(result.getControlGroup()); + Assert.assertTrue(result.getWhiteList()); + + // + ExperimentCacheManager cacheManager = + getExperimentCacheManagerByReflect(getSensorsABTestWorkerByReflect(abTest)); + + assertEquals(0, cacheManager.getCacheSize()); + + assertNotNull(result.getResult()); + } + + /** + * 验证配置有效值后,初始化正确后调用 fastFetchABTest 且缓存不存储 + */ + @Test + public void fastFetchABTestReturnResultStr() throws InvalidArgumentException, IOException, NoSuchFieldException, IllegalAccessException{ + //初始化 AB Testing SDK + final ISensorsABTest abTest = new SensorsABTest(abGlobalConfig); +// Experiment result = abTest.asyncFetchABTest(distinctId, false, experimentName, "grey", false); + String experimentName = "test_str"; + Experiment result = abTest.fastFetchABTest(distinctId, false, experimentName, "...", false); + System.out.println("======= result: " + result.getResult()); + + assertEquals(distinctId, result.getDistinctId()); + assertEquals(false, result.getIsLoginId()); + assertEquals("102",result.getAbTestExperimentId()); + assertEquals("0",result.getAbTestExperimentGroupId()); + Assert.assertTrue(result.getControlGroup()); + Assert.assertTrue(result.getWhiteList()); + + // + ExperimentCacheManager cacheManager = + getExperimentCacheManagerByReflect(getSensorsABTestWorkerByReflect(abTest)); + + assertEquals(1, cacheManager.getCacheSize()); + + assertNotNull(result.getResult()); + } + + /** + * 验证配置有效值后,初始化正确后调用 AsyncFetchABTest 且缓存不存储 + */ + @Test + public void asyncFetchABTesReturnResultBool() throws InvalidArgumentException, IOException, NoSuchFieldException, IllegalAccessException{ + //初始化 AB Testing SDK + final ISensorsABTest abTest = new SensorsABTest(abGlobalConfig); + String experimentName = "test_bool"; + Experiment result = abTest.asyncFetchABTest(distinctId, false, experimentName, false, false); + System.out.println("======= result: " + result.getResult()); + + assertEquals(distinctId, result.getDistinctId()); + assertEquals(false, result.getIsLoginId()); + assertEquals("102",result.getAbTestExperimentId()); + assertEquals("0",result.getAbTestExperimentGroupId()); + Assert.assertTrue(result.getControlGroup()); + Assert.assertTrue(result.getWhiteList()); + + // + ExperimentCacheManager cacheManager = + getExperimentCacheManagerByReflect(getSensorsABTestWorkerByReflect(abTest)); + + assertEquals(0, cacheManager.getCacheSize()); + + assertNotNull(result.getResult()); + } + + /** + * 验证配置有效值后,初始化正确后调用 fastFetchABTest 且缓存不存储 + */ + @Test + public void fastFetchABTestReturnResultBool() throws InvalidArgumentException, IOException, NoSuchFieldException, IllegalAccessException{ + //初始化 AB Testing SDK + final ISensorsABTest abTest = new SensorsABTest(abGlobalConfig); + String experimentName = "test_bool"; + Experiment result = abTest.fastFetchABTest(distinctId, false, experimentName, false, false); + System.out.println("======= result: " + result.getResult()); + + assertEquals(distinctId, result.getDistinctId()); + assertEquals(false, result.getIsLoginId()); + assertEquals("102",result.getAbTestExperimentId()); + assertEquals("0",result.getAbTestExperimentGroupId()); + Assert.assertTrue(result.getControlGroup()); + Assert.assertTrue(result.getWhiteList()); + + // + ExperimentCacheManager cacheManager = + getExperimentCacheManagerByReflect(getSensorsABTestWorkerByReflect(abTest)); + + assertEquals(1, cacheManager.getCacheSize()); + + assertNotNull(result.getResult()); + } + + /** + * 验证断网情况下调用 AsyncFetchABTest 返回默认值 + */ + @Test + public void asyncFetchABTest02() throws IOException, InvalidArgumentException { + //初始化 AB Testing SDK + final ISensorsABTest abTest = new SensorsABTest(abGlobalConfig); + Experiment result = abTest.asyncFetchABTest(distinctId, false, experimentName, -1, false); + System.out.println("======= " + result.getResult()); + + assertEquals(distinctId, result.getDistinctId()); + assertEquals(false, result.getIsLoginId()); + assertNotNull(result.getResult()); + } + + /** + * 验证断网情况下调用 AsyncFetchABTest 返回默认值 + */ + @Test + public void asyncFetchABTest03() throws IOException, InvalidArgumentException { + + //初始化 AB Testing SDK + final ISensorsABTest abTest = new SensorsABTest(abGlobalConfig); + Experiment result = abTest.asyncFetchABTest(distinctId, false, experimentName, -1, false); + System.out.println("======= " + result.getResult()); + + assertEquals(distinctId, result.getDistinctId()); + assertEquals(false, result.getIsLoginId()); + assertNotNull(result.getResult()); + } + + /** + * 验证断网情况下调用 AsyncFetchABTest 返回默认值 + */ + @Test + public void asyncFetchABTest04() throws IOException, InvalidArgumentException { + + //初始化 AB Testing SDK + final ISensorsABTest abTest = new SensorsABTest(abGlobalConfig); + Experiment result = abTest.asyncFetchABTest(distinctId, false, experimentName, -1, false); + System.out.println("======= " + result.getResult()); + + assertEquals(distinctId, result.getDistinctId()); + assertEquals(false, result.getIsLoginId()); + assertNotNull(result.getResult()); + } + + + /** + * AsyncFetchABTest 参数值 - isLoginId=false + */ + @Test + public void asyncFetchABTest05() throws IOException, InvalidArgumentException { + + //初始化 AB Testing SDK + final ISensorsABTest abTest = new SensorsABTest(abGlobalConfig); + Experiment result = abTest.asyncFetchABTest(distinctId, false, experimentName, -1, false); + System.out.println("======= " + result.getResult()); + + assertEquals(distinctId, result.getDistinctId()); + assertEquals(false, result.getIsLoginId()); + assertNotNull(result.getResult()); + } + + /** + * AsyncFetchABTest 参数值 - isLoginId=true + */ + @Test + public void asyncFetchABTest06() throws IOException, InvalidArgumentException, NoSuchFieldException, IllegalAccessException { + + //初始化 AB Testing SDK + final ISensorsABTest abTest = new SensorsABTest(abGlobalConfig); + Experiment result = abTest.asyncFetchABTest(distinctId, true, experimentName, -1, false); + System.out.println("======= " + result.getResult()); + + assertEquals(distinctId, result.getDistinctId()); + assertEquals(true, result.getIsLoginId()); + assertEquals("115",result.getAbTestExperimentId()); + assertEquals("1",result.getAbTestExperimentGroupId()); + Assert.assertFalse(result.getControlGroup()); + Assert.assertFalse(result.getWhiteList()); + + // + ExperimentCacheManager cacheManager = + getExperimentCacheManagerByReflect(getSensorsABTestWorkerByReflect(abTest)); + + assertEquals(0, cacheManager.getCacheSize()); + + assertNotNull(result.getResult()); + } + + + /** + * AsyncFetchABTest 参数值为异常值- distinctId 超长 + */ + @Test + public void asyncFetchABTest07() throws IOException, InvalidArgumentException, NoSuchFieldException, IllegalAccessException { + + //初始化 AB Testing SDK + + String distinctId = "123666664444444444444444444666666666666"; + + final ISensorsABTest abTest = new SensorsABTest(abGlobalConfig); + Experiment result = abTest.asyncFetchABTest(distinctId, true, experimentName, -1, false); + System.out.println("======= " + result.getResult()); + + assertEquals(distinctId, result.getDistinctId()); + assertEquals(true, result.getIsLoginId()); + assertEquals("115",result.getAbTestExperimentId()); + assertEquals("1",result.getAbTestExperimentGroupId()); + Assert.assertFalse(result.getControlGroup()); + Assert.assertFalse(result.getWhiteList()); + + // + ExperimentCacheManager cacheManager = + getExperimentCacheManagerByReflect(getSensorsABTestWorkerByReflect(abTest)); + + assertEquals(0, cacheManager.getCacheSize()); + + assertNotNull(result.getResult()); + } + + + /** + * AsyncFetchABTest 参数值为异常值- distinctId = null + */ + @Test + public void asyncFetchABTest08() throws IOException, InvalidArgumentException, NoSuchFieldException, IllegalAccessException { + + //初始化 AB Testing SDK + + String distinctId = null; + + final ISensorsABTest abTest = new SensorsABTest(abGlobalConfig); + Experiment result = abTest.asyncFetchABTest(distinctId, true, experimentName, -1, false); + System.out.println("======= " + result.getResult()); + + assertEquals(distinctId, result.getDistinctId()); + assertEquals(true, result.getIsLoginId()); + assertEquals("115",result.getAbTestExperimentId()); + assertEquals("1",result.getAbTestExperimentGroupId()); + Assert.assertFalse(result.getControlGroup()); + Assert.assertFalse(result.getWhiteList()); + + // + ExperimentCacheManager cacheManager = + getExperimentCacheManagerByReflect(getSensorsABTestWorkerByReflect(abTest)); + + assertEquals(0, cacheManager.getCacheSize()); + + assertNotNull(result.getResult()); + } + + /** + * AsyncFetchABTest 参数值为异常值- distinctId = "" + */ + @Test + public void asyncFetchABTest09() throws IOException, InvalidArgumentException, NoSuchFieldException, IllegalAccessException { + + //初始化 AB Testing SDK + + String distinctId = ""; + + final ISensorsABTest abTest = new SensorsABTest(abGlobalConfig); + Experiment result = abTest.asyncFetchABTest(distinctId, true, experimentName, -1, false); + System.out.println("======= " + result.getResult()); + + assertEquals(distinctId, result.getDistinctId()); + assertEquals(true, result.getIsLoginId()); + assertEquals("115",result.getAbTestExperimentId()); + assertEquals("1",result.getAbTestExperimentGroupId()); + Assert.assertFalse(result.getControlGroup()); + Assert.assertFalse(result.getWhiteList()); + + // + ExperimentCacheManager cacheManager = + getExperimentCacheManagerByReflect(getSensorsABTestWorkerByReflect(abTest)); + + assertEquals(0, cacheManager.getCacheSize()); + assertNotNull(result.getResult()); + } + + /** + * fastFetchABTest 参数值 - isLoginId=false + */ + @Test + public void fastFetchABTest05() throws IOException, InvalidArgumentException { + + //初始化 AB Testing SDK + final ISensorsABTest abTest = new SensorsABTest(abGlobalConfig); + Experiment result = abTest.fastFetchABTest(distinctId, false, experimentName, -1, false); + System.out.println("======= " + result.getResult()); + + assertEquals(distinctId, result.getDistinctId()); + assertEquals(false, result.getIsLoginId()); + assertNotNull(result.getResult()); + } + + /** + * fastFetchABTest 参数值 - isLoginId=true + */ + @Test + public void fastFetchABTest06() throws IOException, InvalidArgumentException, NoSuchFieldException, IllegalAccessException { + //初始化 AB Testing SDK + final ISensorsABTest abTest = new SensorsABTest(abGlobalConfig); + ExperimentCacheManager cacheManager = + getExperimentCacheManagerByReflect(getSensorsABTestWorkerByReflect(abTest)); + Experiment result = abTest.fastFetchABTest(distinctId, true, experimentName, -1, false); + System.out.println("==== cacheManager ==== " + cacheManager.getExperimentResultByCache(distinctId, true, experimentName)); + System.out.println("==== result ==== " + result.getResult()); + + assertEquals(distinctId, result.getDistinctId()); + assertEquals(true, result.getIsLoginId()); + assertEquals("115",result.getAbTestExperimentId()); + assertEquals("1",result.getAbTestExperimentGroupId()); + Assert.assertFalse(result.getControlGroup()); + Assert.assertFalse(result.getWhiteList()); + + + assertEquals(0, cacheManager.getCacheSize()); + assertNotNull(result.getResult()); + } + + + /** + * fastFetchABTest 参数值为异常值- distinctId 超长 + */ + @Test + public void fastFetchABTest07() throws IOException, InvalidArgumentException, NoSuchFieldException, IllegalAccessException { + + //初始化 AB Testing SDK + + String distinctId = "123666664444444444444444444666666666666"; + + final ISensorsABTest abTest = new SensorsABTest(abGlobalConfig); + Experiment result = abTest.fastFetchABTest(distinctId, true, experimentName, -1, false); + System.out.println("======= " + result.getResult()); + + assertEquals(distinctId, result.getDistinctId()); + assertEquals(true, result.getIsLoginId()); + assertEquals("115",result.getAbTestExperimentId()); + assertEquals("1",result.getAbTestExperimentGroupId()); + Assert.assertFalse(result.getControlGroup()); + Assert.assertFalse(result.getWhiteList()); + + // + ExperimentCacheManager cacheManager = + getExperimentCacheManagerByReflect(getSensorsABTestWorkerByReflect(abTest)); + + assertEquals(0, cacheManager.getCacheSize()); + + assertNotNull(result.getResult()); + } + + + /** + * fastFetchABTest 参数值为异常值- distinctId = null + */ + @Test + public void fastFetchABTest08() throws IOException, InvalidArgumentException, NoSuchFieldException, IllegalAccessException { + + //初始化 AB Testing SDK + + String distinctId = null; + + final ISensorsABTest abTest = new SensorsABTest(abGlobalConfig); + Experiment result = abTest.fastFetchABTest(distinctId, true, experimentName, -1, false); + } + + /** + * fastFetchABTest 参数值为异常值- distinctId = "" + */ + @Test + public void fastFetchABTest09() throws IOException, InvalidArgumentException, NoSuchFieldException, IllegalAccessException { + + //初始化 AB Testing SDK + + String distinctId = ""; + + final ISensorsABTest abTest = new SensorsABTest(abGlobalConfig); + Experiment result = abTest.fastFetchABTest(distinctId, true, experimentName, -1, false); + } + + /** + * 当用户不在白名单内——首次触发 asyncFetchABTest + */ + @Test + public void asyncFetchABTestFirst() throws IOException, InvalidArgumentException, NoSuchFieldException, IllegalAccessException { + //初始化 AB Testing SDK + final ISensorsABTest abTest = new SensorsABTest(abGlobalConfig); +// Experiment result = abTest.asyncFetchABTest(distinctId, false, experimentName, "grey", false); + Experiment result = abTest.asyncFetchABTest(distinctId, false, experimentName, -1, true); + System.out.println("======= " + result.getResult()); + + assertEquals(distinctId, result.getDistinctId()); + assertEquals(false, result.getIsLoginId()); + assertEquals("115",result.getAbTestExperimentId()); + assertEquals("1",result.getAbTestExperimentGroupId()); + Assert.assertFalse(result.getControlGroup()); + Assert.assertFalse(result.getWhiteList()); + + // + ExperimentCacheManager cacheManager = + getExperimentCacheManagerByReflect(getSensorsABTestWorkerByReflect(abTest)); + + assertEquals(0, cacheManager.getCacheSize()); + + assertNotNull(result.getResult()); + } + + /** + * 当用户不在白名单内——首次触发 fastFetchABTest + */ + @Test + public void fastFetchABTestFirst() throws IOException, InvalidArgumentException, NoSuchFieldException, IllegalAccessException { + //初始化 AB Testing SDK + final ISensorsABTest abTest = new SensorsABTest(abGlobalConfig); +// Experiment result = abTest.asyncFetchABTest(distinctId, false, experimentName, "grey", false); + Experiment result = abTest.fastFetchABTest(distinctId, false, experimentName, -1, true); + System.out.println("======= " + result.getResult()); + +// assertEquals(distinctId, result.getDistinctId()); +// assertEquals(false, result.getIsLoginId()); +// assertEquals("115",result.getAbTestExperimentId()); +// assertEquals("1",result.getAbTestExperimentGroupId()); +// Assert.assertFalse(result.getControlGroup()); +// Assert.assertFalse(result.getWhiteList()); + +// ExperimentCacheManager cacheManager = +// getExperimentCacheManagerByReflect(getSensorsABTestWorkerByReflect(abTest)); +// +// assertEquals(0, cacheManager.getCacheSize()); +// +// assertNotNull(result.getResult()); + + // 第二次请求 + result = abTest.fastFetchABTest(distinctId, false, experimentName, -1, true); + System.out.println("======= " + result.getResult()); + } + + /** + * experimentVariableName = null + */ + @Test + public void nullExperimentVariableNameF() throws IOException, InvalidArgumentException, NoSuchFieldException, IllegalAccessException { + //初始化 AB Testing SDK + final ISensorsABTest abTest = new SensorsABTest(abGlobalConfig); +// Experiment result = abTest.asyncFetchABTest(distinctId, false, experimentName, "grey", false); + Experiment result = abTest.fastFetchABTest(distinctId, false, null, -1, true); + System.out.println("======= " + result.getResult()); + } + + /** + * experimentVariableName = null + */ + @Test + public void nullExperimentVariableNameA() throws IOException, InvalidArgumentException, NoSuchFieldException, IllegalAccessException { + //初始化 AB Testing SDK + final ISensorsABTest abTest = new SensorsABTest(abGlobalConfig); +// Experiment result = abTest.asyncFetchABTest(distinctId, false, experimentName, "grey", false); + Experiment result = abTest.asyncFetchABTest(distinctId, false, null, -1, true); + System.out.println("======= " + result.getResult()); + } + + /** + * experimentVariableName = "" + */ + @Test + public void EmptyExperimentVariableNameF() throws IOException, InvalidArgumentException, NoSuchFieldException, IllegalAccessException { + //初始化 AB Testing SDK + final ISensorsABTest abTest = new SensorsABTest(abGlobalConfig); +// Experiment result = abTest.asyncFetchABTest(distinctId, false, experimentName, "grey", false); + Experiment result = abTest.fastFetchABTest(distinctId, false, "", -1, true); + System.out.println("======= " + result.getResult()); + } + + /** + * experimentVariableName = "" + */ + @Test + public void EmptyExperimentVariableNameA() throws IOException, InvalidArgumentException, NoSuchFieldException, IllegalAccessException { + //初始化 AB Testing SDK + final ISensorsABTest abTest = new SensorsABTest(abGlobalConfig); +// Experiment result = abTest.asyncFetchABTest(distinctId, false, experimentName, "grey", false); + Experiment result = abTest.asyncFetchABTest(distinctId, false, "", -1, true); + System.out.println("======= " + result.getResult()); + } + + /** + * experimentVariableName = long string + */ + @Test + public void longStringExperimentVariableNameF() throws IOException, InvalidArgumentException, NoSuchFieldException, IllegalAccessException { + //初始化 AB Testing SDK + final ISensorsABTest abTest = new SensorsABTest(abGlobalConfig); +// Experiment result = abTest.asyncFetchABTest(distinctId, false, experimentName, "grey", false); + Experiment result = abTest.fastFetchABTest(distinctId, false, "12311111111111111111111111111111111111111111111", -1, true); + System.out.println("======= " + result.getResult()); + } + + /** + * experimentVariableName = long string + */ + @Test + public void longStringExperimentVariableNameA() throws IOException, InvalidArgumentException, NoSuchFieldException, IllegalAccessException { + //初始化 AB Testing SDK + final ISensorsABTest abTest = new SensorsABTest(abGlobalConfig); +// Experiment result = abTest.asyncFetchABTest(distinctId, false, experimentName, "grey", false); + Experiment result = abTest.asyncFetchABTest(distinctId, false, "12311111111111111111111111111111111111111111111", -1, true); + System.out.println("======= " + result.getResult()); + } + + + /** + * 立即调用,默认值与返回值类型不一致,返回默认值,且试验相关字段信息返回为空,只返回默认信息 + */ + @Test + public void asyncFetchABTestReturnDefaultValue() throws IOException, InvalidArgumentException, NoSuchFieldException, IllegalAccessException { + //初始化 AB Testing SDK + final ISensorsABTest abTest = new SensorsABTest(abGlobalConfig); + + String experimentName = "int"; + Experiment result = abTest.asyncFetchABTest(distinctId, false, experimentName, "grey"); + System.out.println(" ==== result ==== " + result.getResult()); + + assertEquals(distinctId, result.getDistinctId()); + assertEquals(false, result.getIsLoginId()); + assertNull(result.getAbTestExperimentGroupId()); + assertNull(result.getAbTestExperimentId()); + assertNull(result.getControlGroup()); + assertNull(result.getWhiteList()); + assertEquals("grey", result.getResult()); + + // + ExperimentCacheManager cacheManager = + getExperimentCacheManagerByReflect(getSensorsABTestWorkerByReflect(abTest)); + + assertEquals(0, cacheManager.getCacheSize()); + } + + /** + * 立即调用,默认值与返回值类型不一致,返回默认值,且试验相关字段信息返回为空,只返回默认信息 + */ + @Test + public void fastFetchABTestReturnDefaultValue() throws IOException, InvalidArgumentException, NoSuchFieldException, IllegalAccessException { + //初始化 AB Testing SDK + final ISensorsABTest abTest = new SensorsABTest(abGlobalConfig); + + String experimentName = "int"; + Experiment result = abTest.fastFetchABTest(distinctId, false, experimentName, "grey"); + System.out.println(" ==== result ==== " + result.getResult()); + + assertEquals(distinctId, result.getDistinctId()); + assertEquals(false, result.getIsLoginId()); + assertNull(result.getAbTestExperimentGroupId()); + assertNull(result.getAbTestExperimentId()); + assertNull(result.getControlGroup()); + assertNull(result.getWhiteList()); + assertEquals("grey", result.getResult()); + + // + ExperimentCacheManager cacheManager = + getExperimentCacheManagerByReflect(getSensorsABTestWorkerByReflect(abTest)); + + assertEquals(1, cacheManager.getCacheSize()); + } + + + /** + * 优先查缓存 + */ + @Test + public void fastFetchABTestReturnResult() throws IOException, InvalidArgumentException { + //初始化 AB Testing SDK + final ISensorsABTest abTest = new SensorsABTest(abGlobalConfig); + Experiment result = abTest.fastFetchABTest(distinctId, false, experimentName, "grey"); + assertNotNull(result); + assertNotNull(result.getDistinctId()); + assertNotNull(result.getIsLoginId()); + assertNotNull(result.getResult()); + } + + /** + * 初始化正确后调用 fastFetchABTest 且试验结果缓存存储正确 + */ + @Test + public void experimentCacheSizeTest() + throws InvalidArgumentException, IOException, NoSuchFieldException, IllegalAccessException { + ABGlobalConfig abGlobalConfig = ABGlobalConfig.builder() + .setExperimentCacheSize(1) //只缓存一条 + .setApiUrl(url) //分流试验地址 + .setSensorsAnalytics(sa) //神策分析 SDK 实例 + .build(); + final ISensorsABTest abTest = new SensorsABTest(abGlobalConfig); + Experiment s1 = abTest.fastFetchABTest("AB123", true, "int", "1"); + Experiment s2 = abTest.fastFetchABTest("AB12234", true, "int", "2"); + ExperimentCacheManager cacheManager = + getExperimentCacheManagerByReflect(getSensorsABTestWorkerByReflect(abTest)); + + assertEquals(1, cacheManager.getCacheSize()); + System.out.println(cacheManager.getExperimentResultByCache("AB123", true, "int")); + System.out.println(cacheManager.getExperimentResultByCache("AB12234", true, "int")); + } + + /** + * 初始化正确后调用 fastFetchABTest 且试验结果缓存存储正确,默认 ExperimentCacheSize 为 4096 + */ + @Test + @Ignore + public void experimentCacheSizeTest_01() + throws InvalidArgumentException, IOException, NoSuchFieldException, IllegalAccessException { + ABGlobalConfig abGlobalConfig = ABGlobalConfig.builder() + .setApiUrl(url) //分流试验地址 + .setSensorsAnalytics(sa) //神策分析 SDK 实例 + .build(); + final ISensorsABTest abTest = new SensorsABTest(abGlobalConfig); + + for (int i = 0; i < 5000; i++){ + abTest.fastFetchABTest("AB12234_" + i, true, "int", "2"); + } + ExperimentCacheManager cacheManager = + getExperimentCacheManagerByReflect(getSensorsABTestWorkerByReflect(abTest)); + System.out.println(cacheManager.getExperimentResultByCache("AB123", true, "int")); + System.out.println(cacheManager.getExperimentResultByCache("AB12234", true, "int")); + assertEquals(4096, cacheManager.getCacheSize()); + } + + //TODO + /** + * 初始化正确后调用 fastFetchABTest 且试验结果缓存存储正确 setEventCacheTime + */ + @Test + public void experimentCacheSizeTest_02() + throws InvalidArgumentException, IOException, NoSuchFieldException, IllegalAccessException, InterruptedException { + ABGlobalConfig abGlobalConfig = ABGlobalConfig.builder() + .setApiUrl(url) //分流试验地址 + .setSensorsAnalytics(sa) //神策分析 SDK 实例 + .setEventCacheTime(1) + .build(); + final ISensorsABTest abTest = new SensorsABTest(abGlobalConfig); + Experiment s1 = abTest.fastFetchABTest("AB123", true, "int", -1); + Thread.sleep(60000); + Experiment s2 = abTest.fastFetchABTest("AB12234", true, "int", -2); + ExperimentCacheManager cacheManager = + getExperimentCacheManagerByReflect(getSensorsABTestWorkerByReflect(abTest)); + } + + /** + * 初始化正确后调用 fastFetchABTest 且试验结果缓存存储正确 setEventCacheSize + */ + @Test@Ignore + public void experimentCacheSizeTest_03() + throws InvalidArgumentException, IOException, NoSuchFieldException, IllegalAccessException, InterruptedException { + ABGlobalConfig abGlobalConfig = ABGlobalConfig.builder() + .setApiUrl(url) //分流试验地址 + .setSensorsAnalytics(sa) //神策分析 SDK 实例 + .setEventCacheSize(100) + .build(); + final ISensorsABTest abTest = new SensorsABTest(abGlobalConfig); + EventCacheManager eventCacheManager = + getEventCacheManagerByReflect(getSensorsABTestWorkerByReflect(abTest)); + + for (int i = 0; i < 200; i++){ + Experiment s2 = abTest.fastFetchABTest("AB12234_" + i, true, "int", -2); + } + + ExperimentCacheManager cacheManager = + getExperimentCacheManagerByReflect(getSensorsABTestWorkerByReflect(abTest)); + + System.out.println("========= EventCacheSize:" + eventCacheManager.getCacheSize()); + } + + /** + * 初始化正确后调用 setApiUrl=null + */ + @Test + public void setApiUrlTest_null() + throws InvalidArgumentException, IOException, NoSuchFieldException, IllegalAccessException, InterruptedException { + ABGlobalConfig abGlobalConfig = ABGlobalConfig.builder() + .setApiUrl(null) //分流试验地址 + .setSensorsAnalytics(sa) //神策分析 SDK 实例 + .setEventCacheSize(100) + .build(); + final ISensorsABTest abTest = new SensorsABTest(abGlobalConfig); + EventCacheManager eventCacheManager = + getEventCacheManagerByReflect(getSensorsABTestWorkerByReflect(abTest)); + + Experiment s2 = abTest.fastFetchABTest("AB12234", true, "int", -2); + + ExperimentCacheManager cacheManager = + getExperimentCacheManagerByReflect(getSensorsABTestWorkerByReflect(abTest)); + + } + + /** + * 初始化正确后调用 setApiUrl=empty + */ + @Test + public void setApiUrlTest_empty() + throws InvalidArgumentException, IOException, NoSuchFieldException, IllegalAccessException, InterruptedException { + ABGlobalConfig abGlobalConfig = ABGlobalConfig.builder() + .setApiUrl("") //分流试验地址 + .setSensorsAnalytics(sa) //神策分析 SDK 实例 + .setEventCacheSize(100) + .build(); + final ISensorsABTest abTest = new SensorsABTest(abGlobalConfig); + EventCacheManager eventCacheManager = + getEventCacheManagerByReflect(getSensorsABTestWorkerByReflect(abTest)); + + Experiment s2 = abTest.fastFetchABTest("AB12234", true, "int", -2); + + ExperimentCacheManager cacheManager = + getExperimentCacheManagerByReflect(getSensorsABTestWorkerByReflect(abTest)); + } + + /** + * 初始化正确后调用 setApiUrl=empty + */ + @Test + public void setApiUrlTest_baidu() + throws InvalidArgumentException, IOException, NoSuchFieldException, IllegalAccessException, InterruptedException { + ABGlobalConfig abGlobalConfig = ABGlobalConfig.builder() + .setApiUrl("www.baidu.com") //分流试验地址 + .setSensorsAnalytics(sa) //神策分析 SDK 实例 + .setEventCacheSize(100) + .build(); + final ISensorsABTest abTest = new SensorsABTest(abGlobalConfig); + EventCacheManager eventCacheManager = + getEventCacheManagerByReflect(getSensorsABTestWorkerByReflect(abTest)); + + Experiment s2 = abTest.fastFetchABTest("AB12234", true, "int", -2); + + ExperimentCacheManager cacheManager = + getExperimentCacheManagerByReflect(getSensorsABTestWorkerByReflect(abTest)); + } + + /** + * 初始化不调用 setSensorsAnalytics + */ + @Test + public void setSensorsAnalytics_01() + throws InvalidArgumentException, IOException, NoSuchFieldException, IllegalAccessException, InterruptedException { + ABGlobalConfig abGlobalConfig = ABGlobalConfig.builder() + .setApiUrl(url) //分流试验地址 + .setEventCacheSize(100) + .build(); + final ISensorsABTest abTest = new SensorsABTest(abGlobalConfig); + EventCacheManager eventCacheManager = + getEventCacheManagerByReflect(getSensorsABTestWorkerByReflect(abTest)); + + Experiment s2 = abTest.fastFetchABTest("AB12234", true, "int", -2); + + ExperimentCacheManager cacheManager = + getExperimentCacheManagerByReflect(getSensorsABTestWorkerByReflect(abTest)); + } + + /** + * 初始化调用 setSensorsAnalytics(null) + */ + @Test + public void setSensorsAnalytics_02() + throws InvalidArgumentException, IOException, NoSuchFieldException, IllegalAccessException, InterruptedException { + ABGlobalConfig abGlobalConfig = ABGlobalConfig.builder() + .setApiUrl(url) //分流试验地址 + .setSensorsAnalytics(null) + .setEventCacheSize(100) + .build(); + final ISensorsABTest abTest = new SensorsABTest(abGlobalConfig); + EventCacheManager eventCacheManager = + getEventCacheManagerByReflect(getSensorsABTestWorkerByReflect(abTest)); + + Experiment s2 = abTest.fastFetchABTest("AB12234", true, "int", -2); + + ExperimentCacheManager cacheManager = + getExperimentCacheManagerByReflect(getSensorsABTestWorkerByReflect(abTest)); + } + + /** + * 初始化调用两次 enableEventCache + */ + @Test + public void setSensorsAnalytics_03() + throws InvalidArgumentException, IOException, NoSuchFieldException, IllegalAccessException, InterruptedException { + ABGlobalConfig abGlobalConfig = ABGlobalConfig.builder() + .setApiUrl(url) //分流试验地址 + .setSensorsAnalytics(sa) + .setEventCacheSize(100) + .enableEventCache(true) + .enableEventCache(false) + .build(); + final ISensorsABTest abTest = new SensorsABTest(abGlobalConfig); + EventCacheManager eventCacheManager = + getEventCacheManagerByReflect(getSensorsABTestWorkerByReflect(abTest)); + + Experiment s2 = abTest.fastFetchABTest("AB12234", true, "int", -2); + + ExperimentCacheManager cacheManager = + getExperimentCacheManagerByReflect(getSensorsABTestWorkerByReflect(abTest)); + + System.out.println("======== CacheSize: " + eventCacheManager.getCacheSize()); + assertEquals(1, eventCacheManager.getCacheSize()); + } + + /** + * 初始化先 enableEventCache(true) 再 enableEventCache(false) + */ + @Test + public void setSensorsAnalytics_04() + throws InvalidArgumentException, IOException, NoSuchFieldException, IllegalAccessException, InterruptedException { + ABGlobalConfig abGlobalConfig = ABGlobalConfig.builder() + .setApiUrl(url) //分流试验地址 + .setSensorsAnalytics(sa) + .setEventCacheSize(100) + .enableEventCache(true) + .build(); + final ISensorsABTest abTest = new SensorsABTest(abGlobalConfig); + EventCacheManager eventCacheManager = + getEventCacheManagerByReflect(getSensorsABTestWorkerByReflect(abTest)); + + Experiment s1 = abTest.fastFetchABTest("AB12234", true, "int", -2); + + ExperimentCacheManager cacheManager = + getExperimentCacheManagerByReflect(getSensorsABTestWorkerByReflect(abTest)); + + System.out.println("======== CacheSize: " + eventCacheManager.getCacheSize()); + assertEquals(1, eventCacheManager.getCacheSize()); + + /** + * + */ + abGlobalConfig = ABGlobalConfig.builder() + .setApiUrl(url) //分流试验地址 + .setSensorsAnalytics(sa) + .setEventCacheSize(100) + .enableEventCache(false) + .build(); + final ISensorsABTest abTest01 = new SensorsABTest(abGlobalConfig); + + EventCacheManager eventCacheManager01 = + getEventCacheManagerByReflect(getSensorsABTestWorkerByReflect(abTest)); + + Experiment s2 = abTest01.fastFetchABTest("AB12234_2", true, "int", -2); + + ExperimentCacheManager cacheManager01 = + getExperimentCacheManagerByReflect(getSensorsABTestWorkerByReflect(abTest)); + + System.out.println("======== CacheSize: " + eventCacheManager.getCacheSize()); + assertEquals(1, eventCacheManager.getCacheSize()); + } + + /** + * 初始化先 enableEventCache(true) 再 enableEventCache(false) + */ + @Test + public void setSensorsAnalytics_05() + throws InvalidArgumentException, IOException, NoSuchFieldException, IllegalAccessException, InterruptedException { + ABGlobalConfig abGlobalConfig = ABGlobalConfig.builder() + .setApiUrl(url) //分流试验地址 + .setSensorsAnalytics(sa) + .setEventCacheSize(100) + .enableEventCache(true) + .build(); + final ISensorsABTest abTest = new SensorsABTest(abGlobalConfig); + EventCacheManager eventCacheManager = + getEventCacheManagerByReflect(getSensorsABTestWorkerByReflect(abTest)); + + Experiment s1 = abTest.fastFetchABTest("AB12234", true, "int", -2); + + ExperimentCacheManager cacheManager = + getExperimentCacheManagerByReflect(getSensorsABTestWorkerByReflect(abTest)); + + System.out.println("======== CacheSize: " + eventCacheManager.getCacheSize()); + assertEquals(1, eventCacheManager.getCacheSize()); + } + + /** + * 初始化先 abGlobalConfig + */ + @Test + public void setSensorsAnalytics_06() + throws InvalidArgumentException, IOException, NoSuchFieldException, IllegalAccessException, InterruptedException { + ABGlobalConfig abGlobalConfig = ABGlobalConfig.builder() + .setApiUrl(url) //分流试验地址 + .setSensorsAnalytics(sa) + .setEventCacheSize(100) + .enableEventCache(true) + .build(); + + final ISensorsABTest abTest = new SensorsABTest(abGlobalConfig); + EventCacheManager eventCacheManager = + getEventCacheManagerByReflect(getSensorsABTestWorkerByReflect(abTest)); + + Experiment s1 = abTest.fastFetchABTest("AB12234", true, "int", -2); + + ExperimentCacheManager cacheManager = + getExperimentCacheManagerByReflect(getSensorsABTestWorkerByReflect(abTest)); + + System.out.println("======== CacheSize: " + eventCacheManager.getCacheSize()); + assertEquals(1, eventCacheManager.getCacheSize()); + } + + /** + * 初始化先 abGlobalConfig + */ + @Test + public void asyncFetchABTest_07() + throws InvalidArgumentException, IOException, NoSuchFieldException, IllegalAccessException, InterruptedException { + ABGlobalConfig abGlobalConfig = ABGlobalConfig.builder() + .setApiUrl(url) //分流试验地址 + .setSensorsAnalytics(sa) + .setEventCacheSize(100) + .enableEventCache(true) + .build(); + + final ISensorsABTest abTest = new SensorsABTest(abGlobalConfig); + EventCacheManager eventCacheManager = + getEventCacheManagerByReflect(getSensorsABTestWorkerByReflect(abTest)); + + Experiment s1 = abTest.asyncFetchABTest("AB12234", true, "int", -2); + + ExperimentCacheManager cacheManager = + getExperimentCacheManagerByReflect(getSensorsABTestWorkerByReflect(abTest)); + + System.out.println("======== CacheSize: " + eventCacheManager.getCacheSize()); + assertEquals(1, eventCacheManager.getCacheSize()); + } + + /** + * 开启事件缓存,asyncFetchABTest 验证事件缓存存储正确 + */ + @Test + public void openEventCacheAsyncTest() + throws IOException, InvalidArgumentException, NoSuchFieldException, IllegalAccessException { + final ISensorsABTest abTest = new SensorsABTest(abGlobalConfig); + Experiment result = abTest.asyncFetchABTest("AB123", true, "btn_type", "eee"); + EventCacheManager eventCacheManager = getEventCacheManagerByReflect(getSensorsABTestWorkerByReflect(abTest)); + assertEquals(1, eventCacheManager.getCacheSize()); + } + + /** + * 关闭事件缓存,asyncFetchABTest 验证事件缓存 + */ + @Test + public void closeEventCacheAsyncTest() + throws InvalidArgumentException, IOException, NoSuchFieldException, IllegalAccessException { + ABGlobalConfig abGlobalConfig = ABGlobalConfig.builder() + .enableEventCache(false) //关闭事件缓存 + .setApiUrl(url) //分流试验地址 + .setSensorsAnalytics(sa) //神策分析 SDK 实例 + .build(); + final ISensorsABTest abTest = new SensorsABTest(abGlobalConfig); + Experiment result = abTest.asyncFetchABTest("AB123", true, "btn_type", "eee"); + EventCacheManager eventCacheManager = getEventCacheManagerByReflect(getSensorsABTestWorkerByReflect(abTest)); + assertEquals(0, eventCacheManager.getCacheSize()); + } + + /** + * 开启事件缓存,fastFetchABTest 验证事件缓存存储正确 + */ + @Test + public void openEventCacheFastTest() + throws IOException, InvalidArgumentException, NoSuchFieldException, IllegalAccessException { + final ISensorsABTest abTest = new SensorsABTest(abGlobalConfig); + Experiment result = abTest.fastFetchABTest("AB123", true, "btn_type", "eee"); + EventCacheManager eventCacheManager = getEventCacheManagerByReflect(getSensorsABTestWorkerByReflect(abTest)); + assertEquals(1, eventCacheManager.getCacheSize()); + } + + /** + * 关闭事件缓存,fastFetchABTest 验证事件缓存 + */ + @Test + public void closeEventCacheFastTest() + throws InvalidArgumentException, IOException, NoSuchFieldException, IllegalAccessException { + ABGlobalConfig abGlobalConfig = ABGlobalConfig.builder() + .enableEventCache(false) //关闭事件缓存 + .setApiUrl(url) //分流试验地址 + .setSensorsAnalytics(sa) //神策分析 SDK 实例 + .build(); + final ISensorsABTest abTest = new SensorsABTest(abGlobalConfig); + Experiment result = abTest.fastFetchABTest("AB123", true, "btn_type", "eee"); + EventCacheManager eventCacheManager = getEventCacheManagerByReflect(getSensorsABTestWorkerByReflect(abTest)); + assertEquals(0, eventCacheManager.getCacheSize()); + } + + /** + * 调用两次fastFetchABTest,验证第二次请求是否进行网络请求 + */ + @Test + public void experimentCacheTest() + throws InvalidArgumentException, IOException, NoSuchFieldException, IllegalAccessException { + ABGlobalConfig abGlobalConfig = ABGlobalConfig.builder() + .setApiUrl(url) //分流试验地址 + .setSensorsAnalytics(sa) //神策分析 SDK 实例 + .build(); + final ISensorsABTest abTest = new SensorsABTest(abGlobalConfig); + Experiment s1 = abTest.fastFetchABTest("AB123", true, "btn_type", "eee", false); + ExperimentCacheManager cacheManager = + getExperimentCacheManagerByReflect(getSensorsABTestWorkerByReflect(abTest)); + assertEquals(1, cacheManager.getCacheSize()); + System.out.println(cacheManager.getExperimentResultByCache("AB123", true, "btn_type")); + Experiment s2 = abTest.fastFetchABTest("AB123", true, "btn_type", "eee", false); + } + + /** + * 调用两次 asyncFetchABTest,验证 AsyncFetchABTest 不会存实验缓存 + */ + @Test + public void experimentCacheTestFast() + throws InvalidArgumentException, IOException, NoSuchFieldException, IllegalAccessException { + ABGlobalConfig abGlobalConfig = ABGlobalConfig.builder() + .setApiUrl(url) //分流试验地址 + .setSensorsAnalytics(sa) //神策分析 SDK 实例 + .build(); + final ISensorsABTest abTest = new SensorsABTest(abGlobalConfig); + Experiment s1 = abTest.fastFetchABTest("AB123_00", true, "int", -111); + ExperimentCacheManager cacheManager = + getExperimentCacheManagerByReflect(getSensorsABTestWorkerByReflect(abTest)); + assertEquals(1, cacheManager.getCacheSize()); + System.out.println(cacheManager.getExperimentResultByCache("AB123_00", true, "int")); + + Experiment s2 = abTest.fastFetchABTest("AB123_01", true, "int", -222); + } + + /** + * 调用两次 asyncFetchABTest,验证 AsyncFetchABTest 不会存实验缓存 + */ + @Test + public void experimentCacheTestAsync() + throws InvalidArgumentException, IOException, NoSuchFieldException, IllegalAccessException { + ABGlobalConfig abGlobalConfig = ABGlobalConfig.builder() + .setApiUrl(url) //分流试验地址 + .setSensorsAnalytics(sa) //神策分析 SDK 实例 + .build(); + final ISensorsABTest abTest = new SensorsABTest(abGlobalConfig); + Experiment s1 = abTest.asyncFetchABTest("AB123_00", true, "int", -111); + ExperimentCacheManager cacheManager = + getExperimentCacheManagerByReflect(getSensorsABTestWorkerByReflect(abTest)); + assertEquals(0, cacheManager.getCacheSize()); + + System.out.println(cacheManager.getExperimentResultByCache("AB123_00", true, "int")); + Experiment s2 = abTest.asyncFetchABTest("AB123_01", true, "int", -222); + } + + /** + * fastFetchABTest 设置不自动上报事件,手动触发 + */ + @Test + public void selfTriggerEventByFast() + throws IOException, InvalidArgumentException, NoSuchFieldException, IllegalAccessException { + final ISensorsABTest abTest = new SensorsABTest(abGlobalConfig); + Experiment result = abTest.fastFetchABTest("AB123", true, "btn_type", "eee", false); + EventCacheManager eventCacheManager = getEventCacheManagerByReflect(getSensorsABTestWorkerByReflect(abTest)); + assertEquals(0, eventCacheManager.getCacheSize()); + abTest.trackABTestTrigger(result); + //查看当前项目路径下是否存在日志文件,以及里面的日志信息 + } + + /** + * 测试试验结果缓存过期时间 + * cache清除内存没有使用额外的线程,所以可能不会立即删除,但是用户get操作已经获取不到值,下一次get操作就会清理内存。 + */ + @Test + public void experimentCacheTimeTest() + throws InvalidArgumentException, IOException, NoSuchFieldException, IllegalAccessException, + InterruptedException { + ABGlobalConfig abGlobalConfig = ABGlobalConfig.builder() + .setExperimentCacheTime(1) //缓存有效期分钟 + .setApiUrl(url) //分流试验地址 + .setSensorsAnalytics(sa) //神策分析 SDK 实例 + .build(); + final ISensorsABTest abTest = new SensorsABTest(abGlobalConfig); + Experiment s1 = abTest.fastFetchABTest("AB123", false, "color", "1"); + ExperimentCacheManager cacheManager = + getExperimentCacheManagerByReflect(getSensorsABTestWorkerByReflect(abTest)); + Thread.sleep(60000); + assertNull(cacheManager.getExperimentResultByCache("AB123", false, "color")); + assertEquals(0, cacheManager.getCacheSize()); + + abTest.fastFetchABTest("AB123", false, "color", "1"); + } + + /** + * 测试试验结果缓存过期时间 + * cache清除内存没有使用额外的线程,所以可能不会立即删除,但是用户get操作已经获取不到值,下一次get操作就会清理内存。 + */ + @Test + public void experimentCacheTimeTestDefault() + throws InvalidArgumentException, IOException, NoSuchFieldException, IllegalAccessException, + InterruptedException { + ABGlobalConfig abGlobalConfig = ABGlobalConfig.builder() + .setApiUrl(url) //分流试验地址 + .setSensorsAnalytics(sa) //神策分析 SDK 实例 + .build(); + final ISensorsABTest abTest = new SensorsABTest(abGlobalConfig); + Experiment s1 = abTest.fastFetchABTest("AB123", false, "color", "1"); + ExperimentCacheManager cacheManager = + getExperimentCacheManagerByReflect(getSensorsABTestWorkerByReflect(abTest)); + assertEquals(1440, (int)abGlobalConfig.getExperimentCacheTime()); + System.out.print("======= cacheTime: " + abGlobalConfig.getExperimentCacheTime()); + } + + /** + * 测试试验结果缓存过期时间 + * cache清除内存没有使用额外的线程,所以可能不会立即删除,但是用户get操作已经获取不到值,下一次get操作就会清理内存。 + */ + @Test + public void experimentCacheTimeTestDefault01() + throws InvalidArgumentException, IOException, NoSuchFieldException, IllegalAccessException, + InterruptedException { + ABGlobalConfig abGlobalConfig = ABGlobalConfig.builder() + .setExperimentCacheTime(-1) //缓存有效期分钟 + .setApiUrl(url) //分流试验地址 + .setSensorsAnalytics(sa) //神策分析 SDK 实例 + .build(); + final ISensorsABTest abTest = new SensorsABTest(abGlobalConfig); + Experiment s1 = abTest.fastFetchABTest("AB123", false, "color", "1"); + ExperimentCacheManager cacheManager = + getExperimentCacheManagerByReflect(getSensorsABTestWorkerByReflect(abTest)); + assertEquals(1440, (int)abGlobalConfig.getExperimentCacheTime()); + System.out.print("======= cacheTime: " + abGlobalConfig.getExperimentCacheTime()); + } + + /** + * 测试试验结果缓存过期时间 + * cache清除内存没有使用额外的线程,所以可能不会立即删除,但是用户get操作已经获取不到值,下一次get操作就会清理内存。 + */ + @Test + public void experimentCacheTimeTestDefault02() + throws InvalidArgumentException, IOException, NoSuchFieldException, IllegalAccessException, + InterruptedException { + ABGlobalConfig abGlobalConfig = ABGlobalConfig.builder() + .setExperimentCacheTime(1439) //缓存有效期分钟 + .setApiUrl(url) //分流试验地址 + .setSensorsAnalytics(sa) //神策分析 SDK 实例 + .build(); + final ISensorsABTest abTest = new SensorsABTest(abGlobalConfig); + Experiment s1 = abTest.fastFetchABTest("AB123", false, "color", "1"); + ExperimentCacheManager cacheManager = + getExperimentCacheManagerByReflect(getSensorsABTestWorkerByReflect(abTest)); + assertEquals(1440, (int)abGlobalConfig.getExperimentCacheTime()); + System.out.print("======= cacheTime: " + abGlobalConfig.getExperimentCacheTime()); + } + + /** + * 测试试验结果缓存过期时间 + * cache清除内存没有使用额外的线程,所以可能不会立即删除,但是用户get操作已经获取不到值,下一次get操作就会清理内存。 + */ + @Test + public void experimentCacheTimeTestDefault03() + throws InvalidArgumentException, IOException, NoSuchFieldException, IllegalAccessException, + InterruptedException { + ABGlobalConfig abGlobalConfig = ABGlobalConfig.builder() + .setExperimentCacheTime(null) //缓存有效期分钟 + .setApiUrl(url) //分流试验地址 + .setSensorsAnalytics(sa) //神策分析 SDK 实例 + .build(); + final ISensorsABTest abTest = new SensorsABTest(abGlobalConfig); + Experiment s1 = abTest.fastFetchABTest("AB123", false, "color", "1"); + ExperimentCacheManager cacheManager = + getExperimentCacheManagerByReflect(getSensorsABTestWorkerByReflect(abTest)); + assertEquals(1440, (int)abGlobalConfig.getExperimentCacheTime()); + System.out.print("======= cacheTime: " + abGlobalConfig.getExperimentCacheTime()); + } + + /** + * asyncFetchABTest 请求携带预制属性 + */ + @Test + public void setPropertiesByAsyncTest() throws IOException, InvalidArgumentException, NoSuchFieldException, IllegalAccessException { + final ISensorsABTest abTest = new SensorsABTest(abGlobalConfig); + Map properties = new HashMap<>(); + properties.put("$os", "windows"); + String experimentName = "test_str"; + Experiment result = abTest.asyncFetchABTest(distinctId, false, experimentName, "...", properties); + + System.out.println("======= result: " + result.getResult()); + + assertEquals(distinctId, result.getDistinctId()); + assertEquals(false, result.getIsLoginId()); + assertEquals("102",result.getAbTestExperimentId()); + assertEquals("0",result.getAbTestExperimentGroupId()); + Assert.assertTrue(result.getControlGroup()); + Assert.assertTrue(result.getWhiteList()); + + // + ExperimentCacheManager cacheManager = + getExperimentCacheManagerByReflect(getSensorsABTestWorkerByReflect(abTest)); + + assertEquals(0, cacheManager.getCacheSize()); + + assertNotNull(result.getResult()); + } + + /** + * fastFetchABTest 请求携带预制属性 + */ + @Test + public void setPropertiesByFastTest() throws IOException, InvalidArgumentException, NoSuchFieldException, IllegalAccessException { + final ISensorsABTest abTest = new SensorsABTest(abGlobalConfig); + Map properties = new HashMap<>(); + properties.put("$os", "windows"); + + String experimentName = "test_str"; + Experiment result = abTest.fastFetchABTest(distinctId, false, experimentName, "...", properties); + System.out.println("======= result: " + result.getResult()); + + assertEquals(distinctId, result.getDistinctId()); + assertEquals(false, result.getIsLoginId()); + assertEquals("102", result.getAbTestExperimentId()); + assertEquals("0", result.getAbTestExperimentGroupId()); + Assert.assertTrue(result.getControlGroup()); + Assert.assertTrue(result.getWhiteList()); + + // + ExperimentCacheManager cacheManager = + getExperimentCacheManagerByReflect(getSensorsABTestWorkerByReflect(abTest)); + + assertEquals(1, cacheManager.getCacheSize()); + assertNotNull(result.getResult()); + } + + /** + * asyncFetchABTest 请求携带预制属性 + */ + @Test + public void setPropertiesByAsyncTest01() throws IOException, InvalidArgumentException, NoSuchFieldException, IllegalAccessException { + final ISensorsABTest abTest = new SensorsABTest(abGlobalConfig); + Map properties = new HashMap<>(); + properties.put("test", "test"); + String experimentName = "test_str"; + Experiment result = abTest.asyncFetchABTest(distinctId, false, experimentName, "...", properties); + + System.out.println("======= result: " + result.getResult()); + + assertEquals(distinctId, result.getDistinctId()); + assertEquals(false, result.getIsLoginId()); + assertEquals("102",result.getAbTestExperimentId()); + assertEquals("0",result.getAbTestExperimentGroupId()); + Assert.assertTrue(result.getControlGroup()); + Assert.assertTrue(result.getWhiteList()); + + // + ExperimentCacheManager cacheManager = + getExperimentCacheManagerByReflect(getSensorsABTestWorkerByReflect(abTest)); + + assertEquals(0, cacheManager.getCacheSize()); + + assertNotNull(result.getResult()); + } + + /** + * fastFetchABTest 请求携带预制属性 + */ + @Test + public void setPropertiesByFastTest01() throws IOException, InvalidArgumentException, NoSuchFieldException, IllegalAccessException { + final ISensorsABTest abTest = new SensorsABTest(abGlobalConfig); + Map properties = new HashMap<>(); + properties.put("test", "test"); + + String experimentName = "test_str"; + Experiment result = abTest.fastFetchABTest(distinctId, false, experimentName, "...", properties); + System.out.println("======= result: " + result.getResult()); + + assertEquals(distinctId, result.getDistinctId()); + assertEquals(false, result.getIsLoginId()); + assertEquals("102", result.getAbTestExperimentId()); + assertEquals("0", result.getAbTestExperimentGroupId()); + Assert.assertTrue(result.getControlGroup()); + Assert.assertTrue(result.getWhiteList()); + + // + ExperimentCacheManager cacheManager = + getExperimentCacheManagerByReflect(getSensorsABTestWorkerByReflect(abTest)); + + assertEquals(1, cacheManager.getCacheSize()); + assertNotNull(result.getResult()); + } + + /** + * asyncFetchABTest 请求携带预制属性 + */ + @Test + public void setPropertiesByAsyncTestNull() throws IOException, InvalidArgumentException, NoSuchFieldException, IllegalAccessException { + final ISensorsABTest abTest = new SensorsABTest(abGlobalConfig); + Map properties = null; + String experimentName = "test_str"; + Experiment result = abTest.asyncFetchABTest(distinctId, false, experimentName, "...", properties); + + System.out.println("======= result: " + result.getResult()); + + assertEquals(distinctId, result.getDistinctId()); + assertEquals(false, result.getIsLoginId()); + assertEquals("102",result.getAbTestExperimentId()); + assertEquals("0",result.getAbTestExperimentGroupId()); + Assert.assertTrue(result.getControlGroup()); + Assert.assertTrue(result.getWhiteList()); + + // + ExperimentCacheManager cacheManager = + getExperimentCacheManagerByReflect(getSensorsABTestWorkerByReflect(abTest)); + + assertEquals(0, cacheManager.getCacheSize()); + + assertNotNull(result.getResult()); + } + + /** + * fastFetchABTest 请求携带预制属性 + */ + @Test + public void setPropertiesByFastTestNull() throws IOException, InvalidArgumentException, NoSuchFieldException, IllegalAccessException { + final ISensorsABTest abTest = new SensorsABTest(abGlobalConfig); + Map properties = null; + + String experimentName = "test_str"; + Experiment result = abTest.fastFetchABTest(distinctId, false, experimentName, "...", properties); + System.out.println("======= result: " + result.getResult()); + + assertEquals(distinctId, result.getDistinctId()); + assertEquals(false, result.getIsLoginId()); + assertEquals("102", result.getAbTestExperimentId()); + assertEquals("0", result.getAbTestExperimentGroupId()); + Assert.assertTrue(result.getControlGroup()); + Assert.assertTrue(result.getWhiteList()); + + // + ExperimentCacheManager cacheManager = + getExperimentCacheManagerByReflect(getSensorsABTestWorkerByReflect(abTest)); + + assertEquals(1, cacheManager.getCacheSize()); + assertNotNull(result.getResult()); + } + + /** + * asyncFetchABTest 请求携带预制属性 + */ + @Test + public void setPropertiesByAsyncTestNull01() throws IOException, InvalidArgumentException, NoSuchFieldException, IllegalAccessException { + final ISensorsABTest abTest = new SensorsABTest(abGlobalConfig); + Map properties = new HashMap<>(); + String experimentName = "test_str"; + Experiment result = abTest.asyncFetchABTest(distinctId, false, experimentName, "...", properties); + + System.out.println("======= result: " + result.getResult()); + + assertEquals(distinctId, result.getDistinctId()); + assertEquals(false, result.getIsLoginId()); + assertEquals("102",result.getAbTestExperimentId()); + assertEquals("0",result.getAbTestExperimentGroupId()); + Assert.assertTrue(result.getControlGroup()); + Assert.assertTrue(result.getWhiteList()); + + // + ExperimentCacheManager cacheManager = + getExperimentCacheManagerByReflect(getSensorsABTestWorkerByReflect(abTest)); + + assertEquals(0, cacheManager.getCacheSize()); + + assertNotNull(result.getResult()); + } + + /** + * fastFetchABTest 请求携带预制属性 + */ + @Test + public void setPropertiesByFastTestNull01() throws IOException, InvalidArgumentException, NoSuchFieldException, IllegalAccessException { + final ISensorsABTest abTest = new SensorsABTest(abGlobalConfig); + Map properties = null; + + String experimentName = "test_str"; + Experiment result = abTest.fastFetchABTest(distinctId, false, experimentName, "...", properties); + System.out.println("======= result: " + result.getResult()); + + assertEquals(distinctId, result.getDistinctId()); + assertEquals(false, result.getIsLoginId()); + assertEquals("102", result.getAbTestExperimentId()); + assertEquals("0", result.getAbTestExperimentGroupId()); + Assert.assertTrue(result.getControlGroup()); + Assert.assertTrue(result.getWhiteList()); + + // + ExperimentCacheManager cacheManager = + getExperimentCacheManagerByReflect(getSensorsABTestWorkerByReflect(abTest)); + + assertEquals(1, cacheManager.getCacheSize()); + assertNotNull(result.getResult()); + } + + /** + * asyncFetchABTest 请求携带预制属性 + */ + @Test + public void setPropertiesByAsyncTest02() throws IOException, InvalidArgumentException, NoSuchFieldException, IllegalAccessException { + final ISensorsABTest abTest = new SensorsABTest(abGlobalConfig); + Map properties = new HashMap<>(); + + String experimentName = "test_str"; + Experiment result = abTest.asyncFetchABTest(distinctId, false, experimentName, "...", properties); + + System.out.println("======= result: " + result.getResult()); + + assertEquals(distinctId, result.getDistinctId()); + assertEquals(false, result.getIsLoginId()); + assertEquals("102",result.getAbTestExperimentId()); + assertEquals("0",result.getAbTestExperimentGroupId()); + Assert.assertTrue(result.getControlGroup()); + Assert.assertTrue(result.getWhiteList()); + + // + ExperimentCacheManager cacheManager = + getExperimentCacheManagerByReflect(getSensorsABTestWorkerByReflect(abTest)); + + assertEquals(0, cacheManager.getCacheSize()); + + assertNotNull(result.getResult()); + } + + /** + * fastFetchABTest 请求携带预制属性 + */ + @Test + public void setPropertiesByFastTest02() throws IOException, InvalidArgumentException, NoSuchFieldException, IllegalAccessException { + final ISensorsABTest abTest = new SensorsABTest(abGlobalConfig); + Map properties = new HashMap<>(); + properties.put("test", null); + + String experimentName = "test_str"; + Experiment result = abTest.fastFetchABTest(distinctId, false, experimentName, "...", properties); + System.out.println("======= result: " + result.getResult()); + + assertEquals(distinctId, result.getDistinctId()); + assertEquals(false, result.getIsLoginId()); + assertEquals("102", result.getAbTestExperimentId()); + assertEquals("0", result.getAbTestExperimentGroupId()); + Assert.assertTrue(result.getControlGroup()); + Assert.assertTrue(result.getWhiteList()); + + // + ExperimentCacheManager cacheManager = + getExperimentCacheManagerByReflect(getSensorsABTestWorkerByReflect(abTest)); + + assertEquals(1, cacheManager.getCacheSize()); + assertNotNull(result.getResult()); + } + + /** + * 验证配置有效值后,初始化正确后调用 AsyncFetchABTest 且缓存不存储 + */ + @Test + public void asyncFetchABTestEnableAutoTrackEventTrue() throws InvalidArgumentException, IOException, NoSuchFieldException, IllegalAccessException{ + //初始化 AB Testing SDK + final ISensorsABTest abTest = new SensorsABTest(abGlobalConfig); +// Experiment result = abTest.asyncFetchABTest(distinctId, false, experimentName, "grey", false); + EventCacheManager eventCacheManager = + getEventCacheManagerByReflect(getSensorsABTestWorkerByReflect(abTest)); + + Boolean isEventCacheExist = eventCacheManager.judgeEventCacheExist(distinctId, experimentName); + System.out.println("======= isEventCacheExist: " + isEventCacheExist); + + String experimentName = "int"; + String distinctId = "xc123"; + + Experiment result = abTest.asyncFetchABTest(distinctId, false, experimentName, -1, true); + System.out.println("======= result: " + result.getResult()); + + assertEquals(distinctId, result.getDistinctId()); + assertEquals(false, result.getIsLoginId()); + assertEquals("115",result.getAbTestExperimentId()); + assertEquals("0",result.getAbTestExperimentGroupId()); + Assert.assertTrue(result.getControlGroup()); + Assert.assertFalse(result.getWhiteList()); + + ExperimentCacheManager cacheManager = + getExperimentCacheManagerByReflect(getSensorsABTestWorkerByReflect(abTest)); + + assertEquals(0, cacheManager.getCacheSize()); + + assertNotNull(result.getResult()); + } + + /** + * 验证配置有效值后,初始化正确后调用 AsyncFetchABTest 且缓存不存储 + */ + @Test + public void asyncFetchABTestEnableAutoTrackEventFalse() throws InvalidArgumentException, IOException, NoSuchFieldException, IllegalAccessException{ + //初始化 AB Testing SDK + final ISensorsABTest abTest = new SensorsABTest(abGlobalConfig); +// Experiment result = abTest.asyncFetchABTest(distinctId, false, experimentName, "grey", false); + EventCacheManager eventCacheManager = + getEventCacheManagerByReflect(getSensorsABTestWorkerByReflect(abTest)); + + Boolean isEventCacheExist = eventCacheManager.judgeEventCacheExist(distinctId, experimentName); + System.out.println("======= isEventCacheExist: " + isEventCacheExist); + + String experimentName = "int"; + String distinctId = "xc123"; + + Experiment result = abTest.asyncFetchABTest(distinctId, false, experimentName, -1, false); + System.out.println("======= result: " + result.getResult()); + + assertEquals(distinctId, result.getDistinctId()); + assertEquals(false, result.getIsLoginId()); + assertEquals("115",result.getAbTestExperimentId()); + assertEquals("0",result.getAbTestExperimentGroupId()); + Assert.assertTrue(result.getControlGroup()); + Assert.assertFalse(result.getWhiteList()); + + ExperimentCacheManager cacheManager = + getExperimentCacheManagerByReflect(getSensorsABTestWorkerByReflect(abTest)); + + assertEquals(0, cacheManager.getCacheSize()); + + assertNotNull(result.getResult()); + } + + /** + * 验证配置有效值后,初始化正确后调用 fastFetchABTest 且缓存不存储 + */ + @Test + public void fastFetchABTestEnableAutoTrackEventTrue() throws InvalidArgumentException, IOException, NoSuchFieldException, IllegalAccessException{ + //初始化 AB Testing SDK + final ISensorsABTest abTest = new SensorsABTest(abGlobalConfig); +// Experiment result = abTest.asyncFetchABTest(distinctId, false, experimentName, "grey", false); + String experimentName = "int"; + String distinctId = "xc123"; + + Experiment result = abTest.fastFetchABTest(distinctId, false, experimentName, -1, true); + System.out.println("======= result: " + result.getResult()); + + assertEquals(distinctId, result.getDistinctId()); + assertEquals(false, result.getIsLoginId()); + assertEquals("115",result.getAbTestExperimentId()); + assertEquals("0",result.getAbTestExperimentGroupId()); + Assert.assertTrue(result.getControlGroup()); + Assert.assertFalse(result.getWhiteList()); + + // + ExperimentCacheManager cacheManager = + getExperimentCacheManagerByReflect(getSensorsABTestWorkerByReflect(abTest)); + + assertEquals(1, cacheManager.getCacheSize()); + + assertNotNull(result.getResult()); + } + + /** + * 验证配置有效值后,初始化正确后调用 fastFetchABTest 且缓存不存储 + */ + @Test + public void fastFetchABTestEnableAutoTrackEventFalse() throws InvalidArgumentException, IOException, NoSuchFieldException, IllegalAccessException{ + //初始化 AB Testing SDK + final ISensorsABTest abTest = new SensorsABTest(abGlobalConfig); +// Experiment result = abTest.asyncFetchABTest(distinctId, false, experimentName, "grey", false); + String experimentName = "int"; + String distinctId = "xc123"; + + Experiment result = abTest.fastFetchABTest(distinctId, false, experimentName, -1, false); + System.out.println("======= result: " + result.getResult()); + + assertEquals(distinctId, result.getDistinctId()); + assertEquals(false, result.getIsLoginId()); + assertEquals("115",result.getAbTestExperimentId()); + assertEquals("0",result.getAbTestExperimentGroupId()); + Assert.assertTrue(result.getControlGroup()); + Assert.assertFalse(result.getWhiteList()); + + // + ExperimentCacheManager cacheManager = + getExperimentCacheManagerByReflect(getSensorsABTestWorkerByReflect(abTest)); + + assertEquals(1, cacheManager.getCacheSize()); + + assertNotNull(result.getResult()); + } + + + /** + * 验证 $ABTestTrigger 事件缓存 + */ + @Test + public void asyncFetchABTestAssertEventCache() throws InvalidArgumentException, IOException, NoSuchFieldException, IllegalAccessException{ + //初始化 AB Testing SDK + + final ISensorsABTest abTest = new SensorsABTest(abGlobalConfig); +// Experiment result = abTest.asyncFetchABTest(distinctId, false, experimentName, "grey", false); + EventCacheManager eventCacheManager = + getEventCacheManagerByReflect(getSensorsABTestWorkerByReflect(abTest)); + + assertEquals(0, eventCacheManager.getCacheSize()); + + String experimentName = "int"; + String distinctId = "xc123"; + + Experiment result = abTest.asyncFetchABTest(distinctId, false, experimentName, -1, true); + System.out.println("======= result: " + result.getResult()); + assertTrue(eventCacheManager.judgeEventCacheExist(distinctId, result.getAbTestExperimentId())); + + result = abTest.asyncFetchABTest(distinctId, false, experimentName, -1, true); + System.out.println("======= result: " + result.getResult()); + } + + /** + * 验证配置有效值后,初始化正确后调用 fastFetchABTest 且缓存不存储 + */ + @Test + public void fastFetchABTestAssertEventCache() throws InvalidArgumentException, IOException, NoSuchFieldException, IllegalAccessException{ + //初始化 AB Testing SDK + final ISensorsABTest abTest = new SensorsABTest(abGlobalConfig); +// Experiment result = abTest.asyncFetchABTest(distinctId, false, experimentName, "grey", false); + String experimentName = "int"; + String distinctId = "xc123"; + + EventCacheManager eventCacheManager = + getEventCacheManagerByReflect(getSensorsABTestWorkerByReflect(abTest)); + + Experiment result = abTest.fastFetchABTest(distinctId, false, experimentName, -1, true); + System.out.println("======= result: " + result.getResult()); + + result = abTest.fastFetchABTest(distinctId, false, experimentName, -1, true); + System.out.println("======= result: " + result.getResult()); + assertTrue(eventCacheManager.judgeEventCacheExist(distinctId, result.getAbTestExperimentId())); + } + + /** + * 验证清除 $ABTestTrigger 事件缓存,可以触发事件 + */ + @Test + public void asyncFetchABTestinvalidateEventCache() throws InvalidArgumentException, IOException, NoSuchFieldException, IllegalAccessException{ + //初始化 AB Testing SDK + + final ISensorsABTest abTest = new SensorsABTest(abGlobalConfig); +// Experiment result = abTest.asyncFetchABTest(distinctId, false, experimentName, "grey", false); + EventCacheManager eventCacheManager = + getEventCacheManagerByReflect(getSensorsABTestWorkerByReflect(abTest)); + + assertEquals(0, eventCacheManager.getCacheSize()); + + String experimentName = "int"; + String distinctId = "xc123"; + + Experiment result = abTest.asyncFetchABTest(distinctId, false, experimentName, -1, true); + System.out.println("======= result: " + result.getResult()); + assertTrue(eventCacheManager.judgeEventCacheExist(distinctId, result.getAbTestExperimentId())); + + getEventCacheByReflect(eventCacheManager).invalidateAll(); //强制清除缓存 + + result = abTest.asyncFetchABTest(distinctId, false, experimentName, -1, true); + System.out.println("======= result: " + result.getResult()); + } + + /** + * 验证清除 $ABTestTrigger 事件缓存,可以触发事件 + */ + @Test + public void fastFetchABTestEventnvalidateCache() throws InvalidArgumentException, IOException, NoSuchFieldException, IllegalAccessException{ + //初始化 AB Testing SDK + + final ISensorsABTest abTest = new SensorsABTest(abGlobalConfig); +// Experiment result = abTest.asyncFetchABTest(distinctId, false, experimentName, "grey", false); + EventCacheManager eventCacheManager = + getEventCacheManagerByReflect(getSensorsABTestWorkerByReflect(abTest)); + + assertEquals(0, eventCacheManager.getCacheSize()); + + String experimentName = "int"; + String distinctId = "xc123"; + + Experiment result = abTest.fastFetchABTest(distinctId, false, experimentName, -1, true); + System.out.println("======= result: " + result.getResult()); + assertTrue(eventCacheManager.judgeEventCacheExist(distinctId, result.getAbTestExperimentId())); + + getEventCacheByReflect(eventCacheManager).invalidateAll(); //强制清除缓存 + + result = abTest.fastFetchABTest(distinctId, false, experimentName, -1, true); + System.out.println("======= result: " + result.getResult()); + } + + + + /** + * 上报事件接口测试 + * 第一次携带version,之后请求不携带 + */ + @Test + public void trackABTestTriggerTest() throws InvalidArgumentException { + final ISensorsABTest abTest = new SensorsABTest(abGlobalConfig); + Experiment result = new Experiment<>("AB123456", true, "102", "88", false, false, "grey"); + abTest.trackABTestTrigger(result); + Experiment result1 = new Experiment<>("AB1234567", true, "103", "99", false, false, "grey"); + abTest.trackABTestTrigger(result1); + + Experiment result2 = new Experiment<>("AB1234567", true, "104", "100", false, false, "grey"); + Map properties = new HashMap<>(); + properties.put("$os", "mac"); + abTest.trackABTestTrigger(result2, properties); + } + + /** + * 上报事件接口测试 prop + */ + @Test + public void trackABTestTriggerTestProp() throws InvalidArgumentException { + final ISensorsABTest abTest = new SensorsABTest(abGlobalConfig); + + Experiment result2 = new Experiment<>("AB1234567", true, "104", "100", false, false, "grey"); + Map properties = new HashMap<>(); + properties.put("$os", "mac"); + abTest.trackABTestTrigger(result2, properties); + } + + /** + * 上报事件接口测试 prop + */ + @Test + public void trackABTestTriggerTestNull() throws InvalidArgumentException { + String url = + "http://abtesting.saas.debugbox.sensorsdata.cn/api/v2/abtest/online/results?project-key=438B9364C98D54371751BA82F6484A1A03A5155E"; + + //构建配置 AB Testing 试验全局参数 + ABGlobalConfig abGlobalConfig = ABGlobalConfig.builder() + .setApiUrl(url) //分流试验地址 + .setSensorsAnalytics(sa) //神策分析 SDK 实例 + .enableEventCache(true) + .build(); + + final ISensorsABTest abTest = new SensorsABTest(abGlobalConfig); + + Experiment result2 = new Experiment<>("AB1234567", true, "104", "100", false, false, "grey"); + Map properties = new HashMap<>(); + properties.put("$os", "mac"); + abTest.trackABTestTrigger(null, properties); + } + + @Test + public void trackABTestTriggerTestMulProp() throws InvalidArgumentException { + String url = + "http://abtesting.saas.debugbox.sensorsdata.cn/api/v2/abtest/online/results?project-key=438B9364C98D54371751BA82F6484A1A03A5155E"; + + //构建配置 AB Testing 试验全局参数 + ABGlobalConfig abGlobalConfig = ABGlobalConfig.builder() + .setApiUrl(url) //分流试验地址 + .setSensorsAnalytics(sa) //神策分析 SDK 实例 + .enableEventCache(true) + .build(); + + final ISensorsABTest abTest = new SensorsABTest(abGlobalConfig); + + Experiment result2 = new Experiment<>("AB1234567", true, "104", "100", false, false, "grey"); + Map properties = new HashMap<>(); + properties.put("test", "test"); + properties.put("andoter", "andoter"); + properties.put("antway", "dddddd"); + abTest.trackABTestTrigger(result2, properties); + } + + @Test + public void trackABTestTriggerTestMulProps() throws InvalidArgumentException { + String url = + "http://abtesting.saas.debugbox.sensorsdata.cn/api/v2/abtest/online/results?project-key=438B9364C98D54371751BA82F6484A1A03A5155E"; + + //构建配置 AB Testing 试验全局参数 + ABGlobalConfig abGlobalConfig = ABGlobalConfig.builder() + .setApiUrl(url) //分流试验地址 + .setSensorsAnalytics(sa) //神策分析 SDK 实例 + .enableEventCache(true) + .build(); + + final ISensorsABTest abTest = new SensorsABTest(abGlobalConfig); + + Experiment result2 = new Experiment<>("AB1234567", true, "104", "100", false, false, "grey"); + Map properties = new HashMap<>(); + properties.put("$os", "mac"); + properties.put("$lib_version", "000"); + abTest.trackABTestTrigger(result2, properties); + } + + /** + * 用户为白名单用户,不触发上报事件 + */ + @Test + public void notTriggerEventTest() + throws IOException, InvalidArgumentException, NoSuchFieldException, IllegalAccessException { + final ISensorsABTest abTest = new SensorsABTest(abGlobalConfig); + ObjectMapper objectMapper = new ObjectMapper(); + Experiment result = abTest.asyncFetchABTest(distinctId, true, "color", "{\"color\":\"grey\"}"); + assertTrue(result.getWhiteList()); + EventCacheManager eventCacheManager = getEventCacheManagerByReflect(getSensorsABTestWorkerByReflect(abTest)); + assertEquals(0, eventCacheManager.getCacheSize()); + } + + + + private SensorsABTestWorker getSensorsABTestWorkerByReflect(ISensorsABTest abTest) + throws NoSuchFieldException, IllegalAccessException { + Class abTestClazz = abTest.getClass(); + Field field = abTestClazz.getDeclaredField("worker"); + field.setAccessible(true); + return (SensorsABTestWorker) field.get(abTest); + } + + private ExperimentCacheManager getExperimentCacheManagerByReflect(SensorsABTestWorker worker) + throws NoSuchFieldException, IllegalAccessException { + Class workerClazz = worker.getClass(); + Field cacheManagerField = workerClazz.getDeclaredField("experimentCacheManager"); + cacheManagerField.setAccessible(true); + return (ExperimentCacheManager) cacheManagerField.get(worker); + } + + private LoadingCache getExperimentResultCacheByReflect(ExperimentCacheManager experimentCacheManager) throws NoSuchFieldException, IllegalAccessException { + + Class experimentCacheManagerClass = experimentCacheManager.getClass(); + + Field cacheField = experimentCacheManagerClass.getDeclaredField("experimentResultCache"); + cacheField.setAccessible(true); + return (LoadingCache) cacheField.get(experimentCacheManager); + } + + private EventCacheManager getEventCacheManagerByReflect(SensorsABTestWorker worker) + throws NoSuchFieldException, IllegalAccessException { + Class workerClazz = worker.getClass(); + Field cacheManagerField = workerClazz.getDeclaredField("eventCacheManager"); + cacheManagerField.setAccessible(true); + return (EventCacheManager) cacheManagerField.get(worker); + } + + private LoadingCache getEventCacheByReflect(EventCacheManager eventCacheManager) throws NoSuchFieldException, IllegalAccessException { + Class eventCacheManagerClass = eventCacheManager.getClass(); + Field cacheField = eventCacheManagerClass.getDeclaredField("eventCache"); + cacheField.setAccessible(true); + return (LoadingCache) cacheField.get(eventCacheManager); + } +} \ No newline at end of file