From 3ffecf22505a44a9253496eb245c10dc2df5996b Mon Sep 17 00:00:00 2001 From: arifBurakDemiray Date: Mon, 18 Sep 2023 11:28:51 +0300 Subject: [PATCH 01/82] chore: ignore vscode path --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 4f800c96c..0477c7f78 100644 --- a/.gitignore +++ b/.gitignore @@ -12,3 +12,4 @@ sdk-java/build/ app-java/out/ data sdk-java/out/ +/.vscode/ \ No newline at end of file From 4397867be1bcf1279421daa31b0d35bf3a5480e2 Mon Sep 17 00:00:00 2001 From: arifBurakDemiray Date: Mon, 18 Sep 2023 15:01:17 +0300 Subject: [PATCH 02/82] feat: module events --- .../count/sdk/java/internal/ModuleEvents.java | 142 ++++++++++++++++++ 1 file changed, 142 insertions(+) create mode 100644 sdk-java/src/main/java/ly/count/sdk/java/internal/ModuleEvents.java diff --git a/sdk-java/src/main/java/ly/count/sdk/java/internal/ModuleEvents.java b/sdk-java/src/main/java/ly/count/sdk/java/internal/ModuleEvents.java new file mode 100644 index 000000000..ece05a9ae --- /dev/null +++ b/sdk-java/src/main/java/ly/count/sdk/java/internal/ModuleEvents.java @@ -0,0 +1,142 @@ +package ly.count.sdk.java.internal; + +import java.util.Map; + +public class ModuleEvents extends ModuleBase { + + @Override + public void init(InternalConfig config, Log logger) { + super.init(config, logger); + internalConfig = config; + } + + private void recordEventInternal(String key, int count, double sum, Map segmentation, double dur) { + + } + + private boolean startEventInternal(String key) { + return false; + } + + private boolean endEventInternal(String key) { + return false; + } + + private boolean cancelEventInternal(String key) { + return false; + } + + public class Events { + + /** + * Record an event. + * + * @param key key for this event, cannot be null or empty + * @param count how many of these events have occurred, default value is "1", must be greater than 0 + * @param sum set sum parameter of the event default value is "0" + * @param dur set duration of event, default value is "0" + * @param segmentation additional segmentation data that you want to set, leave null if you don't want to add anything + */ + public void recordEvent(String key, int count, double sum, Map segmentation, double dur) { + recordEventInternal(key, count, sum, segmentation, dur); + } + + /** + * Record an event with "duration" 0 + * + * @param key key for this event, cannot be null or empty + * @param count how many of these events have occurred, default value is "1", must be greater than 0 + * @param sum set sum parameter of the event default value is "0" + * @param segmentation additional segmentation data that you want to set, leave null if you don't want to add anything + */ + public void recordEvent(String key, int count, double sum, Map segmentation) { + recordEvent(key, count, sum, segmentation, 0.0); + } + + /** + * Record an event with "segmentation","key" and "count" value only + * "duration" is zero by default + * + * @param key key for this event, cannot be null or empty + * @param count how many of these events have occurred, default value is "1", must be greater than 0 + * @param segmentation additional segmentation data that you want to set, leave null if you don't want to add anything + */ + public void recordEvent(String key, int count, Map segmentation) { + recordEvent(key, count, 0.0, segmentation); + } + + /** + * Record an event with "segmentation" and "key" value only + * "sum" and "duration" is zero by default + * + * @param key key for this event, cannot be null or empty + * @param segmentation additional segmentation data that you want to set, leave null if you don't want to add anything + */ + public void recordEvent(String key, Map segmentation) { + recordEvent(key, 1, 0.0, segmentation); + } + + /** + * Record an event with "key" only + * "sum" and "duration" is zero by default + * "count" is 1 by default + * + * @param key key for this event, cannot be null or empty + */ + public void recordEvent(String key) { + recordEvent(key, 1, 0, null); + } + + /** + * Record an event with "key" and "count" only + * "sum" and "duration" is zero by default + * + * @param key key for this event, cannot be null or empty + * @param count how many of these events have occurred, default value is "1", must be greater than 0 + */ + public void recordEvent(String key, int count) { + recordEvent(key, count, 0, null); + } + + /** + * Record an event with "key", "sum" and "count" only + * "duration" is zero by default + * + * @param key key for this event, cannot be null or empty + * @param count how many of these events have occurred, default value is "1", must be greater than 0 + * @param sum set sum parameter of the event default value is "0" + */ + public void recordEvent(String key, double sum, int count) { + recordEvent(key, count, sum, null); + } + + /** + * Start timed event with a specified key + * + * @param key name of the custom event, required, must not be the empty string or null + * @return true if no event with this key existed before and event is started, false otherwise + */ + public boolean startEvent(final String key) { + return startEventInternal(key); + } + + /** + * End timed event with a specified key + * + * @param key name of the custom event, required, must not be the empty string or null + * @return true if event with this key has been previously started, false otherwise + */ + public boolean endEvent(final String key) { + return endEventInternal(key); + } + + /** + * Cancel timed event with a specified key + * + * @return true if event with this key has been previously started, false otherwise + **/ + public boolean cancelEvent(final String key) { + return cancelEventInternal(key); + } + } +} From abd616c6588bb2bb016f1037add3e8ac601df2c6 Mon Sep 17 00:00:00 2001 From: arifBurakDemiray Date: Mon, 18 Sep 2023 17:13:01 +0300 Subject: [PATCH 03/82] feat: new constructor for EventImpl --- .../ly/count/sdk/java/internal/EventImpl.java | 23 ++++++++++++++----- 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/sdk-java/src/main/java/ly/count/sdk/java/internal/EventImpl.java b/sdk-java/src/main/java/ly/count/sdk/java/internal/EventImpl.java index f419fc9d4..19621108a 100644 --- a/sdk-java/src/main/java/ly/count/sdk/java/internal/EventImpl.java +++ b/sdk-java/src/main/java/ly/count/sdk/java/internal/EventImpl.java @@ -1,15 +1,12 @@ package ly.count.sdk.java.internal; -import org.json.JSONException; -import org.json.JSONObject; - import java.util.HashMap; import java.util.Iterator; import java.util.Map; - -import ly.count.sdk.java.Event; import javax.annotation.Nonnull; -import javax.annotation.Nullable; +import ly.count.sdk.java.Event; +import org.json.JSONException; +import org.json.JSONObject; /** * Event base class implementation @@ -41,6 +38,20 @@ public interface EventRecorder { */ private boolean invalid = false; + EventImpl(@Nonnull String key, int count, double sum, double duration, @Nonnull Map segmentation, @Nonnull Log givenL) { + L = givenL; + + this.recorder = null; + this.key = key; + this.count = count; + this.sum = sum; + this.duration = duration; + this.segmentation = segmentation; + this.timestamp = Device.dev.uniqueTimestamp(); + this.hour = Device.dev.currentHour(); + this.dow = Device.dev.currentDayOfWeek(); + } + EventImpl(@Nonnull EventRecorder recorder, @Nonnull String key, @Nonnull Log givenL) { L = givenL; if (recorder == null) { From d42571cab6f2ae088f3cb77d5d1b4b7d2f3418f4 Mon Sep 17 00:00:00 2001 From: arifBurakDemiray Date: Mon, 18 Sep 2023 17:13:30 +0300 Subject: [PATCH 04/82] feat: new way of events module call --- .../main/java/ly/count/sdk/java/Countly.java | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/sdk-java/src/main/java/ly/count/sdk/java/Countly.java b/sdk-java/src/main/java/ly/count/sdk/java/Countly.java index 77512c5ab..a3e4f87e4 100644 --- a/sdk-java/src/main/java/ly/count/sdk/java/Countly.java +++ b/sdk-java/src/main/java/ly/count/sdk/java/Countly.java @@ -42,6 +42,8 @@ private static void empty() { protected CtxCore ctx; protected Log L; + ModuleEvents moduleEvents; + protected Countly(SDKCore sdk, CtxCore ctx, Log logger) { L = logger; this.sdk = sdk; @@ -107,6 +109,9 @@ public void init(final Config config) { this.sdk = sdk; this.ctx = new CtxCore(sdk, sdk.config(), L, directory); this.L = L; + + moduleEvents = new ModuleEvents(); + moduleEvents.init(internalConfig, L); } /** @@ -311,12 +316,28 @@ public static void onConsentRemoval(Config.Feature... features) { } } + /** + * Record event with provided key. + * + * @param key key for this event, cannot be null or empty + * @return Builder object for this event + * @deprecated use {@link #events()} instead via instance() call + */ @Override public Event event(String key) { L.d("[Cly] event: key = " + key); return ((Session) sdk.session(ctx, null)).event(key); } + /** + * Event module calls + * + * @return event module + */ + public ModuleEvents.Events events() { + return moduleEvents.new Events(); + } + @Override public Event timedEvent(String key) { L.d("[Cly] timedEvent: key = " + key); From 6e1b263b0f86bd8990fbc0831ce7e1b96724c8b8 Mon Sep 17 00:00:00 2001 From: arifBurakDemiray Date: Mon, 18 Sep 2023 17:14:02 +0300 Subject: [PATCH 05/82] feat: module events --- .../count/sdk/java/internal/ModuleEvents.java | 147 ++++++++++++++++++ 1 file changed, 147 insertions(+) diff --git a/sdk-java/src/main/java/ly/count/sdk/java/internal/ModuleEvents.java b/sdk-java/src/main/java/ly/count/sdk/java/internal/ModuleEvents.java index ece05a9ae..f5a0ae0ca 100644 --- a/sdk-java/src/main/java/ly/count/sdk/java/internal/ModuleEvents.java +++ b/sdk-java/src/main/java/ly/count/sdk/java/internal/ModuleEvents.java @@ -1,17 +1,159 @@ package ly.count.sdk.java.internal; +import java.util.ArrayDeque; +import java.util.ArrayList; +import java.util.Calendar; +import java.util.HashMap; +import java.util.List; import java.util.Map; +import java.util.Queue; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; +import ly.count.sdk.java.Countly; +import org.json.JSONArray; public class ModuleEvents extends ModuleBase { + protected InternalConfig internalConfig = null; + protected CtxCore ctx = null; + + //disabled is set when a empty module is created + //in instances when the rating feature was not enabled + //when a module is disabled, developer facing functions do nothing + protected boolean disabledModule = false; + protected final Queue eventQueues = new ArrayDeque<>(); + + private ScheduledExecutorService executor = null; @Override public void init(InternalConfig config, Log logger) { super.init(config, logger); internalConfig = config; + L.d("[ModuleEvents] init: config = " + config); + } + + @Override + public void onContextAcquired(CtxCore ctx) { + this.ctx = ctx; + L.d("[ModuleEvents] onContextAcquired: " + ctx.toString()); + + if (ctx.getConfig().getSendUpdateEachSeconds() > 0 && executor == null) { + executor = Executors.newScheduledThreadPool(1); + executor.scheduleWithFixedDelay(new Runnable() { + @Override + public void run() { + addEventsToRequestQ(); + } + }, ctx.getConfig().getSendUpdateEachSeconds(), ctx.getConfig().getSendUpdateEachSeconds(), TimeUnit.SECONDS); + } + } + + private synchronized void addEventsToRequestQ() { + L.d("[ModuleEvents] addEventsToRequestQ"); + JSONArray events = new JSONArray(); + + if (eventQueues.isEmpty()) { + return; + } + + for (EventImpl event : eventQueues) { + events.put(event.toJSON(L)); + } + + Request request = new Request(); + request.params.add("device_id", Countly.instance().getDeviceId()); + request.params.add("events", events); + addTimeInfoIntoRequest(request, System.currentTimeMillis()); + request.own(ModuleEvents.class); + addRequestToRequestQ(request); + + eventQueues.clear(); + } + + private void addTimeInfoIntoRequest(Request request, Long timestamp) { + Calendar calendar = Calendar.getInstance(); + calendar.setTimeInMillis(timestamp); + + final int hour = calendar.get(Calendar.HOUR_OF_DAY); + final int dow = calendar.get(Calendar.DAY_OF_WEEK) - 1; + + request.params.add("dow", dow); + request.params.add("hour", hour); + request.params.add("timestamp", timestamp); + request.params.add("tz", Device.dev.getTimezoneOffset()); + } + + private void addRequestToRequestQ(Request request) { + synchronized (SDKCore.instance.lockBRQStorage) { + L.d("[ModuleEvents] addRequestToRequestQ"); + if (internalConfig.getRequestQueueMaxSize() == SDKCore.instance.requestQueueMemory.size()) { + L.d("[ModuleEvents] addRequestToRequestQ: In Memory request queue is full, dropping oldest request: " + request.params.toString()); + SDKCore.instance.requestQueueMemory.remove(); + } + + SDKCore.instance.requestQueueMemory.add(request); + SDKCore.instance.networking.check(ctx); + } + } + + protected Map removeInvalidDataFromSegments(Map segments) { + + if (segments == null || segments.isEmpty()) { + return segments; + } + + int i = 0; + List toRemove = new ArrayList<>(); + for (Map.Entry item : segments.entrySet()) { + Object type = item.getValue(); + + boolean isValidDataType = (type instanceof Boolean + || type instanceof Integer + || type instanceof Long + || type instanceof String + || type instanceof Double + || type instanceof Float + ); + + if (!isValidDataType) { + toRemove.add(item.getKey()); + L.w("[ModuleEvents] RemoveSegmentInvalidDataTypes: In segmentation Data type '" + type + "' of item '" + item.getValue() + "' isn't valid."); + } + } + + for (String k : toRemove) { + segments.remove(k); + } + + return segments; } private void recordEventInternal(String key, int count, double sum, Map segmentation, double dur) { + removeInvalidDataFromSegments(segmentation); + if (count <= 0) { + L.w("[ModuleEvents] recordEventInternal: Count can't be less than 1, ignoring this event."); + return; + } + + if (key == null || key.isEmpty()) { + L.w("[ModuleEvents] recordEventInternal: Key can't be null or empty, ignoring this event."); + return; + } + + Map mappedSegmentation = new HashMap<>(); + if (segmentation != null) { + for (Map.Entry entry : segmentation.entrySet()) { + mappedSegmentation.put(entry.getKey(), entry.getValue().toString()); + } + } + + EventImpl event = new EventImpl(key, count, sum, dur, mappedSegmentation, L); + eventQueues.add(event); + + if (eventQueues.size() >= internalConfig.getEventsBufferSize()) { + addEventsToRequestQ(); + } } private boolean startEventInternal(String key) { @@ -38,6 +180,11 @@ public class Events { * @param segmentation additional segmentation data that you want to set, leave null if you don't want to add anything */ public void recordEvent(String key, int count, double sum, Map segmentation, double dur) { + if (!Countly.isInitialized()) { + L.e("[Events] recordEvent: Countly.instance().init must be called before recordEvent"); + return; + } + recordEventInternal(key, count, sum, segmentation, dur); } From 906d3468913d95a68ca8f4b15f91a9e30d618237 Mon Sep 17 00:00:00 2001 From: arifBurakDemiray Date: Tue, 19 Sep 2023 09:16:23 +0300 Subject: [PATCH 06/82] fix: update version to 23.8.0 --- .idea/modules/sdk-java/countly-sdk-java.sdk-java.iml | 2 +- .idea/modules/sdk-java/countly-sdk-java.sdk-java.main.iml | 2 +- .idea/modules/sdk-java/countly-sdk-java.sdk-java.test.iml | 2 +- CHANGELOG.md | 2 +- build.gradle | 2 +- gradle.properties | 2 +- sdk-java/src/main/java/ly/count/sdk/java/Config.java | 2 +- .../src/test/java/ly/count/sdk/java/internal/ConfigTests.java | 2 +- 8 files changed, 8 insertions(+), 8 deletions(-) diff --git a/.idea/modules/sdk-java/countly-sdk-java.sdk-java.iml b/.idea/modules/sdk-java/countly-sdk-java.sdk-java.iml index 9bf50b5c7..34a534369 100644 --- a/.idea/modules/sdk-java/countly-sdk-java.sdk-java.iml +++ b/.idea/modules/sdk-java/countly-sdk-java.sdk-java.iml @@ -1,5 +1,5 @@ - + diff --git a/.idea/modules/sdk-java/countly-sdk-java.sdk-java.main.iml b/.idea/modules/sdk-java/countly-sdk-java.sdk-java.main.iml index a5e12edee..d20d9ec48 100644 --- a/.idea/modules/sdk-java/countly-sdk-java.sdk-java.main.iml +++ b/.idea/modules/sdk-java/countly-sdk-java.sdk-java.main.iml @@ -1,5 +1,5 @@ - + diff --git a/.idea/modules/sdk-java/countly-sdk-java.sdk-java.test.iml b/.idea/modules/sdk-java/countly-sdk-java.sdk-java.test.iml index f555606df..49aad1b78 100644 --- a/.idea/modules/sdk-java/countly-sdk-java.sdk-java.test.iml +++ b/.idea/modules/sdk-java/countly-sdk-java.sdk-java.test.iml @@ -1,5 +1,5 @@ - + diff --git a/CHANGELOG.md b/CHANGELOG.md index 14682992f..aaa72ce19 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,4 @@ -23.09.0 +23.8.0 * Deprecated call "Countly::getSession" is removed * Deprecated call "resetDeviceId" is removed * Deprecated the init time configuration of 'setEventsBufferSize(eventsBufferSize)'. Introduced replacement 'setEventQueueSizeToSend(eventsQueueSize)' diff --git a/build.gradle b/build.gradle index 6dbbeb4c1..1e78ec271 100644 --- a/build.gradle +++ b/build.gradle @@ -20,7 +20,7 @@ buildscript { } allprojects { - ext.CLY_VERSION = "23.09.0" + ext.CLY_VERSION = "23.8.0" ext.POWERMOCK_VERSION = "1.7.4" tasks.withType(Javadoc) { diff --git a/gradle.properties b/gradle.properties index 51bcca5c5..a2bb2a80e 100644 --- a/gradle.properties +++ b/gradle.properties @@ -18,7 +18,7 @@ # org.gradle.parallel=true # RELEASE FIELD SECTION -VERSION_NAME=23.09.0 +VERSION_NAME=23.8.0 GROUP=ly.count.sdk POM_URL=https://github.com/Countly/countly-sdk-java diff --git a/sdk-java/src/main/java/ly/count/sdk/java/Config.java b/sdk-java/src/main/java/ly/count/sdk/java/Config.java index a860859d2..932766fe4 100644 --- a/sdk-java/src/main/java/ly/count/sdk/java/Config.java +++ b/sdk-java/src/main/java/ly/count/sdk/java/Config.java @@ -282,7 +282,7 @@ public boolean restore(byte[] data, Log L) { /** * Countly SDK version to be sent in HTTP requests */ - protected String sdkVersion = "23.09.0"; + protected String sdkVersion = "23.8.0"; /** * Countly SDK version to be sent in HTTP requests diff --git a/sdk-java/src/test/java/ly/count/sdk/java/internal/ConfigTests.java b/sdk-java/src/test/java/ly/count/sdk/java/internal/ConfigTests.java index f4b3d30ae..1f756fd0c 100644 --- a/sdk-java/src/test/java/ly/count/sdk/java/internal/ConfigTests.java +++ b/sdk-java/src/test/java/ly/count/sdk/java/internal/ConfigTests.java @@ -81,7 +81,7 @@ public void testSDKName() { @Test public void testSDKVersion() { - String versionName = "23.09.0"; + String versionName = "23.8.0"; Assert.assertEquals(versionName, internalConfig.getSdkVersion()); internalConfig.setSdkVersion(null); From 92c6041e5cf467dd2afc018841c74febbbb638c6 Mon Sep 17 00:00:00 2001 From: arifBurakDemiray Date: Tue, 19 Sep 2023 09:48:41 +0300 Subject: [PATCH 07/82] feat: events module to sdk core --- sdk-java/src/main/java/ly/count/sdk/java/Countly.java | 7 +------ .../main/java/ly/count/sdk/java/internal/CoreFeature.java | 2 +- .../main/java/ly/count/sdk/java/internal/ModuleEvents.java | 2 ++ .../src/main/java/ly/count/sdk/java/internal/SDKCore.java | 5 +++++ 4 files changed, 9 insertions(+), 7 deletions(-) diff --git a/sdk-java/src/main/java/ly/count/sdk/java/Countly.java b/sdk-java/src/main/java/ly/count/sdk/java/Countly.java index a3e4f87e4..e31bd497f 100644 --- a/sdk-java/src/main/java/ly/count/sdk/java/Countly.java +++ b/sdk-java/src/main/java/ly/count/sdk/java/Countly.java @@ -42,8 +42,6 @@ private static void empty() { protected CtxCore ctx; protected Log L; - ModuleEvents moduleEvents; - protected Countly(SDKCore sdk, CtxCore ctx, Log logger) { L = logger; this.sdk = sdk; @@ -109,9 +107,6 @@ public void init(final Config config) { this.sdk = sdk; this.ctx = new CtxCore(sdk, sdk.config(), L, directory); this.L = L; - - moduleEvents = new ModuleEvents(); - moduleEvents.init(internalConfig, L); } /** @@ -335,7 +330,7 @@ public Event event(String key) { * @return event module */ public ModuleEvents.Events events() { - return moduleEvents.new Events(); + return sdk.events(); } @Override diff --git a/sdk-java/src/main/java/ly/count/sdk/java/internal/CoreFeature.java b/sdk-java/src/main/java/ly/count/sdk/java/internal/CoreFeature.java index c384254e1..a24c98016 100644 --- a/sdk-java/src/main/java/ly/count/sdk/java/internal/CoreFeature.java +++ b/sdk-java/src/main/java/ly/count/sdk/java/internal/CoreFeature.java @@ -5,7 +5,7 @@ public enum CoreFeature { Sessions(1 << 1, ModuleSessions::new), - Events(1 << 2), + Events(1 << 2, ModuleEvents::new), Views(1 << 3, ModuleViews::new), CrashReporting(1 << 4, ModuleCrash::new), Location(1 << 5), diff --git a/sdk-java/src/main/java/ly/count/sdk/java/internal/ModuleEvents.java b/sdk-java/src/main/java/ly/count/sdk/java/internal/ModuleEvents.java index f5a0ae0ca..cee7f486f 100644 --- a/sdk-java/src/main/java/ly/count/sdk/java/internal/ModuleEvents.java +++ b/sdk-java/src/main/java/ly/count/sdk/java/internal/ModuleEvents.java @@ -25,6 +25,8 @@ public class ModuleEvents extends ModuleBase { private ScheduledExecutorService executor = null; + protected Events eventsInterface = new Events(); + @Override public void init(InternalConfig config, Log logger) { super.init(config, logger); diff --git a/sdk-java/src/main/java/ly/count/sdk/java/internal/SDKCore.java b/sdk-java/src/main/java/ly/count/sdk/java/internal/SDKCore.java index e5324b7cb..4db4e4006 100644 --- a/sdk-java/src/main/java/ly/count/sdk/java/internal/SDKCore.java +++ b/sdk-java/src/main/java/ly/count/sdk/java/internal/SDKCore.java @@ -53,6 +53,7 @@ protected static void registerDefaultModuleMappings() { moduleMappings.put(CoreFeature.Sessions.getIndex(), ModuleSessions.class); moduleMappings.put(CoreFeature.CrashReporting.getIndex(), ModuleCrash.class); moduleMappings.put(CoreFeature.BackendMode.getIndex(), ModuleBackendMode.class); + moduleMappings.put(CoreFeature.Events.getIndex(), ModuleEvents.class); } public interface Modulator { @@ -492,6 +493,10 @@ TimedEvents timedEvents() { return ((ModuleSessions) module(CoreFeature.Sessions.getIndex())).timedEvents(); } + public ModuleEvents.Events events() { + return ((ModuleEvents) module(CoreFeature.Events.getIndex())).eventsInterface; + } + public InternalConfig config() { return config; } From 18782df91bf1174aaab3409b9772989e885d13ee Mon Sep 17 00:00:00 2001 From: arifBurakDemiray Date: Tue, 19 Sep 2023 11:20:00 +0300 Subject: [PATCH 08/82] feat: end event deatiled --- .../count/sdk/java/internal/ModuleEvents.java | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/sdk-java/src/main/java/ly/count/sdk/java/internal/ModuleEvents.java b/sdk-java/src/main/java/ly/count/sdk/java/internal/ModuleEvents.java index cee7f486f..4242d0896 100644 --- a/sdk-java/src/main/java/ly/count/sdk/java/internal/ModuleEvents.java +++ b/sdk-java/src/main/java/ly/count/sdk/java/internal/ModuleEvents.java @@ -162,7 +162,7 @@ private boolean startEventInternal(String key) { return false; } - private boolean endEventInternal(String key) { + private boolean endEventInternal(final String key, final Map segmentation, final int count, final double sum) { return false; } @@ -276,7 +276,22 @@ public boolean startEvent(final String key) { * @return true if event with this key has been previously started, false otherwise */ public boolean endEvent(final String key) { - return endEventInternal(key); + return endEventInternal(key,null,1,0); + } + + /** + * End timed event with a specified key + * + * @param key name of the custom event, required, must not be the empty string + * @param segmentation segmentation dictionary to associate with the event, can be null + * @param count count to associate with the event, should be more than zero, default value is 1 + * @param sum sum to associate with the event, default value is 0 + * @return true if event with this key has been previously started, false otherwise + * @throws IllegalStateException if Countly SDK has not been initialized + * @throws IllegalArgumentException if key is null or empty, count is less than 1, or if segmentation contains null or empty keys or values + */ + public boolean endEvent(final String key, final Map segmentation, final int count, final double sum) { + return endEventInternal(key,segmentation,count,sum); } /** From 8bd5a24084bdfb1578f83f133a6adce792bc3db8 Mon Sep 17 00:00:00 2001 From: arifBurakDemiray Date: Tue, 19 Sep 2023 11:32:37 +0300 Subject: [PATCH 09/82] feat: init check to events call --- .../main/java/ly/count/sdk/java/Countly.java | 26 ++++++++++++------- .../count/sdk/java/internal/ModuleEvents.java | 15 ++++------- 2 files changed, 21 insertions(+), 20 deletions(-) diff --git a/sdk-java/src/main/java/ly/count/sdk/java/Countly.java b/sdk-java/src/main/java/ly/count/sdk/java/Countly.java index e31bd497f..2f2c4e536 100644 --- a/sdk-java/src/main/java/ly/count/sdk/java/Countly.java +++ b/sdk-java/src/main/java/ly/count/sdk/java/Countly.java @@ -320,22 +320,28 @@ public static void onConsentRemoval(Config.Feature... features) { */ @Override public Event event(String key) { - L.d("[Cly] event: key = " + key); + L.d("[Countly] event: key = " + key); return ((Session) sdk.session(ctx, null)).event(key); } /** * Event module calls * - * @return event module + * @return event module otherwise null if SDK is not initialized */ public ModuleEvents.Events events() { + if(!isInitialized()){ + if (L != null) { + L.e("[Countly] SDK is not initialized yet."); + } + return null; + } return sdk.events(); } @Override public Event timedEvent(String key) { - L.d("[Cly] timedEvent: key = " + key); + L.d("[Countly] timedEvent: key = " + key); return ((Session) sdk.session(ctx, null)).timedEvent(key); } @@ -348,43 +354,43 @@ public Event timedEvent(String key) { */ @Override public User user() { - L.d("[Cly] user"); + L.d("[Countly] user"); return ((Session) sdk.session(ctx, null)).user(); } @Override public Usage addParam(String key, Object value) { - L.d("[Cly] addParam: key = " + key + " value = " + value); + L.d("[Countly] addParam: key = " + key + " value = " + value); return ((Session) sdk.session(ctx, null)).addParam(key, value); } @Override public Usage addCrashReport(Throwable t, boolean fatal) { - L.d("[Cly] addCrashReport: t = " + t + " fatal = " + fatal); + L.d("[Countly] addCrashReport: t = " + t + " fatal = " + fatal); return ((Session) sdk.session(ctx, null)).addCrashReport(t, fatal); } @Override public Usage addCrashReport(Throwable t, boolean fatal, String name, Map segments, String... logs) { - L.d("[Cly] addCrashReport: t = " + t + " fatal = " + fatal + " name = " + name + " segments = " + segments + " logs = " + logs); + L.d("[Countly] addCrashReport: t = " + t + " fatal = " + fatal + " name = " + name + " segments = " + segments + " logs = " + logs); return ((Session) sdk.session(ctx, null)).addCrashReport(t, fatal, name, segments, logs); } @Override public Usage addLocation(double latitude, double longitude) { - L.d("[Cly] addLocation: latitude = " + latitude + " longitude = " + longitude); + L.d("[Countly] addLocation: latitude = " + latitude + " longitude = " + longitude); return ((Session) sdk.session(ctx, null)).addLocation(latitude, longitude); } @Override public View view(String name, boolean start) { - L.d("[Cly] view: name = " + name + " start = " + start); + L.d("[Countly] view: name = " + name + " start = " + start); return ((Session) sdk.session(ctx, null)).view(name, start); } @Override public View view(String name) { - L.d("[Cly] view: name = " + name); + L.d("[Countly] view: name = " + name); return ((Session) sdk.session(ctx, null)).view(name); } } diff --git a/sdk-java/src/main/java/ly/count/sdk/java/internal/ModuleEvents.java b/sdk-java/src/main/java/ly/count/sdk/java/internal/ModuleEvents.java index 4242d0896..6e51af4eb 100644 --- a/sdk-java/src/main/java/ly/count/sdk/java/internal/ModuleEvents.java +++ b/sdk-java/src/main/java/ly/count/sdk/java/internal/ModuleEvents.java @@ -14,24 +14,19 @@ import org.json.JSONArray; public class ModuleEvents extends ModuleBase { - protected InternalConfig internalConfig = null; protected CtxCore ctx = null; - - //disabled is set when a empty module is created - //in instances when the rating feature was not enabled - //when a module is disabled, developer facing functions do nothing - protected boolean disabledModule = false; + protected final Queue eventQueues = new ArrayDeque<>(); private ScheduledExecutorService executor = null; - protected Events eventsInterface = new Events(); + protected Events eventsInterface = null; @Override public void init(InternalConfig config, Log logger) { super.init(config, logger); - internalConfig = config; L.d("[ModuleEvents] init: config = " + config); + eventsInterface = new Events(); } @Override @@ -276,7 +271,7 @@ public boolean startEvent(final String key) { * @return true if event with this key has been previously started, false otherwise */ public boolean endEvent(final String key) { - return endEventInternal(key,null,1,0); + return endEventInternal(key, null, 1, 0); } /** @@ -291,7 +286,7 @@ public boolean endEvent(final String key) { * @throws IllegalArgumentException if key is null or empty, count is less than 1, or if segmentation contains null or empty keys or values */ public boolean endEvent(final String key, final Map segmentation, final int count, final double sum) { - return endEventInternal(key,segmentation,count,sum); + return endEventInternal(key, segmentation, count, sum); } /** From a094d0ba775ea656ba6f67c987f6deccedafb9c6 Mon Sep 17 00:00:00 2001 From: arifBurakDemiray Date: Tue, 19 Sep 2023 14:49:49 +0300 Subject: [PATCH 10/82] feat: event impl queue to store it in the future --- .../sdk/java/internal/EventImplQueue.java | 63 +++++++++++++++++++ 1 file changed, 63 insertions(+) create mode 100644 sdk-java/src/main/java/ly/count/sdk/java/internal/EventImplQueue.java diff --git a/sdk-java/src/main/java/ly/count/sdk/java/internal/EventImplQueue.java b/sdk-java/src/main/java/ly/count/sdk/java/internal/EventImplQueue.java new file mode 100644 index 000000000..9ce59c99d --- /dev/null +++ b/sdk-java/src/main/java/ly/count/sdk/java/internal/EventImplQueue.java @@ -0,0 +1,63 @@ +package ly.count.sdk.java.internal; + +import ly.count.sdk.java.Event; +import org.json.JSONArray; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.ObjectOutputStream; +import java.util.ArrayDeque; +import java.util.Queue; + +public class EventImplQueue implements Storable { + + private final Log L; + + private Long id = System.currentTimeMillis(); + + final Queue events = new ArrayDeque<>(); + + protected EventImplQueue(Log logger) { + L = logger; + } + + @Override + public Long storageId() { + return id; + } + + @Override + public String storagePrefix() { + return "events"; + } + + @Override + public void setId(Long id) { + //do nothing + } + + @Override + public byte[] store(Log L) { + JSONArray events = new JSONArray(); + for (EventImpl event : this.events) { + events.put(event.toJSON(L)); + } + + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + try { + ObjectOutputStream oos = new ObjectOutputStream(baos); + oos.writeObject(events); + oos.close(); + } catch (IOException e) { + L.e("[EventImplQueue] Failed to serialize timed events " + e.getMessage()); + } + + events.clear(); + id = System.currentTimeMillis(); + return baos.toByteArray(); + } + + @Override + public boolean restore(byte[] data, Log L) { + return false; + } +} From 70b251911b96042d22167ad2e3d848b9ffc7139f Mon Sep 17 00:00:00 2001 From: arifBurakDemiray Date: Tue, 19 Sep 2023 14:50:35 +0300 Subject: [PATCH 11/82] refactor: format and add java 8 way refactorings --- .../main/java/ly/count/sdk/java/Countly.java | 2 +- .../count/sdk/java/internal/ModuleEvents.java | 111 +++++++----------- 2 files changed, 44 insertions(+), 69 deletions(-) diff --git a/sdk-java/src/main/java/ly/count/sdk/java/Countly.java b/sdk-java/src/main/java/ly/count/sdk/java/Countly.java index 2f2c4e536..f019052c4 100644 --- a/sdk-java/src/main/java/ly/count/sdk/java/Countly.java +++ b/sdk-java/src/main/java/ly/count/sdk/java/Countly.java @@ -330,7 +330,7 @@ public Event event(String key) { * @return event module otherwise null if SDK is not initialized */ public ModuleEvents.Events events() { - if(!isInitialized()){ + if (!isInitialized()) { if (L != null) { L.e("[Countly] SDK is not initialized yet."); } diff --git a/sdk-java/src/main/java/ly/count/sdk/java/internal/ModuleEvents.java b/sdk-java/src/main/java/ly/count/sdk/java/internal/ModuleEvents.java index 6e51af4eb..7b9402aba 100644 --- a/sdk-java/src/main/java/ly/count/sdk/java/internal/ModuleEvents.java +++ b/sdk-java/src/main/java/ly/count/sdk/java/internal/ModuleEvents.java @@ -1,5 +1,6 @@ package ly.count.sdk.java.internal; +import java.awt.*; import java.util.ArrayDeque; import java.util.ArrayList; import java.util.Calendar; @@ -10,13 +11,16 @@ import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; +import ly.count.sdk.java.Config; import ly.count.sdk.java.Countly; import org.json.JSONArray; +import org.json.JSONObject; public class ModuleEvents extends ModuleBase { protected CtxCore ctx = null; - - protected final Queue eventQueues = new ArrayDeque<>(); + + protected EventImplQueue eventQueue; private ScheduledExecutorService executor = null; @@ -26,6 +30,7 @@ public class ModuleEvents extends ModuleBase { public void init(InternalConfig config, Log logger) { super.init(config, logger); L.d("[ModuleEvents] init: config = " + config); + eventQueue = new EventImplQueue(L); eventsInterface = new Events(); } @@ -45,87 +50,51 @@ public void run() { } } + @Override + public Boolean onRequest(Request request) { + return true; + } + private synchronized void addEventsToRequestQ() { L.d("[ModuleEvents] addEventsToRequestQ"); - JSONArray events = new JSONArray(); - - if (eventQueues.isEmpty()) { - return; - } - - for (EventImpl event : eventQueues) { - events.put(event.toJSON(L)); - } Request request = new Request(); request.params.add("device_id", Countly.instance().getDeviceId()); - request.params.add("events", events); - addTimeInfoIntoRequest(request, System.currentTimeMillis()); + request.params.arr("events").put(eventQueue.events).add(); request.own(ModuleEvents.class); - addRequestToRequestQ(request); - - eventQueues.clear(); - } - - private void addTimeInfoIntoRequest(Request request, Long timestamp) { - Calendar calendar = Calendar.getInstance(); - calendar.setTimeInMillis(timestamp); - final int hour = calendar.get(Calendar.HOUR_OF_DAY); - final int dow = calendar.get(Calendar.DAY_OF_WEEK) - 1; + ModuleRequests.pushAsync(ctx, request); - request.params.add("dow", dow); - request.params.add("hour", hour); - request.params.add("timestamp", timestamp); - request.params.add("tz", Device.dev.getTimezoneOffset()); + eventQueue.events.clear(); } - private void addRequestToRequestQ(Request request) { - synchronized (SDKCore.instance.lockBRQStorage) { - L.d("[ModuleEvents] addRequestToRequestQ"); - if (internalConfig.getRequestQueueMaxSize() == SDKCore.instance.requestQueueMemory.size()) { - L.d("[ModuleEvents] addRequestToRequestQ: In Memory request queue is full, dropping oldest request: " + request.params.toString()); - SDKCore.instance.requestQueueMemory.remove(); - } - - SDKCore.instance.requestQueueMemory.add(request); - SDKCore.instance.networking.check(ctx); - } - } - - protected Map removeInvalidDataFromSegments(Map segments) { + protected void removeInvalidDataFromSegments(Map segments) { if (segments == null || segments.isEmpty()) { - return segments; + return; } - int i = 0; - List toRemove = new ArrayList<>(); - for (Map.Entry item : segments.entrySet()) { - Object type = item.getValue(); - - boolean isValidDataType = (type instanceof Boolean - || type instanceof Integer - || type instanceof Long - || type instanceof String - || type instanceof Double - || type instanceof Float - ); - - if (!isValidDataType) { - toRemove.add(item.getKey()); - L.w("[ModuleEvents] RemoveSegmentInvalidDataTypes: In segmentation Data type '" + type + "' of item '" + item.getValue() + "' isn't valid."); - } - } + List toRemove = segments.entrySet().stream() + .filter(entry -> !isValidDataType(entry.getValue())) + .map(Map.Entry::getKey) + .collect(Collectors.toList()); - for (String k : toRemove) { - segments.remove(k); - } + toRemove.forEach(key -> { + L.w("[ModuleEvents] RemoveSegmentInvalidDataTypes: In segmentation Data type '" + segments.get(key) + "' of item '" + key + "' isn't valid."); + segments.remove(key); + }); + } - return segments; + private boolean isValidDataType(Object value) { + return value instanceof Boolean + || value instanceof Integer + || value instanceof Long + || value instanceof String + || value instanceof Double + || value instanceof Float; } - private void recordEventInternal(String key, int count, double sum, Map segmentation, double dur) { + protected void recordEventInternal(String key, int count, double sum, Map segmentation, double dur) { removeInvalidDataFromSegments(segmentation); if (count <= 0) { L.w("[ModuleEvents] recordEventInternal: Count can't be less than 1, ignoring this event."); @@ -146,10 +115,16 @@ private void recordEventInternal(String key, int count, double sum, Map= internalConfig.getEventsBufferSize()) { - addEventsToRequestQ(); + private void addEventToQueue(EventImpl event) { + synchronized (eventQueue.storageId()) { + eventQueue.events.add(event); + if (eventQueue.events.size() >= internalConfig.getEventsBufferSize()) { + addEventsToRequestQ(); + //Storage.pushAsync(ctx, eventQueue); + } } } From 51255a354a9e575d46de9c2f2d8f60d118930002 Mon Sep 17 00:00:00 2001 From: arifBurakDemiray Date: Tue, 19 Sep 2023 14:51:02 +0300 Subject: [PATCH 12/82] refactor: deprecate session events and add new way of event addind --- .../count/sdk/java/internal/SessionImpl.java | 28 ++++++++++++------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/sdk-java/src/main/java/ly/count/sdk/java/internal/SessionImpl.java b/sdk-java/src/main/java/ly/count/sdk/java/internal/SessionImpl.java index 0b53d5add..3ab897639 100644 --- a/sdk-java/src/main/java/ly/count/sdk/java/internal/SessionImpl.java +++ b/sdk-java/src/main/java/ly/count/sdk/java/internal/SessionImpl.java @@ -6,12 +6,14 @@ import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; import ly.count.sdk.java.Config; +import ly.count.sdk.java.Countly; import ly.count.sdk.java.Event; import ly.count.sdk.java.Session; import ly.count.sdk.java.Usage; @@ -286,6 +288,12 @@ protected TimedEvents timedEvents() { return SDKCore.instance.timedEvents(); } + /** + * Record event to session. + * + * @param event + * @deprecated use {@link ModuleEvents.Events#recordEvent(String, int, double, Map, double)} instead + */ @Override public void recordEvent(Event event) { L.d("[SessionImpl] recordEvent: " + event.toString()); @@ -293,21 +301,21 @@ public void recordEvent(Event event) { L.i("[SessionImpl] recordEvent: Skipping event - feature is not enabled"); return; } + + if (!Countly.isInitialized()) { + L.e("[SessionImpl] recordEvent: Countly is not initialized"); + return; + } + if (began == null) { begin(); } - synchronized (storageId()) { - events.add(event); - if (pushOnChange) { - Storage.pushAsync(ctx, this); - } + ModuleEvents eventsModule = (ModuleEvents) SDKCore.instance.module(CoreFeature.Events.getIndex()); + EventImpl eventImpl = (EventImpl) event; + Map segmentation = new HashMap<>(eventImpl.segmentation); - Config config = SDKCore.instance.config(); - if (config != null && ctx.getConfig().getEventsBufferSize() <= events.size()) { - update(); - } - } + eventsModule.recordEventInternal(eventImpl.key, eventImpl.count, eventImpl.sum, segmentation, eventImpl.timestamp); } @Override From 998e08b51bfdd237cd45001ed32b18975015b5e2 Mon Sep 17 00:00:00 2001 From: arifBurakDemiray Date: Tue, 19 Sep 2023 14:59:41 +0300 Subject: [PATCH 13/82] feat: add deprecated changelog --- CHANGELOG.md | 10 ++++++++++ sdk-java/src/main/java/ly/count/sdk/java/Event.java | 9 +++++++++ 2 files changed, 19 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index aaa72ce19..214d3dc17 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,16 @@ * In Countly class, the old "init(directory,config)" method is deprecated, use "init(config)" instead via "instance()" call. +* The following methods are deprecated from the "Event" interface: + * "record" + * "endAndRecord" + * "addSegment" + * "addSegments" + * "setSegmentation" + * "setSum" + * "setCount" + * "setDuration" + 22.09.2 * Fixed internal log calls that did not respect the configured log level and did not work with the log listener. diff --git a/sdk-java/src/main/java/ly/count/sdk/java/Event.java b/sdk-java/src/main/java/ly/count/sdk/java/Event.java index 40ea98a4f..106de4640 100644 --- a/sdk-java/src/main/java/ly/count/sdk/java/Event.java +++ b/sdk-java/src/main/java/ly/count/sdk/java/Event.java @@ -1,5 +1,6 @@ package ly.count.sdk.java; +import ly.count.sdk.java.internal.ModuleEvents; import javax.annotation.Nonnull; import java.util.Map; @@ -11,6 +12,7 @@ public interface Event { /** * Add event to the buffer, send it to the server in case number of events in the session * is equal or bigger than {@link Config#eventsBufferSize} or wait until next {@link Session#update()}. + * @deprecated this function is deprecated, use {@link ModuleEvents.Events#recordEvent(String, int, double, Map, double)} instead */ void record(); @@ -19,6 +21,7 @@ public interface Event { * and current time in seconds. Then add the event to its session (if they're enabled), * send it to the server in case number of events in the session is equal or bigger * than {@link Config#eventsBufferSize} or wait until next {@link Session#update()}. + * @deprecated this function is deprecated, use {@link ModuleEvents.Events#endEvent(String, Map, int, double)} instead */ void endAndRecord(); @@ -28,6 +31,7 @@ public interface Event { * @param key key of segment, must not be null or empty * @param value value of segment, must not be null or empty * @return this instance for method chaining + * @deprecated this function is deprecated, use {@link ModuleEvents.Events#recordEvent(String, int, double, Map, double)} instead */ Event addSegment(@Nonnull String key, @Nonnull String value); @@ -38,6 +42,7 @@ public interface Event { * segmentation from; cannot contain nulls or empty strings; must have * even length * @return this instance for method chaining + * @deprecated this function is deprecated, use {@link ModuleEvents.Events#recordEvent(String, int, double, Map, double)} instead */ Event addSegments(@Nonnull String... segmentation); @@ -46,6 +51,7 @@ public interface Event { * * @param segmentation map of segment pairs ({key1: value1, key2: value2} * @return this instance for method chaining + * @deprecated this function is deprecated, use {@link ModuleEvents.Events#recordEvent(String, int, double, Map, double)} instead */ Event setSegmentation(@Nonnull Map segmentation); @@ -54,6 +60,7 @@ public interface Event { * * @param count event count, cannot be 0 * @return this instance for method chaining + * @deprecated this function is deprecated, use {@link ModuleEvents.Events#recordEvent(String, int, double, Map, double)} instead */ Event setCount(int count); @@ -62,6 +69,7 @@ public interface Event { * * @param sum event sum * @return this instance for method chaining + * @deprecated this function is deprecated, use {@link ModuleEvents.Events#recordEvent(String, int, double, Map, double)} instead */ Event setSum(double sum); @@ -70,6 +78,7 @@ public interface Event { * * @param duration event duration * @return this instance for method chaining + * @deprecated this function is deprecated, use {@link ModuleEvents.Events#recordEvent(String, int, double, Map, double)} instead */ Event setDuration(double duration); From 1046cbdc3ddd2d96e61de07e41ca09b0c30ed1b3 Mon Sep 17 00:00:00 2001 From: arifBurakDemiray Date: Tue, 19 Sep 2023 15:02:39 +0300 Subject: [PATCH 14/82] feat: add deprecated call for event --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 214d3dc17..84f8c75a6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ * "removeFromCohort(key)" * In Countly class, the old "init(directory,config)" method is deprecated, use "init(config)" instead via "instance()" call. +* Deprecated "Countly::event" call, deprecated builder pattern. Use "Countly::events" instead. * The following methods are deprecated from the "Event" interface: * "record" From 5f85c5883c04584c9f43c1daa3bc91c3dac559d4 Mon Sep 17 00:00:00 2001 From: arifBurakDemiray Date: Tue, 19 Sep 2023 15:08:31 +0300 Subject: [PATCH 15/82] feat: map in a beauty way, --- .../count/sdk/java/internal/ModuleEvents.java | 25 +++++++------------ 1 file changed, 9 insertions(+), 16 deletions(-) diff --git a/sdk-java/src/main/java/ly/count/sdk/java/internal/ModuleEvents.java b/sdk-java/src/main/java/ly/count/sdk/java/internal/ModuleEvents.java index 7b9402aba..95095f813 100644 --- a/sdk-java/src/main/java/ly/count/sdk/java/internal/ModuleEvents.java +++ b/sdk-java/src/main/java/ly/count/sdk/java/internal/ModuleEvents.java @@ -1,21 +1,13 @@ package ly.count.sdk.java.internal; -import java.awt.*; -import java.util.ArrayDeque; -import java.util.ArrayList; -import java.util.Calendar; import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.Queue; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; -import ly.count.sdk.java.Config; import ly.count.sdk.java.Countly; -import org.json.JSONArray; -import org.json.JSONObject; public class ModuleEvents extends ModuleBase { protected CtxCore ctx = null; @@ -95,7 +87,6 @@ private boolean isValidDataType(Object value) { } protected void recordEventInternal(String key, int count, double sum, Map segmentation, double dur) { - removeInvalidDataFromSegments(segmentation); if (count <= 0) { L.w("[ModuleEvents] recordEventInternal: Count can't be less than 1, ignoring this event."); return; @@ -106,16 +97,18 @@ protected void recordEventInternal(String key, int count, double sum, Map mappedSegmentation = new HashMap<>(); + removeInvalidDataFromSegments(segmentation); + EventImpl event = new EventImpl(key, count, sum, dur, mapify(segmentation), L); + addEventToQueue(event); + } - if (segmentation != null) { - for (Map.Entry entry : segmentation.entrySet()) { - mappedSegmentation.put(entry.getKey(), entry.getValue().toString()); - } + private Map mapify(Map segmentation) { + if (segmentation == null) { + return new HashMap<>(); } - EventImpl event = new EventImpl(key, count, sum, dur, mappedSegmentation, L); - addEventToQueue(event); + return segmentation.entrySet().stream() + .collect(Collectors.toMap(Map.Entry::getKey, entry -> entry.getValue().toString())); } private void addEventToQueue(EventImpl event) { From 661e8c55b5479c7644388f6a7e33688ac92a409b Mon Sep 17 00:00:00 2001 From: arifBurakDemiray Date: Tue, 19 Sep 2023 15:12:31 +0300 Subject: [PATCH 16/82] feat: info logs --- .../main/java/ly/count/sdk/java/internal/ModuleEvents.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/sdk-java/src/main/java/ly/count/sdk/java/internal/ModuleEvents.java b/sdk-java/src/main/java/ly/count/sdk/java/internal/ModuleEvents.java index 95095f813..f0e80dd6c 100644 --- a/sdk-java/src/main/java/ly/count/sdk/java/internal/ModuleEvents.java +++ b/sdk-java/src/main/java/ly/count/sdk/java/internal/ModuleEvents.java @@ -145,6 +145,7 @@ public class Events { * @param segmentation additional segmentation data that you want to set, leave null if you don't want to add anything */ public void recordEvent(String key, int count, double sum, Map segmentation, double dur) { + L.i("[Events] recordEvent: key = " + key + ", count = " + count + ", sum = " + sum + ", segmentation = " + segmentation + ", dur = " + dur); if (!Countly.isInitialized()) { L.e("[Events] recordEvent: Countly.instance().init must be called before recordEvent"); return; @@ -229,6 +230,7 @@ public void recordEvent(String key, double sum, int count) { * @return true if no event with this key existed before and event is started, false otherwise */ public boolean startEvent(final String key) { + L.i("[Events] startEvent: key = " + key); return startEventInternal(key); } @@ -239,6 +241,7 @@ public boolean startEvent(final String key) { * @return true if event with this key has been previously started, false otherwise */ public boolean endEvent(final String key) { + L.i("[Events] endEvent: key = " + key); return endEventInternal(key, null, 1, 0); } @@ -254,6 +257,7 @@ public boolean endEvent(final String key) { * @throws IllegalArgumentException if key is null or empty, count is less than 1, or if segmentation contains null or empty keys or values */ public boolean endEvent(final String key, final Map segmentation, final int count, final double sum) { + L.i("[Events] endEvent: key = " + key + ", segmentation = " + segmentation + ", count = " + count + ", sum = " + sum); return endEventInternal(key, segmentation, count, sum); } @@ -263,6 +267,7 @@ public boolean endEvent(final String key, final Map segmentation * @return true if event with this key has been previously started, false otherwise **/ public boolean cancelEvent(final String key) { + L.i("[Events] cancelEvent: key = " + key); return cancelEventInternal(key); } } From 07b25518bc4b9a98963eeccb1b6756d088d1ae18 Mon Sep 17 00:00:00 2001 From: arifBurakDemiray Date: Tue, 19 Sep 2023 15:23:25 +0300 Subject: [PATCH 17/82] feat: timed events additionb --- .../ly/count/sdk/java/internal/EventImpl.java | 2 + .../count/sdk/java/internal/ModuleEvents.java | 52 ++++++++++++++++--- 2 files changed, 47 insertions(+), 7 deletions(-) diff --git a/sdk-java/src/main/java/ly/count/sdk/java/internal/EventImpl.java b/sdk-java/src/main/java/ly/count/sdk/java/internal/EventImpl.java index 2b97c425b..4d93a8f73 100644 --- a/sdk-java/src/main/java/ly/count/sdk/java/internal/EventImpl.java +++ b/sdk-java/src/main/java/ly/count/sdk/java/internal/EventImpl.java @@ -52,6 +52,8 @@ public interface EventRecorder { this.dow = Device.dev.currentDayOfWeek(); } + + EventImpl(@Nonnull EventRecorder recorder, @Nonnull String key, @Nonnull Log givenL) { L = givenL; if (recorder == null) { diff --git a/sdk-java/src/main/java/ly/count/sdk/java/internal/ModuleEvents.java b/sdk-java/src/main/java/ly/count/sdk/java/internal/ModuleEvents.java index f0e80dd6c..86086da28 100644 --- a/sdk-java/src/main/java/ly/count/sdk/java/internal/ModuleEvents.java +++ b/sdk-java/src/main/java/ly/count/sdk/java/internal/ModuleEvents.java @@ -14,6 +14,7 @@ public class ModuleEvents extends ModuleBase { protected EventImplQueue eventQueue; + static final Map timedEvents = new HashMap<>(); private ScheduledExecutorService executor = null; protected Events eventsInterface = null; @@ -121,18 +122,55 @@ private void addEventToQueue(EventImpl event) { } } - private boolean startEventInternal(String key) { - return false; + boolean startEventInternal(final String key) { + if (key == null || key.isEmpty()) { + L.e("[ModuleEvents] Can't start event with a null or empty key"); + return false; + } + if (timedEvents.containsKey(key)) { + return false; + } + L.d("[ModuleEvents] Starting event: [" + key + "]"); + timedEvents.put(key, new EventImpl(null, key, L)); + return true; } - private boolean endEventInternal(final String key, final Map segmentation, final int count, final double sum) { - return false; - } + boolean endEventInternal(final String key, final Map segmentation, final int count, final double sum) { + L.d("[ModuleEvents] Ending event: [" + key + "]"); + + if (key == null || key.isEmpty()) { + L.e("[ModuleEvents] Can't end event with a null or empty key"); + return false; + } + + EventImpl event = timedEvents.remove(key); - private boolean cancelEventInternal(String key) { - return false; + if (event != null) { + if (count < 1) { + throw new IllegalArgumentException("Countly event count should be greater than zero"); + } + L.d("[ModuleEvents] Ending event: [" + key + "]"); + + long currentTimestamp = Device.dev.uniqueTimestamp(); + double duration = (currentTimestamp - event.timestamp) / 1000.0; + + recordEventInternal(key, count, sum, segmentation, duration); + return true; + } else { + return false; + } } + boolean cancelEventInternal(final String key) { + if (key == null || key.isEmpty()) { + L.e("[ModuleEvents] Can't cancel event with a null or empty key"); + return false; + } + + EventImpl event = timedEvents.remove(key); + + return event != null; + } public class Events { /** From ffb893c75412dffead9b3ad5e2d1cc2b6d780380 Mon Sep 17 00:00:00 2001 From: arifBurakDemiray Date: Tue, 19 Sep 2023 15:38:17 +0300 Subject: [PATCH 18/82] fix: remove unnecesaary check --- .../main/java/ly/count/sdk/java/internal/ModuleEvents.java | 5 ----- 1 file changed, 5 deletions(-) diff --git a/sdk-java/src/main/java/ly/count/sdk/java/internal/ModuleEvents.java b/sdk-java/src/main/java/ly/count/sdk/java/internal/ModuleEvents.java index 86086da28..b3179ba0e 100644 --- a/sdk-java/src/main/java/ly/count/sdk/java/internal/ModuleEvents.java +++ b/sdk-java/src/main/java/ly/count/sdk/java/internal/ModuleEvents.java @@ -184,11 +184,6 @@ public class Events { */ public void recordEvent(String key, int count, double sum, Map segmentation, double dur) { L.i("[Events] recordEvent: key = " + key + ", count = " + count + ", sum = " + sum + ", segmentation = " + segmentation + ", dur = " + dur); - if (!Countly.isInitialized()) { - L.e("[Events] recordEvent: Countly.instance().init must be called before recordEvent"); - return; - } - recordEventInternal(key, count, sum, segmentation, dur); } From 5d943754e0c10ad941e1cde03c4b194cad67b45a Mon Sep 17 00:00:00 2001 From: arifBurakDemiray Date: Tue, 19 Sep 2023 15:41:22 +0300 Subject: [PATCH 19/82] feat: remove unnecessary check --- .../main/java/ly/count/sdk/java/internal/SessionImpl.java | 5 ----- 1 file changed, 5 deletions(-) diff --git a/sdk-java/src/main/java/ly/count/sdk/java/internal/SessionImpl.java b/sdk-java/src/main/java/ly/count/sdk/java/internal/SessionImpl.java index 3ab897639..bb599d2e1 100644 --- a/sdk-java/src/main/java/ly/count/sdk/java/internal/SessionImpl.java +++ b/sdk-java/src/main/java/ly/count/sdk/java/internal/SessionImpl.java @@ -302,11 +302,6 @@ public void recordEvent(Event event) { return; } - if (!Countly.isInitialized()) { - L.e("[SessionImpl] recordEvent: Countly is not initialized"); - return; - } - if (began == null) { begin(); } From 10b113df8ba8b75f539a5d6547b3620fbb9543c8 Mon Sep 17 00:00:00 2001 From: arifBurakDemiray Date: Tue, 19 Sep 2023 15:53:19 +0300 Subject: [PATCH 20/82] fix: add correct param --- .../src/main/java/ly/count/sdk/java/internal/SessionImpl.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk-java/src/main/java/ly/count/sdk/java/internal/SessionImpl.java b/sdk-java/src/main/java/ly/count/sdk/java/internal/SessionImpl.java index bb599d2e1..0e9ee4bab 100644 --- a/sdk-java/src/main/java/ly/count/sdk/java/internal/SessionImpl.java +++ b/sdk-java/src/main/java/ly/count/sdk/java/internal/SessionImpl.java @@ -310,7 +310,7 @@ public void recordEvent(Event event) { EventImpl eventImpl = (EventImpl) event; Map segmentation = new HashMap<>(eventImpl.segmentation); - eventsModule.recordEventInternal(eventImpl.key, eventImpl.count, eventImpl.sum, segmentation, eventImpl.timestamp); + eventsModule.recordEventInternal(eventImpl.key, eventImpl.count, eventImpl.sum, segmentation, eventImpl.duration); } @Override From 558b3506d2c5de8e5042c4c9ae1ee7e3dba8d566 Mon Sep 17 00:00:00 2001 From: arifBurakDemiray Date: Tue, 19 Sep 2023 16:56:43 +0300 Subject: [PATCH 21/82] feat: migarete cly to County logs --- .../main/java/ly/count/sdk/java/Countly.java | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/sdk-java/src/main/java/ly/count/sdk/java/Countly.java b/sdk-java/src/main/java/ly/count/sdk/java/Countly.java index 77512c5ab..3d26f980c 100644 --- a/sdk-java/src/main/java/ly/count/sdk/java/Countly.java +++ b/sdk-java/src/main/java/ly/count/sdk/java/Countly.java @@ -313,13 +313,13 @@ public static void onConsentRemoval(Config.Feature... features) { @Override public Event event(String key) { - L.d("[Cly] event: key = " + key); + L.d("[Countly] event: key = " + key); return ((Session) sdk.session(ctx, null)).event(key); } @Override public Event timedEvent(String key) { - L.d("[Cly] timedEvent: key = " + key); + L.d("[Countly] timedEvent: key = " + key); return ((Session) sdk.session(ctx, null)).timedEvent(key); } @@ -332,43 +332,43 @@ public Event timedEvent(String key) { */ @Override public User user() { - L.d("[Cly] user"); + L.d("[Countly] user"); return ((Session) sdk.session(ctx, null)).user(); } @Override public Usage addParam(String key, Object value) { - L.d("[Cly] addParam: key = " + key + " value = " + value); + L.d("[Countly] addParam: key = " + key + " value = " + value); return ((Session) sdk.session(ctx, null)).addParam(key, value); } @Override public Usage addCrashReport(Throwable t, boolean fatal) { - L.d("[Cly] addCrashReport: t = " + t + " fatal = " + fatal); + L.d("[Countly] addCrashReport: t = " + t + " fatal = " + fatal); return ((Session) sdk.session(ctx, null)).addCrashReport(t, fatal); } @Override public Usage addCrashReport(Throwable t, boolean fatal, String name, Map segments, String... logs) { - L.d("[Cly] addCrashReport: t = " + t + " fatal = " + fatal + " name = " + name + " segments = " + segments + " logs = " + logs); + L.d("[Countly] addCrashReport: t = " + t + " fatal = " + fatal + " name = " + name + " segments = " + segments + " logs = " + logs); return ((Session) sdk.session(ctx, null)).addCrashReport(t, fatal, name, segments, logs); } @Override public Usage addLocation(double latitude, double longitude) { - L.d("[Cly] addLocation: latitude = " + latitude + " longitude = " + longitude); + L.d("[Countly] addLocation: latitude = " + latitude + " longitude = " + longitude); return ((Session) sdk.session(ctx, null)).addLocation(latitude, longitude); } @Override public View view(String name, boolean start) { - L.d("[Cly] view: name = " + name + " start = " + start); + L.d("[Countly] view: name = " + name + " start = " + start); return ((Session) sdk.session(ctx, null)).view(name, start); } @Override public View view(String name) { - L.d("[Cly] view: name = " + name); + L.d("[Countly] view: name = " + name); return ((Session) sdk.session(ctx, null)).view(name); } } From 95ace1bc9f94dad8884cd5d134aa48497f3d4356 Mon Sep 17 00:00:00 2001 From: arifBurakDemiray Date: Tue, 19 Sep 2023 16:58:39 +0300 Subject: [PATCH 22/82] revert: Countly to Cly --- .../main/java/ly/count/sdk/java/Countly.java | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/sdk-java/src/main/java/ly/count/sdk/java/Countly.java b/sdk-java/src/main/java/ly/count/sdk/java/Countly.java index f019052c4..ed7ec5874 100644 --- a/sdk-java/src/main/java/ly/count/sdk/java/Countly.java +++ b/sdk-java/src/main/java/ly/count/sdk/java/Countly.java @@ -320,7 +320,7 @@ public static void onConsentRemoval(Config.Feature... features) { */ @Override public Event event(String key) { - L.d("[Countly] event: key = " + key); + L.d("[Cly] event: key = " + key); return ((Session) sdk.session(ctx, null)).event(key); } @@ -341,7 +341,7 @@ public ModuleEvents.Events events() { @Override public Event timedEvent(String key) { - L.d("[Countly] timedEvent: key = " + key); + L.d("[Cly] timedEvent: key = " + key); return ((Session) sdk.session(ctx, null)).timedEvent(key); } @@ -354,43 +354,43 @@ public Event timedEvent(String key) { */ @Override public User user() { - L.d("[Countly] user"); + L.d("[Cly] user"); return ((Session) sdk.session(ctx, null)).user(); } @Override public Usage addParam(String key, Object value) { - L.d("[Countly] addParam: key = " + key + " value = " + value); + L.d("[Cly] addParam: key = " + key + " value = " + value); return ((Session) sdk.session(ctx, null)).addParam(key, value); } @Override public Usage addCrashReport(Throwable t, boolean fatal) { - L.d("[Countly] addCrashReport: t = " + t + " fatal = " + fatal); + L.d("[Cly] addCrashReport: t = " + t + " fatal = " + fatal); return ((Session) sdk.session(ctx, null)).addCrashReport(t, fatal); } @Override public Usage addCrashReport(Throwable t, boolean fatal, String name, Map segments, String... logs) { - L.d("[Countly] addCrashReport: t = " + t + " fatal = " + fatal + " name = " + name + " segments = " + segments + " logs = " + logs); + L.d("[Cly] addCrashReport: t = " + t + " fatal = " + fatal + " name = " + name + " segments = " + segments + " logs = " + logs); return ((Session) sdk.session(ctx, null)).addCrashReport(t, fatal, name, segments, logs); } @Override public Usage addLocation(double latitude, double longitude) { - L.d("[Countly] addLocation: latitude = " + latitude + " longitude = " + longitude); + L.d("[Cly] addLocation: latitude = " + latitude + " longitude = " + longitude); return ((Session) sdk.session(ctx, null)).addLocation(latitude, longitude); } @Override public View view(String name, boolean start) { - L.d("[Countly] view: name = " + name + " start = " + start); + L.d("[Cly] view: name = " + name + " start = " + start); return ((Session) sdk.session(ctx, null)).view(name, start); } @Override public View view(String name) { - L.d("[Countly] view: name = " + name); + L.d("[Cly] view: name = " + name); return ((Session) sdk.session(ctx, null)).view(name); } } From d147821ae07cf210ec08194476be02fdaf349686 Mon Sep 17 00:00:00 2001 From: arifBurakDemiray Date: Tue, 19 Sep 2023 17:04:19 +0300 Subject: [PATCH 23/82] feat: log infos --- .../main/java/ly/count/sdk/java/internal/ModuleEvents.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/sdk-java/src/main/java/ly/count/sdk/java/internal/ModuleEvents.java b/sdk-java/src/main/java/ly/count/sdk/java/internal/ModuleEvents.java index ece05a9ae..58b046fca 100644 --- a/sdk-java/src/main/java/ly/count/sdk/java/internal/ModuleEvents.java +++ b/sdk-java/src/main/java/ly/count/sdk/java/internal/ModuleEvents.java @@ -38,6 +38,7 @@ public class Events { * @param segmentation additional segmentation data that you want to set, leave null if you don't want to add anything */ public void recordEvent(String key, int count, double sum, Map segmentation, double dur) { + L.i("[Events] recordEvent: key = " + key + ", count = " + count + ", sum = " + sum + ", segmentation = " + segmentation + ", dur = " + dur); recordEventInternal(key, count, sum, segmentation, dur); } @@ -117,6 +118,7 @@ public void recordEvent(String key, double sum, int count) { * @return true if no event with this key existed before and event is started, false otherwise */ public boolean startEvent(final String key) { + L.i("[Events] startEvent: key = " + key); return startEventInternal(key); } @@ -127,6 +129,7 @@ public boolean startEvent(final String key) { * @return true if event with this key has been previously started, false otherwise */ public boolean endEvent(final String key) { + L.i("[Events] endEvent: key = " + key); return endEventInternal(key); } @@ -136,6 +139,7 @@ public boolean endEvent(final String key) { * @return true if event with this key has been previously started, false otherwise **/ public boolean cancelEvent(final String key) { + L.i("[Events] cancelEvent: key = " + key); return cancelEventInternal(key); } } From db613c5661929b544a7eab13ce9a02606a426d51 Mon Sep 17 00:00:00 2001 From: arifBurakDemiray Date: Tue, 19 Sep 2023 17:06:15 +0300 Subject: [PATCH 24/82] revert: remove logs to anoother pr --- .../main/java/ly/count/sdk/java/internal/ModuleEvents.java | 4 ---- 1 file changed, 4 deletions(-) diff --git a/sdk-java/src/main/java/ly/count/sdk/java/internal/ModuleEvents.java b/sdk-java/src/main/java/ly/count/sdk/java/internal/ModuleEvents.java index b3179ba0e..3ce5ab8ca 100644 --- a/sdk-java/src/main/java/ly/count/sdk/java/internal/ModuleEvents.java +++ b/sdk-java/src/main/java/ly/count/sdk/java/internal/ModuleEvents.java @@ -183,7 +183,6 @@ public class Events { * @param segmentation additional segmentation data that you want to set, leave null if you don't want to add anything */ public void recordEvent(String key, int count, double sum, Map segmentation, double dur) { - L.i("[Events] recordEvent: key = " + key + ", count = " + count + ", sum = " + sum + ", segmentation = " + segmentation + ", dur = " + dur); recordEventInternal(key, count, sum, segmentation, dur); } @@ -263,7 +262,6 @@ public void recordEvent(String key, double sum, int count) { * @return true if no event with this key existed before and event is started, false otherwise */ public boolean startEvent(final String key) { - L.i("[Events] startEvent: key = " + key); return startEventInternal(key); } @@ -274,7 +272,6 @@ public boolean startEvent(final String key) { * @return true if event with this key has been previously started, false otherwise */ public boolean endEvent(final String key) { - L.i("[Events] endEvent: key = " + key); return endEventInternal(key, null, 1, 0); } @@ -300,7 +297,6 @@ public boolean endEvent(final String key, final Map segmentation * @return true if event with this key has been previously started, false otherwise **/ public boolean cancelEvent(final String key) { - L.i("[Events] cancelEvent: key = " + key); return cancelEventInternal(key); } } From 3e2784554fb6628cd7495c3020c2d140ac178d47 Mon Sep 17 00:00:00 2001 From: arifBurakDemiray Date: Tue, 19 Sep 2023 17:17:50 +0300 Subject: [PATCH 25/82] feat: add deprecation --- CHANGELOG.md | 2 ++ sdk-java/src/main/java/ly/count/sdk/java/Event.java | 1 + sdk-java/src/main/java/ly/count/sdk/java/Usage.java | 2 ++ 3 files changed, 5 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 84f8c75a6..1c90def4a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ * In Countly class, the old "init(directory,config)" method is deprecated, use "init(config)" instead via "instance()" call. * Deprecated "Countly::event" call, deprecated builder pattern. Use "Countly::events" instead. +* Deprecated "Usage::event" call, deprecated builder pattern. Use "Countly::events" instead. * The following methods are deprecated from the "Event" interface: * "record" @@ -20,6 +21,7 @@ * "setSum" * "setCount" * "setDuration" + * "isInvalid" 22.09.2 * Fixed internal log calls that did not respect the configured log level and did not work with the log listener. diff --git a/sdk-java/src/main/java/ly/count/sdk/java/Event.java b/sdk-java/src/main/java/ly/count/sdk/java/Event.java index 106de4640..8389d4b1e 100644 --- a/sdk-java/src/main/java/ly/count/sdk/java/Event.java +++ b/sdk-java/src/main/java/ly/count/sdk/java/Event.java @@ -90,6 +90,7 @@ public interface Event { *
  • Invalid data supplied (count < 0, NaN as sum, duration < 0, etc.) while in production mode
  • *
  • Event has been already recorded in session, thus should be discarded
  • * + * @deprecated this function is deprecated */ boolean isInvalid(); } diff --git a/sdk-java/src/main/java/ly/count/sdk/java/Usage.java b/sdk-java/src/main/java/ly/count/sdk/java/Usage.java index e68d72be4..4210e2fea 100644 --- a/sdk-java/src/main/java/ly/count/sdk/java/Usage.java +++ b/sdk-java/src/main/java/ly/count/sdk/java/Usage.java @@ -1,5 +1,6 @@ package ly.count.sdk.java; +import ly.count.sdk.java.internal.ModuleEvents; import java.util.Map; /** @@ -20,6 +21,7 @@ public interface Usage { * @param key key for this event, cannot be null or empty * @return Event instance. * @see Event#record() + * @deprecated this function is deprecated, use {@link ModuleEvents.Events#recordEvent} instead */ Event event(String key); From d03d18640457a9c88905720afbbc5b448b3b5605 Mon Sep 17 00:00:00 2001 From: arifBurakDemiray Date: Tue, 19 Sep 2023 17:21:58 +0300 Subject: [PATCH 26/82] feat: push util func to utils class --- .../count/sdk/java/internal/ModuleEvents.java | 12 ++-------- .../ly/count/sdk/java/internal/Utils.java | 22 ++++++++++++++----- 2 files changed, 19 insertions(+), 15 deletions(-) diff --git a/sdk-java/src/main/java/ly/count/sdk/java/internal/ModuleEvents.java b/sdk-java/src/main/java/ly/count/sdk/java/internal/ModuleEvents.java index 3ce5ab8ca..3058a622d 100644 --- a/sdk-java/src/main/java/ly/count/sdk/java/internal/ModuleEvents.java +++ b/sdk-java/src/main/java/ly/count/sdk/java/internal/ModuleEvents.java @@ -99,19 +99,10 @@ protected void recordEventInternal(String key, int count, double sum, Map mapify(Map segmentation) { - if (segmentation == null) { - return new HashMap<>(); - } - - return segmentation.entrySet().stream() - .collect(Collectors.toMap(Map.Entry::getKey, entry -> entry.getValue().toString())); - } - private void addEventToQueue(EventImpl event) { synchronized (eventQueue.storageId()) { eventQueue.events.add(event); @@ -171,6 +162,7 @@ boolean cancelEventInternal(final String key) { return event != null; } + public class Events { /** diff --git a/sdk-java/src/main/java/ly/count/sdk/java/internal/Utils.java b/sdk-java/src/main/java/ly/count/sdk/java/internal/Utils.java index 6663937cb..29918c44f 100644 --- a/sdk-java/src/main/java/ly/count/sdk/java/internal/Utils.java +++ b/sdk-java/src/main/java/ly/count/sdk/java/internal/Utils.java @@ -1,18 +1,21 @@ package ly.count.sdk.java.internal; -import ly.count.sdk.java.Config; - import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.UnsupportedEncodingException; import java.lang.reflect.Field; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; import java.net.URLDecoder; import java.net.URLEncoder; import java.security.MessageDigest; -import java.util.*; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; /** * Utility class @@ -42,6 +45,15 @@ public static String join(Collection objects, String separator) { return sb.toString(); } + protected static Map mapify(Map segmentation) { + if (segmentation == null) { + return new HashMap<>(); + } + + return segmentation.entrySet().stream() + .collect(Collectors.toMap(Map.Entry::getKey, entry -> entry.getValue().toString())); + } + /** * URLDecoder wrapper to remove try-catch * From 14c3ec8e30be4bbef41befb98ed5e5f955004d5e Mon Sep 17 00:00:00 2001 From: arifBurakDemiray Date: Tue, 19 Sep 2023 17:28:47 +0300 Subject: [PATCH 27/82] feat: tests for new util --- .../ly/count/sdk/java/internal/Utils.java | 11 ++++++++-- .../count/sdk/java/internal/UtilsTests.java | 21 +++++++++++++++++++ 2 files changed, 30 insertions(+), 2 deletions(-) diff --git a/sdk-java/src/main/java/ly/count/sdk/java/internal/Utils.java b/sdk-java/src/main/java/ly/count/sdk/java/internal/Utils.java index 29918c44f..504d94eb8 100644 --- a/sdk-java/src/main/java/ly/count/sdk/java/internal/Utils.java +++ b/sdk-java/src/main/java/ly/count/sdk/java/internal/Utils.java @@ -45,13 +45,20 @@ public static String join(Collection objects, String separator) { return sb.toString(); } - protected static Map mapify(Map segmentation) { + public static Map mapify(Map segmentation) { if (segmentation == null) { return new HashMap<>(); } return segmentation.entrySet().stream() - .collect(Collectors.toMap(Map.Entry::getKey, entry -> entry.getValue().toString())); + .collect(Collectors.toMap(Map.Entry::getKey, entry -> { + Object value = entry.getValue(); + if (value == null) { + return ""; + } else { + return value.toString(); + } + })); } /** diff --git a/sdk-java/src/test/java/ly/count/sdk/java/internal/UtilsTests.java b/sdk-java/src/test/java/ly/count/sdk/java/internal/UtilsTests.java index efe3acf32..fc50024f5 100644 --- a/sdk-java/src/test/java/ly/count/sdk/java/internal/UtilsTests.java +++ b/sdk-java/src/test/java/ly/count/sdk/java/internal/UtilsTests.java @@ -212,4 +212,25 @@ public void isNotEmpty() { junit.framework.Assert.assertFalse(Utils.isNotEmpty("")); junit.framework.Assert.assertFalse(Utils.isNotEmpty(null)); } + + @Test + public void mapify() { + Map expected = new HashMap() {{ + put("count", "45"); + put("sum", "76.345"); + put("float", "0.2"); + put("long", "2"); + put("null", ""); + }}; + + Map given = new HashMap() {{ + put("count", 45); + put("sum", 76.345); + put("float", .2f); + put("long", 2L); + put("null", null); + }}; + + Assert.assertEquals(expected, Utils.mapify(given)); + } } From ca4e7af0326105b50167ca2a7f5ea62d37de44d4 Mon Sep 17 00:00:00 2001 From: arifBurakDemiray Date: Wed, 20 Sep 2023 09:57:12 +0300 Subject: [PATCH 28/82] feat: convert string to object --- .../ly/count/sdk/java/internal/EventImpl.java | 24 +++++++++++-------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/sdk-java/src/main/java/ly/count/sdk/java/internal/EventImpl.java b/sdk-java/src/main/java/ly/count/sdk/java/internal/EventImpl.java index 7b2bc05b8..f6527f8f1 100644 --- a/sdk-java/src/main/java/ly/count/sdk/java/internal/EventImpl.java +++ b/sdk-java/src/main/java/ly/count/sdk/java/internal/EventImpl.java @@ -1,14 +1,12 @@ package ly.count.sdk.java.internal; -import org.json.JSONException; -import org.json.JSONObject; - import java.util.HashMap; import java.util.Iterator; import java.util.Map; - -import ly.count.sdk.java.Event; import javax.annotation.Nonnull; +import ly.count.sdk.java.Event; +import org.json.JSONException; +import org.json.JSONObject; /** * Event base class implementation @@ -18,7 +16,7 @@ class EventImpl implements Event, JSONable { protected final EventRecorder recorder; protected final String key; - protected Map segmentation; + protected Map segmentation; protected int count; protected Double sum; @@ -292,12 +290,18 @@ public void recordEvent(Event event) { if (!json.isNull(SEGMENTATION_KEY)) { final JSONObject segm = json.getJSONObject(SEGMENTATION_KEY); - final HashMap segmentation = new HashMap(segm.length()); + final HashMap segmentation = new HashMap<>(segm.length()); final Iterator nameItr = segm.keys(); while (nameItr.hasNext()) { final String key = nameItr.next(); if (!segm.isNull(key)) { - segmentation.put(key, segm.getString(key)); + Object segmKeyValue = segm.get(key); + if (Utils.isValidDataType(segmKeyValue)) { + segmentation.put(key, segmKeyValue); + } else { + String errorMessage = "[EventImpl] fromJSON: Invalid data type for segmentation key: " + key + " value: " + segmKeyValue; + L.d(errorMessage); + } } } event.segmentation = segmentation; @@ -331,7 +335,7 @@ public Double getDuration() { return duration; } - public String getSegment(String key) { + public Object getSegment(String key) { return segmentation.get(key); } @@ -347,7 +351,7 @@ public int getDow() { return dow; } - public Map getSegmentation() { + public Map getSegmentation() { return segmentation; } From 5bcebf9931c29c8d3fd6b9207978a84bc234b331 Mon Sep 17 00:00:00 2001 From: arifBurakDemiray Date: Wed, 20 Sep 2023 10:06:14 +0300 Subject: [PATCH 29/82] feat: util funcs --- .../ly/count/sdk/java/internal/Utils.java | 43 ++++++++++++++++--- 1 file changed, 38 insertions(+), 5 deletions(-) diff --git a/sdk-java/src/main/java/ly/count/sdk/java/internal/Utils.java b/sdk-java/src/main/java/ly/count/sdk/java/internal/Utils.java index 6663937cb..45f5c0571 100644 --- a/sdk-java/src/main/java/ly/count/sdk/java/internal/Utils.java +++ b/sdk-java/src/main/java/ly/count/sdk/java/internal/Utils.java @@ -1,18 +1,21 @@ package ly.count.sdk.java.internal; -import ly.count.sdk.java.Config; - import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.UnsupportedEncodingException; import java.lang.reflect.Field; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; import java.net.URLDecoder; import java.net.URLEncoder; import java.security.MessageDigest; -import java.util.*; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; /** * Utility class @@ -275,6 +278,36 @@ public static Map fixSegmentKeysAndValues(final int keyLength, f return segmentation; } + public static Map mapify(Map segmentation) { + if (segmentation == null) { + return new HashMap<>(); + } + + return segmentation.entrySet().stream() + .collect(Collectors.toMap(Map.Entry::getKey, entry -> { + Object value = entry.getValue(); + if (value == null) { + return ""; + } else { + return value.toString(); + } + })); + } + + public static boolean isValidDataType(Object value) { + if (value == null) { + return false; + } + + return + value instanceof Boolean || + value instanceof Integer || + value instanceof Long || + value instanceof String || + value instanceof Double || + value instanceof Float; + } + public static class Base64 { public static String encode(byte[] bytes) { return ly.count.sdk.java.internal.Base64.encodeBytes(bytes); From cd9b8e9b402bde7349394906d5cf4ac6617fa6ec Mon Sep 17 00:00:00 2001 From: arifBurakDemiray Date: Wed, 20 Sep 2023 10:06:37 +0300 Subject: [PATCH 30/82] feat: tests for util and serialization --- .../sdk/java/internal/EventImplTests.java | 35 +++++++----------- .../count/sdk/java/internal/UtilsTests.java | 36 +++++++++++++++++++ 2 files changed, 49 insertions(+), 22 deletions(-) diff --git a/sdk-java/src/test/java/ly/count/sdk/java/internal/EventImplTests.java b/sdk-java/src/test/java/ly/count/sdk/java/internal/EventImplTests.java index 8540cd15c..8b2fb82c0 100644 --- a/sdk-java/src/test/java/ly/count/sdk/java/internal/EventImplTests.java +++ b/sdk-java/src/test/java/ly/count/sdk/java/internal/EventImplTests.java @@ -1,21 +1,12 @@ package ly.count.sdk.java.internal; +import java.util.HashMap; +import java.util.Map; import org.json.JSONObject; import org.junit.Assert; -import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; -import org.powermock.reflect.Whitebox; - -import java.net.URL; -import java.util.HashMap; -import java.util.Map; -import java.util.Objects; -import java.util.Optional; - -import ly.count.sdk.java.Config; -import ly.count.sdk.java.Event; import static org.mockito.Mockito.mock; @@ -115,11 +106,11 @@ public void recorderCalledAfterRecord() { Assert.assertEquals(new Double(21.0), eventImpl1.duration); Assert.assertEquals(new Double(17.0), eventImpl1.sum); Assert.assertEquals("test_event", eventImpl1.key); - Assert.assertEquals("true", eventImpl1.getSegment("test")); + Assert.assertEquals(true, eventImpl1.getSegment("test")); }, "test_event", L); - Map segmentation = new HashMap<>(); - segmentation.put("test", "true"); + Map segmentation = new HashMap<>(); + segmentation.put("test", true); event.count = 5; event.duration = 21.0; @@ -138,8 +129,8 @@ public void validateToJson() { EventImpl event = new EventImpl((event1) -> { }, "test_buy_event", L); - Map segmentation = new HashMap<>(); - segmentation.put("valid", "false"); + Map segmentation = new HashMap<>(); + segmentation.put("valid", false); event.duration = 34.0; event.sum = 9.0; @@ -169,13 +160,13 @@ public void validateFromJson() { EventImpl event = new EventImpl((event1) -> { }, "test_sell_event", L); - Map segmentation = new HashMap<>(); - segmentation.put("sold", "true"); + Map segmentation = new HashMap<>(); + segmentation.put("sold", true); event.count = 3; event.duration = 15.0; event.sum = 7.0; - event.setSegmentation(segmentation); + event.segmentation = segmentation; JSONObject json = new JSONObject(); @@ -210,8 +201,8 @@ public void testGetters() { EventImpl event = new EventImpl((event1) -> { }, "test_getter_event", L); - Map segmentation = new HashMap<>(); - segmentation.put("get_func", "yes"); + Map segmentation = new HashMap<>(); + segmentation.put("get_func", 90); event.count = 47; event.duration = 59.5; @@ -222,7 +213,7 @@ public void testGetters() { Assert.assertEquals(47, event.getCount()); Assert.assertEquals(new Double(59.5), event.getDuration()); Assert.assertEquals(new Double(37.0), event.getSum()); - Assert.assertEquals("yes", event.getSegment("get_func")); + Assert.assertEquals(90, event.getSegment("get_func")); } /** diff --git a/sdk-java/src/test/java/ly/count/sdk/java/internal/UtilsTests.java b/sdk-java/src/test/java/ly/count/sdk/java/internal/UtilsTests.java index efe3acf32..0b967f3d0 100644 --- a/sdk-java/src/test/java/ly/count/sdk/java/internal/UtilsTests.java +++ b/sdk-java/src/test/java/ly/count/sdk/java/internal/UtilsTests.java @@ -212,4 +212,40 @@ public void isNotEmpty() { junit.framework.Assert.assertFalse(Utils.isNotEmpty("")); junit.framework.Assert.assertFalse(Utils.isNotEmpty(null)); } + + @Test + public void mapify() { + Map expected = new HashMap() {{ + put("count", "45"); + put("sum", "76.345"); + put("float", "0.2"); + put("long", "2"); + put("null", ""); + }}; + + Map given = new HashMap() {{ + put("count", 45); + put("sum", 76.345); + put("float", .2f); + put("long", 2L); + put("null", null); + }}; + + Assert.assertEquals(expected, Utils.mapify(given)); + } + + @Test + public void isValidDataType() { + Assert.assertTrue(Utils.isValidDataType("string")); + Assert.assertTrue(Utils.isValidDataType(6)); + Assert.assertTrue(Utils.isValidDataType(6.0)); + Assert.assertTrue(Utils.isValidDataType(6.0f)); + Assert.assertTrue(Utils.isValidDataType(6L)); + Assert.assertTrue(Utils.isValidDataType(true)); + Assert.assertTrue(Utils.isValidDataType(false)); + Assert.assertFalse(Utils.isValidDataType(null)); + Assert.assertFalse(Utils.isValidDataType(new Object())); + Assert.assertFalse(Utils.isValidDataType(new ArrayList<>())); + Assert.assertFalse(Utils.isValidDataType(new HashMap<>())); + } } From a77a9a605431607ca1f909e58b48ef908e4b8419 Mon Sep 17 00:00:00 2001 From: arifBurakDemiray Date: Wed, 20 Sep 2023 10:11:56 +0300 Subject: [PATCH 31/82] chore: reevert --- .../ly/count/sdk/java/internal/Utils.java | 16 -------------- .../count/sdk/java/internal/UtilsTests.java | 21 ------------------- 2 files changed, 37 deletions(-) diff --git a/sdk-java/src/main/java/ly/count/sdk/java/internal/Utils.java b/sdk-java/src/main/java/ly/count/sdk/java/internal/Utils.java index 504d94eb8..ee074e540 100644 --- a/sdk-java/src/main/java/ly/count/sdk/java/internal/Utils.java +++ b/sdk-java/src/main/java/ly/count/sdk/java/internal/Utils.java @@ -45,22 +45,6 @@ public static String join(Collection objects, String separator) { return sb.toString(); } - public static Map mapify(Map segmentation) { - if (segmentation == null) { - return new HashMap<>(); - } - - return segmentation.entrySet().stream() - .collect(Collectors.toMap(Map.Entry::getKey, entry -> { - Object value = entry.getValue(); - if (value == null) { - return ""; - } else { - return value.toString(); - } - })); - } - /** * URLDecoder wrapper to remove try-catch * diff --git a/sdk-java/src/test/java/ly/count/sdk/java/internal/UtilsTests.java b/sdk-java/src/test/java/ly/count/sdk/java/internal/UtilsTests.java index fc50024f5..efe3acf32 100644 --- a/sdk-java/src/test/java/ly/count/sdk/java/internal/UtilsTests.java +++ b/sdk-java/src/test/java/ly/count/sdk/java/internal/UtilsTests.java @@ -212,25 +212,4 @@ public void isNotEmpty() { junit.framework.Assert.assertFalse(Utils.isNotEmpty("")); junit.framework.Assert.assertFalse(Utils.isNotEmpty(null)); } - - @Test - public void mapify() { - Map expected = new HashMap() {{ - put("count", "45"); - put("sum", "76.345"); - put("float", "0.2"); - put("long", "2"); - put("null", ""); - }}; - - Map given = new HashMap() {{ - put("count", 45); - put("sum", 76.345); - put("float", .2f); - put("long", 2L); - put("null", null); - }}; - - Assert.assertEquals(expected, Utils.mapify(given)); - } } From f6879945ee54f68ad08298bbfdd9a29a72e80314 Mon Sep 17 00:00:00 2001 From: arifBurakDemiray Date: Wed, 20 Sep 2023 10:15:06 +0300 Subject: [PATCH 32/82] feat: usage of util send obj segm --- .../java/ly/count/sdk/java/internal/EventImpl.java | 2 +- .../ly/count/sdk/java/internal/ModuleEvents.java | 13 ++----------- 2 files changed, 3 insertions(+), 12 deletions(-) diff --git a/sdk-java/src/main/java/ly/count/sdk/java/internal/EventImpl.java b/sdk-java/src/main/java/ly/count/sdk/java/internal/EventImpl.java index 202a8ab36..ff411b5df 100644 --- a/sdk-java/src/main/java/ly/count/sdk/java/internal/EventImpl.java +++ b/sdk-java/src/main/java/ly/count/sdk/java/internal/EventImpl.java @@ -38,7 +38,7 @@ public interface EventRecorder { */ private boolean invalid = false; - EventImpl(@Nonnull String key, int count, double sum, double duration, @Nonnull Map segmentation, @Nonnull Log givenL) { + EventImpl(@Nonnull String key, int count, double sum, double duration, @Nonnull Map segmentation, @Nonnull Log givenL) { L = givenL; this.recorder = null; diff --git a/sdk-java/src/main/java/ly/count/sdk/java/internal/ModuleEvents.java b/sdk-java/src/main/java/ly/count/sdk/java/internal/ModuleEvents.java index 3058a622d..8bb2e0628 100644 --- a/sdk-java/src/main/java/ly/count/sdk/java/internal/ModuleEvents.java +++ b/sdk-java/src/main/java/ly/count/sdk/java/internal/ModuleEvents.java @@ -68,7 +68,7 @@ protected void removeInvalidDataFromSegments(Map segments) { } List toRemove = segments.entrySet().stream() - .filter(entry -> !isValidDataType(entry.getValue())) + .filter(entry -> !Utils.isValidDataType(entry.getValue())) .map(Map.Entry::getKey) .collect(Collectors.toList()); @@ -78,15 +78,6 @@ protected void removeInvalidDataFromSegments(Map segments) { }); } - private boolean isValidDataType(Object value) { - return value instanceof Boolean - || value instanceof Integer - || value instanceof Long - || value instanceof String - || value instanceof Double - || value instanceof Float; - } - protected void recordEventInternal(String key, int count, double sum, Map segmentation, double dur) { if (count <= 0) { L.w("[ModuleEvents] recordEventInternal: Count can't be less than 1, ignoring this event."); @@ -99,7 +90,7 @@ protected void recordEventInternal(String key, int count, double sum, Map Date: Wed, 20 Sep 2023 10:21:34 +0300 Subject: [PATCH 33/82] fix: merge issue --- .../src/main/java/ly/count/sdk/java/internal/ModuleEvents.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk-java/src/main/java/ly/count/sdk/java/internal/ModuleEvents.java b/sdk-java/src/main/java/ly/count/sdk/java/internal/ModuleEvents.java index 8bb2e0628..a2e317989 100644 --- a/sdk-java/src/main/java/ly/count/sdk/java/internal/ModuleEvents.java +++ b/sdk-java/src/main/java/ly/count/sdk/java/internal/ModuleEvents.java @@ -255,7 +255,7 @@ public boolean startEvent(final String key) { * @return true if event with this key has been previously started, false otherwise */ public boolean endEvent(final String key) { - return endEventInternal(key, null, 1, 0); + return endEventInternal(key); } /** From 3e93ed651f751adff67c4bcee3176ffc80da69d9 Mon Sep 17 00:00:00 2001 From: arifBurakDemiray Date: Wed, 20 Sep 2023 10:22:50 +0300 Subject: [PATCH 34/82] fix: undo merge conflict changes --- .../src/main/java/ly/count/sdk/java/internal/ModuleEvents.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk-java/src/main/java/ly/count/sdk/java/internal/ModuleEvents.java b/sdk-java/src/main/java/ly/count/sdk/java/internal/ModuleEvents.java index eaf3e4ea8..f2357a505 100644 --- a/sdk-java/src/main/java/ly/count/sdk/java/internal/ModuleEvents.java +++ b/sdk-java/src/main/java/ly/count/sdk/java/internal/ModuleEvents.java @@ -258,7 +258,7 @@ public boolean startEvent(final String key) { */ public boolean endEvent(final String key) { L.i("[Events] endEvent: key = " + key); - return endEventInternal(key); + return endEventInternal(key, null, 1, 0); } /** From 5e406bf3e8ba804b95f6c208f9d257f32b5155ae Mon Sep 17 00:00:00 2001 From: arifBurakDemiray Date: Wed, 20 Sep 2023 13:50:54 +0300 Subject: [PATCH 35/82] feat: new util string joiner --- .../java/ly/count/sdk/java/internal/Utils.java | 14 ++++++++++++++ .../ly/count/sdk/java/internal/UtilsTests.java | 13 +++++++++---- 2 files changed, 23 insertions(+), 4 deletions(-) diff --git a/sdk-java/src/main/java/ly/count/sdk/java/internal/Utils.java b/sdk-java/src/main/java/ly/count/sdk/java/internal/Utils.java index 45f5c0571..f1933a6a5 100644 --- a/sdk-java/src/main/java/ly/count/sdk/java/internal/Utils.java +++ b/sdk-java/src/main/java/ly/count/sdk/java/internal/Utils.java @@ -15,6 +15,7 @@ import java.util.Iterator; import java.util.List; import java.util.Map; +import java.util.StringJoiner; import java.util.stream.Collectors; /** @@ -344,4 +345,17 @@ public static String decodeToString(String string, Log L) { } } } + + /** + * Joins all the strings in the specified collection into a single string with the specified delimiter. + */ + static String joinStrings(final Collection collection, final String delimiter) { + StringJoiner joiner = new StringJoiner(delimiter); + + for (String s : collection) { + joiner.add(s); + } + + return joiner.toString(); + } } diff --git a/sdk-java/src/test/java/ly/count/sdk/java/internal/UtilsTests.java b/sdk-java/src/test/java/ly/count/sdk/java/internal/UtilsTests.java index 0b967f3d0..a510f3de0 100644 --- a/sdk-java/src/test/java/ly/count/sdk/java/internal/UtilsTests.java +++ b/sdk-java/src/test/java/ly/count/sdk/java/internal/UtilsTests.java @@ -1,9 +1,7 @@ package ly.count.sdk.java.internal; -import java.util.ArrayList; -import java.util.Collection; -import java.util.HashMap; -import java.util.Map; +import java.util.*; + import ly.count.sdk.java.Config; import org.junit.After; import org.junit.Assert; @@ -248,4 +246,11 @@ public void isValidDataType() { Assert.assertFalse(Utils.isValidDataType(new ArrayList<>())); Assert.assertFalse(Utils.isValidDataType(new HashMap<>())); } + + @Test + public void joinCountlyStore() { + String expected = "1:::2:::3:::4:::5"; + + Assert.assertEquals(expected, Utils.joinStrings(Arrays.asList("1", "2", "3", "4", "5"),":::")); + } } From bdcdf638a6ff28c6ecebc4eccb5934bcc7024d9a Mon Sep 17 00:00:00 2001 From: arifBurakDemiray Date: Wed, 20 Sep 2023 13:51:23 +0300 Subject: [PATCH 36/82] feat: new sdk storage way of events --- .../sdk/java/internal/EventImplQueue.java | 99 +++++++++++-------- .../count/sdk/java/internal/SDKStorage.java | 60 ++++++++++- 2 files changed, 117 insertions(+), 42 deletions(-) diff --git a/sdk-java/src/main/java/ly/count/sdk/java/internal/EventImplQueue.java b/sdk-java/src/main/java/ly/count/sdk/java/internal/EventImplQueue.java index 9ce59c99d..a39e1a66a 100644 --- a/sdk-java/src/main/java/ly/count/sdk/java/internal/EventImplQueue.java +++ b/sdk-java/src/main/java/ly/count/sdk/java/internal/EventImplQueue.java @@ -1,63 +1,82 @@ package ly.count.sdk.java.internal; -import ly.count.sdk.java.Event; -import org.json.JSONArray; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.ObjectOutputStream; -import java.util.ArrayDeque; -import java.util.Queue; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; -public class EventImplQueue implements Storable { +public class EventImplQueue { - private final Log L; + static final String DELIMITER = ":::"; - private Long id = System.currentTimeMillis(); + private final Log L; - final Queue events = new ArrayDeque<>(); + protected int size = 0; + private final CtxCore ctx; - protected EventImplQueue(Log logger) { + protected EventImplQueue(Log logger, CtxCore ctx) { L = logger; + this.ctx = ctx; } - @Override - public Long storageId() { - return id; + void addEvent(final EventImpl event) { + L.d("[EventImplQueue] Adding event: " + event.key); + final List events = getEventList(); + if (events.size() < ctx.getConfig().getEventsBufferSize()) { + events.add(event); + size = events.size(); + setEventData(joinEvents(events)); + } } - @Override - public String storagePrefix() { - return "events"; + String joinEvents(final Collection collection) { + final List strings = new ArrayList<>(); + for (EventImpl e : collection) { + strings.add(e.toJSON(L)); + } + return Utils.joinStrings(strings, EventImplQueue.DELIMITER); } - @Override - public void setId(Long id) { - //do nothing + /** + * set the new value in event data storage + * + * @param eventData + */ + void setEventData(String eventData) { + L.d("[EventImplQueue] Setting event data: " + eventData); + ctx.getSDK().sdkStorage.storeEventQueue(eventData); } - @Override - public byte[] store(Log L) { - JSONArray events = new JSONArray(); - for (EventImpl event : this.events) { - events.put(event.toJSON(L)); - } + /** + * Returns a list of the current stored events, sorted by timestamp from oldest to newest. + */ + public synchronized List getEventList() { + L.d("[EventImplQueue] Getting event list"); + final String[] array = getEvents(); + final List events = new ArrayList<>(array.length); + for (String s : array) { - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - try { - ObjectOutputStream oos = new ObjectOutputStream(baos); - oos.writeObject(events); - oos.close(); - } catch (IOException e) { - L.e("[EventImplQueue] Failed to serialize timed events " + e.getMessage()); + final EventImpl event = EventImpl.fromJSON(s, (ev) -> { + }, L); + if (event != null) { + events.add(event); + } } + // order the events from least to most recent + events.sort((e1, e2) -> (int) (e1.timestamp - e2.timestamp)); + return events; + } - events.clear(); - id = System.currentTimeMillis(); - return baos.toByteArray(); + public void clear() { + size = 0; + ctx.getSDK().sdkStorage.storeEventQueue(""); } - @Override - public boolean restore(byte[] data, Log L) { - return false; + /** + * Returns an unsorted array of the current stored event JSON strings. + */ + private synchronized String[] getEvents() { + L.d("[EventImplQueue] Getting events from disk"); + final String joinedEventsStr = ctx.getSDK().sdkStorage.readEventQueue(); + return joinedEventsStr.isEmpty() ? new String[0] : joinedEventsStr.split(DELIMITER); } } diff --git a/sdk-java/src/main/java/ly/count/sdk/java/internal/SDKStorage.java b/sdk-java/src/main/java/ly/count/sdk/java/internal/SDKStorage.java index 1a7537282..1651afc43 100644 --- a/sdk-java/src/main/java/ly/count/sdk/java/internal/SDKStorage.java +++ b/sdk-java/src/main/java/ly/count/sdk/java/internal/SDKStorage.java @@ -1,8 +1,21 @@ package ly.count.sdk.java.internal; -import java.io.*; +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.FileReader; +import java.io.FileWriter; +import java.io.IOException; import java.nio.channels.FileLock; -import java.util.*; +import java.util.AbstractMap; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Map; public class SDKStorage { @@ -261,4 +274,47 @@ public List storableList(ly.count.sdk.java.internal.CtxCore context, Strin } return list; } + + protected void storeEventQueue(String eventQueue) { + L.d("[SDKStorage] Writing EQ to json file"); + + //write eventQueue that is consist of json objects of events and separated by ":::" delimiter + //to the config.getSdkStorageRootDirectory() folder named Storage.name(this) + ".events" + //if file already exists overwrite it + //if file doesn't exist create it + //if file can't be created or written, log the error + File sdkStorageDirectory = ctx.getConfig().getSdkStorageRootDirectory(); + File file = new File(sdkStorageDirectory, FILE_NAME_PREFIX + FILE_NAME_SEPARATOR + "events"); + + try (BufferedWriter writer = new BufferedWriter(new FileWriter(file))) { + // Write the eventQueue to the file + writer.write(eventQueue); + L.d("[SDKStorage] Wrote EQ to json file"); + } catch (IOException e) { + // Handle the error if writing fails + L.e("[SDKStorage] Failed to write EQ to json file: " + e.toString()); + } + } + + protected String readEventQueue() { + L.d("[SDKStorage] Getting event queue"); + File file = new File(ctx.getConfig().getSdkStorageRootDirectory(), FILE_NAME_PREFIX + FILE_NAME_SEPARATOR + "events"); + + if (!file.exists()) { + return ""; + } + + StringBuilder eventQueue = new StringBuilder(); + try (BufferedReader reader = new BufferedReader(new FileReader(file))) { + String line; + while ((line = reader.readLine()) != null) { + eventQueue.append(line); + } + } catch (IOException e) { + // Handle the error if reading fails + L.e("[SDKStorage] Failed to read EQ from json file: " + e); + } + + return eventQueue.toString(); + } } From d9ad1fc2ff3c73d323a68834a4b8564e5be0c808 Mon Sep 17 00:00:00 2001 From: arifBurakDemiray Date: Wed, 20 Sep 2023 13:53:24 +0300 Subject: [PATCH 37/82] feat: new way of event add --- .../count/sdk/java/internal/ModuleEvents.java | 23 +++++++++---------- 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/sdk-java/src/main/java/ly/count/sdk/java/internal/ModuleEvents.java b/sdk-java/src/main/java/ly/count/sdk/java/internal/ModuleEvents.java index f2357a505..fc7acd5ce 100644 --- a/sdk-java/src/main/java/ly/count/sdk/java/internal/ModuleEvents.java +++ b/sdk-java/src/main/java/ly/count/sdk/java/internal/ModuleEvents.java @@ -12,7 +12,7 @@ public class ModuleEvents extends ModuleBase { protected CtxCore ctx = null; - protected EventImplQueue eventQueue; + protected EventImplQueue eventQueue = null; static final Map timedEvents = new HashMap<>(); private ScheduledExecutorService executor = null; @@ -23,7 +23,7 @@ public class ModuleEvents extends ModuleBase { public void init(InternalConfig config, Log logger) { super.init(config, logger); L.d("[ModuleEvents] init: config = " + config); - eventQueue = new EventImplQueue(L); + eventQueue = new EventImplQueue(L, ctx); eventsInterface = new Events(); } @@ -37,7 +37,7 @@ public void onContextAcquired(CtxCore ctx) { executor.scheduleWithFixedDelay(new Runnable() { @Override public void run() { - addEventsToRequestQ(); + //addEventsToRequestQ(); } }, ctx.getConfig().getSendUpdateEachSeconds(), ctx.getConfig().getSendUpdateEachSeconds(), TimeUnit.SECONDS); } @@ -53,12 +53,10 @@ private synchronized void addEventsToRequestQ() { Request request = new Request(); request.params.add("device_id", Countly.instance().getDeviceId()); - request.params.arr("events").put(eventQueue.events).add(); + request.params.arr("events").put(eventQueue.getEventList()).add(); request.own(ModuleEvents.class); ModuleRequests.pushAsync(ctx, request); - - eventQueue.events.clear(); } protected void removeInvalidDataFromSegments(Map segments) { @@ -79,6 +77,7 @@ protected void removeInvalidDataFromSegments(Map segments) { } protected void recordEventInternal(String key, int count, double sum, Map segmentation, double dur) { + L.d("[ModuleEvents] recordEventInternal: key = " + key + ", count = " + count + ", sum = " + sum + ", segmentation = " + segmentation + ", dur = " + dur); if (count <= 0) { L.w("[ModuleEvents] recordEventInternal: Count can't be less than 1, ignoring this event."); return; @@ -95,12 +94,12 @@ protected void recordEventInternal(String key, int count, double sum, Map= internalConfig.getEventsBufferSize()) { - addEventsToRequestQ(); - //Storage.pushAsync(ctx, eventQueue); - } + L.d("[ModuleEvents] addEventToQueue"); + eventQueue.addEvent(event); + if (eventQueue.size >= internalConfig.getEventsBufferSize()) { + L.d("[ModuleEvents] addEventToQueue: eventQueue.size >= internalConfig.getEventsBufferSize()"); + addEventsToRequestQ(); + eventQueue.clear(); } } From a724792d72a2d4651c9b7678beef3805b0be920a Mon Sep 17 00:00:00 2001 From: arifBurakDemiray Date: Wed, 20 Sep 2023 13:53:54 +0300 Subject: [PATCH 38/82] feat: expose target folder to used by other inner classes --- sdk-java/src/main/java/ly/count/sdk/java/Config.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/sdk-java/src/main/java/ly/count/sdk/java/Config.java b/sdk-java/src/main/java/ly/count/sdk/java/Config.java index 932766fe4..25e83354d 100644 --- a/sdk-java/src/main/java/ly/count/sdk/java/Config.java +++ b/sdk-java/src/main/java/ly/count/sdk/java/Config.java @@ -1515,5 +1515,9 @@ public Config setMetricOverride(Map metricOverride) { this.metricOverride.putAll(metricOverride); return this; } + + public File getSdkStorageRootDirectory() { + return sdkStorageRootDirectory; + } } From 3136e452e3d22f6cffcc608681ec68e00fead23f Mon Sep 17 00:00:00 2001 From: arifBurakDemiray Date: Wed, 20 Sep 2023 13:54:43 +0300 Subject: [PATCH 39/82] feat: default param for sum and dur --- .../src/main/java/ly/count/sdk/java/internal/EventImpl.java | 4 ++-- .../test/java/ly/count/sdk/java/internal/EventImplTests.java | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/sdk-java/src/main/java/ly/count/sdk/java/internal/EventImpl.java b/sdk-java/src/main/java/ly/count/sdk/java/internal/EventImpl.java index ff411b5df..e99b24c4d 100644 --- a/sdk-java/src/main/java/ly/count/sdk/java/internal/EventImpl.java +++ b/sdk-java/src/main/java/ly/count/sdk/java/internal/EventImpl.java @@ -19,8 +19,8 @@ class EventImpl implements Event, JSONable { protected Map segmentation; protected int count; - protected Double sum; - protected Double duration; + protected Double sum = 0.0; + protected Double duration = 0.0; protected long timestamp; protected int hour; diff --git a/sdk-java/src/test/java/ly/count/sdk/java/internal/EventImplTests.java b/sdk-java/src/test/java/ly/count/sdk/java/internal/EventImplTests.java index 8b2fb82c0..4b5ef3e15 100644 --- a/sdk-java/src/test/java/ly/count/sdk/java/internal/EventImplTests.java +++ b/sdk-java/src/test/java/ly/count/sdk/java/internal/EventImplTests.java @@ -26,8 +26,8 @@ public void constructor_defaultValues() { Assert.assertEquals("test_event", eventImpl.key); Assert.assertEquals(1, eventImpl.count); - Assert.assertNull(eventImpl.duration); - Assert.assertNull(eventImpl.sum); + Assert.assertEquals(new Double(0.0), eventImpl.duration); + Assert.assertEquals(new Double(0.0), eventImpl.sum); Assert.assertTrue(eventImpl.getTimestamp() > 0); Assert.assertEquals(Device.dev.currentHour(), eventImpl.hour); Assert.assertEquals(Device.dev.currentDayOfWeek(), eventImpl.dow); From 5f01f54858480a633b202a5c8dfb3bff76940efa Mon Sep 17 00:00:00 2001 From: arifBurakDemiray Date: Wed, 20 Sep 2023 13:55:09 +0300 Subject: [PATCH 40/82] feat: delete unnecesary mapping --- .../main/java/ly/count/sdk/java/internal/SessionImpl.java | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/sdk-java/src/main/java/ly/count/sdk/java/internal/SessionImpl.java b/sdk-java/src/main/java/ly/count/sdk/java/internal/SessionImpl.java index 0e9ee4bab..501999213 100644 --- a/sdk-java/src/main/java/ly/count/sdk/java/internal/SessionImpl.java +++ b/sdk-java/src/main/java/ly/count/sdk/java/internal/SessionImpl.java @@ -6,14 +6,11 @@ import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.util.ArrayList; -import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; - import ly.count.sdk.java.Config; -import ly.count.sdk.java.Countly; import ly.count.sdk.java.Event; import ly.count.sdk.java.Session; import ly.count.sdk.java.Usage; @@ -308,9 +305,7 @@ public void recordEvent(Event event) { ModuleEvents eventsModule = (ModuleEvents) SDKCore.instance.module(CoreFeature.Events.getIndex()); EventImpl eventImpl = (EventImpl) event; - Map segmentation = new HashMap<>(eventImpl.segmentation); - - eventsModule.recordEventInternal(eventImpl.key, eventImpl.count, eventImpl.sum, segmentation, eventImpl.duration); + eventsModule.recordEventInternal(eventImpl.key, eventImpl.count, eventImpl.sum, eventImpl.segmentation, eventImpl.duration); } @Override From 1ee02ae3ec437df73a649489c40cf7fafab55e85 Mon Sep 17 00:00:00 2001 From: arifBurakDemiray Date: Wed, 20 Sep 2023 14:07:06 +0300 Subject: [PATCH 41/82] feat: string joiner and test --- .../java/ly/count/sdk/java/internal/Utils.java | 14 ++++++++++++++ .../ly/count/sdk/java/internal/UtilsTests.java | 8 ++++++++ 2 files changed, 22 insertions(+) diff --git a/sdk-java/src/main/java/ly/count/sdk/java/internal/Utils.java b/sdk-java/src/main/java/ly/count/sdk/java/internal/Utils.java index 45f5c0571..f1933a6a5 100644 --- a/sdk-java/src/main/java/ly/count/sdk/java/internal/Utils.java +++ b/sdk-java/src/main/java/ly/count/sdk/java/internal/Utils.java @@ -15,6 +15,7 @@ import java.util.Iterator; import java.util.List; import java.util.Map; +import java.util.StringJoiner; import java.util.stream.Collectors; /** @@ -344,4 +345,17 @@ public static String decodeToString(String string, Log L) { } } } + + /** + * Joins all the strings in the specified collection into a single string with the specified delimiter. + */ + static String joinStrings(final Collection collection, final String delimiter) { + StringJoiner joiner = new StringJoiner(delimiter); + + for (String s : collection) { + joiner.add(s); + } + + return joiner.toString(); + } } diff --git a/sdk-java/src/test/java/ly/count/sdk/java/internal/UtilsTests.java b/sdk-java/src/test/java/ly/count/sdk/java/internal/UtilsTests.java index 0b967f3d0..b42556e8a 100644 --- a/sdk-java/src/test/java/ly/count/sdk/java/internal/UtilsTests.java +++ b/sdk-java/src/test/java/ly/count/sdk/java/internal/UtilsTests.java @@ -1,6 +1,7 @@ package ly.count.sdk.java.internal; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; import java.util.HashMap; import java.util.Map; @@ -248,4 +249,11 @@ public void isValidDataType() { Assert.assertFalse(Utils.isValidDataType(new ArrayList<>())); Assert.assertFalse(Utils.isValidDataType(new HashMap<>())); } + + @Test + public void joinCountlyStore() { + String expected = "1:::2:::3:::4:::5"; + + Assert.assertEquals(expected, Utils.joinStrings(Arrays.asList("1", "2", "3", "4", "5"),":::")); + } } From dbb8736e0a3ac2a831eb9521b76c6f90e452c6d9 Mon Sep 17 00:00:00 2001 From: arifBurakDemiray Date: Wed, 20 Sep 2023 14:21:38 +0300 Subject: [PATCH 42/82] fix: remove unnecessary call --- sdk-java/src/main/java/ly/count/sdk/java/Config.java | 4 ---- .../src/main/java/ly/count/sdk/java/internal/SDKStorage.java | 4 ++-- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/sdk-java/src/main/java/ly/count/sdk/java/Config.java b/sdk-java/src/main/java/ly/count/sdk/java/Config.java index 25e83354d..932766fe4 100644 --- a/sdk-java/src/main/java/ly/count/sdk/java/Config.java +++ b/sdk-java/src/main/java/ly/count/sdk/java/Config.java @@ -1515,9 +1515,5 @@ public Config setMetricOverride(Map metricOverride) { this.metricOverride.putAll(metricOverride); return this; } - - public File getSdkStorageRootDirectory() { - return sdkStorageRootDirectory; - } } diff --git a/sdk-java/src/main/java/ly/count/sdk/java/internal/SDKStorage.java b/sdk-java/src/main/java/ly/count/sdk/java/internal/SDKStorage.java index 1651afc43..2fc038daa 100644 --- a/sdk-java/src/main/java/ly/count/sdk/java/internal/SDKStorage.java +++ b/sdk-java/src/main/java/ly/count/sdk/java/internal/SDKStorage.java @@ -283,7 +283,7 @@ protected void storeEventQueue(String eventQueue) { //if file already exists overwrite it //if file doesn't exist create it //if file can't be created or written, log the error - File sdkStorageDirectory = ctx.getConfig().getSdkStorageRootDirectory(); + File sdkStorageDirectory = ctx.getContext(); File file = new File(sdkStorageDirectory, FILE_NAME_PREFIX + FILE_NAME_SEPARATOR + "events"); try (BufferedWriter writer = new BufferedWriter(new FileWriter(file))) { @@ -298,7 +298,7 @@ protected void storeEventQueue(String eventQueue) { protected String readEventQueue() { L.d("[SDKStorage] Getting event queue"); - File file = new File(ctx.getConfig().getSdkStorageRootDirectory(), FILE_NAME_PREFIX + FILE_NAME_SEPARATOR + "events"); + File file = new File(ctx.getContext(), FILE_NAME_PREFIX + FILE_NAME_SEPARATOR + "events"); if (!file.exists()) { return ""; From cc957efc98d00af92dbd549c8f89457d9a8fad5b Mon Sep 17 00:00:00 2001 From: arifBurakDemiray Date: Wed, 20 Sep 2023 14:48:29 +0300 Subject: [PATCH 43/82] feat: reorder events to usage in scheduled --- .../java/ly/count/sdk/java/internal/ModuleEvents.java | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/sdk-java/src/main/java/ly/count/sdk/java/internal/ModuleEvents.java b/sdk-java/src/main/java/ly/count/sdk/java/internal/ModuleEvents.java index fc7acd5ce..6344bde14 100644 --- a/sdk-java/src/main/java/ly/count/sdk/java/internal/ModuleEvents.java +++ b/sdk-java/src/main/java/ly/count/sdk/java/internal/ModuleEvents.java @@ -10,13 +10,11 @@ import ly.count.sdk.java.Countly; public class ModuleEvents extends ModuleBase { - protected CtxCore ctx = null; + protected CtxCore ctx = null; protected EventImplQueue eventQueue = null; - static final Map timedEvents = new HashMap<>(); private ScheduledExecutorService executor = null; - protected Events eventsInterface = null; @Override @@ -37,7 +35,7 @@ public void onContextAcquired(CtxCore ctx) { executor.scheduleWithFixedDelay(new Runnable() { @Override public void run() { - //addEventsToRequestQ(); + addEventsToRequestQ(); } }, ctx.getConfig().getSendUpdateEachSeconds(), ctx.getConfig().getSendUpdateEachSeconds(), TimeUnit.SECONDS); } @@ -56,6 +54,7 @@ private synchronized void addEventsToRequestQ() { request.params.arr("events").put(eventQueue.getEventList()).add(); request.own(ModuleEvents.class); + eventQueue.clear(); ModuleRequests.pushAsync(ctx, request); } @@ -99,7 +98,6 @@ private void addEventToQueue(EventImpl event) { if (eventQueue.size >= internalConfig.getEventsBufferSize()) { L.d("[ModuleEvents] addEventToQueue: eventQueue.size >= internalConfig.getEventsBufferSize()"); addEventsToRequestQ(); - eventQueue.clear(); } } From cc25f6e6ad7922db750634b903e9adf3fcb6514c Mon Sep 17 00:00:00 2001 From: arifBurakDemiray Date: Wed, 20 Sep 2023 14:53:36 +0300 Subject: [PATCH 44/82] chore: remove unnecessary log --- .../src/main/java/ly/count/sdk/java/internal/ModuleEvents.java | 1 - 1 file changed, 1 deletion(-) diff --git a/sdk-java/src/main/java/ly/count/sdk/java/internal/ModuleEvents.java b/sdk-java/src/main/java/ly/count/sdk/java/internal/ModuleEvents.java index 6344bde14..53f71c056 100644 --- a/sdk-java/src/main/java/ly/count/sdk/java/internal/ModuleEvents.java +++ b/sdk-java/src/main/java/ly/count/sdk/java/internal/ModuleEvents.java @@ -76,7 +76,6 @@ protected void removeInvalidDataFromSegments(Map segments) { } protected void recordEventInternal(String key, int count, double sum, Map segmentation, double dur) { - L.d("[ModuleEvents] recordEventInternal: key = " + key + ", count = " + count + ", sum = " + sum + ", segmentation = " + segmentation + ", dur = " + dur); if (count <= 0) { L.w("[ModuleEvents] recordEventInternal: Count can't be less than 1, ignoring this event."); return; From 62a041f7c9f9084c23604c7780061795f805bc0d Mon Sep 17 00:00:00 2001 From: arifBurakDemiray Date: Wed, 20 Sep 2023 17:21:31 +0300 Subject: [PATCH 45/82] fix: reorder recordEvent param --- .../src/main/java/ly/count/sdk/java/internal/ModuleEvents.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk-java/src/main/java/ly/count/sdk/java/internal/ModuleEvents.java b/sdk-java/src/main/java/ly/count/sdk/java/internal/ModuleEvents.java index 53f71c056..4a204f76e 100644 --- a/sdk-java/src/main/java/ly/count/sdk/java/internal/ModuleEvents.java +++ b/sdk-java/src/main/java/ly/count/sdk/java/internal/ModuleEvents.java @@ -231,7 +231,7 @@ public void recordEvent(String key, int count) { * @param count how many of these events have occurred, default value is "1", must be greater than 0 * @param sum set sum parameter of the event default value is "0" */ - public void recordEvent(String key, double sum, int count) { + public void recordEvent(String key, int count, double sum) { recordEvent(key, count, sum, null); } From 807326efebcfe099e4872ab31d3e8e3773893e8d Mon Sep 17 00:00:00 2001 From: arifBurakDemiray Date: Thu, 21 Sep 2023 09:22:18 +0300 Subject: [PATCH 46/82] feat: remove unnecessary funcs and add comment tests --- .../ly/count/sdk/java/internal/Utils.java | 25 ---------------- .../count/sdk/java/internal/UtilsTests.java | 30 ++++++++++++++----- 2 files changed, 22 insertions(+), 33 deletions(-) diff --git a/sdk-java/src/main/java/ly/count/sdk/java/internal/Utils.java b/sdk-java/src/main/java/ly/count/sdk/java/internal/Utils.java index f1933a6a5..8a4678d98 100644 --- a/sdk-java/src/main/java/ly/count/sdk/java/internal/Utils.java +++ b/sdk-java/src/main/java/ly/count/sdk/java/internal/Utils.java @@ -87,18 +87,6 @@ public static List reflectiveGetDeclaredFields(List list, Class return list; } - public static Field findField(Class cls, String name) throws NoSuchFieldException { - try { - return cls.getDeclaredField(name); - } catch (NoSuchFieldException e) { - if (cls.getSuperclass() == null) { - throw e; - } else { - return findField(cls.getSuperclass(), name); - } - } - } - /** * StringUtils.isEmpty replacement. * @@ -345,17 +333,4 @@ public static String decodeToString(String string, Log L) { } } } - - /** - * Joins all the strings in the specified collection into a single string with the specified delimiter. - */ - static String joinStrings(final Collection collection, final String delimiter) { - StringJoiner joiner = new StringJoiner(delimiter); - - for (String s : collection) { - joiner.add(s); - } - - return joiner.toString(); - } } diff --git a/sdk-java/src/test/java/ly/count/sdk/java/internal/UtilsTests.java b/sdk-java/src/test/java/ly/count/sdk/java/internal/UtilsTests.java index b42556e8a..8ab427dbe 100644 --- a/sdk-java/src/test/java/ly/count/sdk/java/internal/UtilsTests.java +++ b/sdk-java/src/test/java/ly/count/sdk/java/internal/UtilsTests.java @@ -1,7 +1,6 @@ package ly.count.sdk.java.internal; import java.util.ArrayList; -import java.util.Arrays; import java.util.Collection; import java.util.HashMap; import java.util.Map; @@ -214,6 +213,10 @@ public void isNotEmpty() { junit.framework.Assert.assertFalse(Utils.isNotEmpty(null)); } + /** + * It checks if the "mapify" method is called. + * And if the map passed there is converted to the map of strings. + */ @Test public void mapify() { Map expected = new HashMap() {{ @@ -222,6 +225,7 @@ public void mapify() { put("float", "0.2"); put("long", "2"); put("null", ""); + put(null, ""); }}; Map given = new HashMap() {{ @@ -230,11 +234,28 @@ public void mapify() { put("float", .2f); put("long", 2L); put("null", null); + put(null, null); }}; Assert.assertEquals(expected, Utils.mapify(given)); } + /** + * It checks when map given as null + * function returns empty map. + */ + @Test + public void mapify_nullMap() { + Map expected = new HashMap<>(); + + Assert.assertEquals(expected, Utils.mapify(null)); + } + + /** + * It checks if the "isValidDataType" method is called. + * And if the data types passed there are valid + * for the segmentation. + */ @Test public void isValidDataType() { Assert.assertTrue(Utils.isValidDataType("string")); @@ -249,11 +270,4 @@ public void isValidDataType() { Assert.assertFalse(Utils.isValidDataType(new ArrayList<>())); Assert.assertFalse(Utils.isValidDataType(new HashMap<>())); } - - @Test - public void joinCountlyStore() { - String expected = "1:::2:::3:::4:::5"; - - Assert.assertEquals(expected, Utils.joinStrings(Arrays.asList("1", "2", "3", "4", "5"),":::")); - } } From 9cbcf8f2ec740990b753fc84e409eee0da8a1a2e Mon Sep 17 00:00:00 2001 From: arifBurakDemiray Date: Thu, 21 Sep 2023 09:29:18 +0300 Subject: [PATCH 47/82] doc: add utils comments --- .../ly/count/sdk/java/internal/Utils.java | 27 ++++++++++++++++--- 1 file changed, 23 insertions(+), 4 deletions(-) diff --git a/sdk-java/src/main/java/ly/count/sdk/java/internal/Utils.java b/sdk-java/src/main/java/ly/count/sdk/java/internal/Utils.java index 8a4678d98..caf564c87 100644 --- a/sdk-java/src/main/java/ly/count/sdk/java/internal/Utils.java +++ b/sdk-java/src/main/java/ly/count/sdk/java/internal/Utils.java @@ -15,7 +15,6 @@ import java.util.Iterator; import java.util.List; import java.util.Map; -import java.util.StringJoiner; import java.util.stream.Collectors; /** @@ -267,12 +266,23 @@ public static Map fixSegmentKeysAndValues(final int keyLength, f return segmentation; } - public static Map mapify(Map segmentation) { - if (segmentation == null) { + /** + * Convert map of 'String, Object' key value pairs to + * 'String, String' key value pairs
    + *
    + * Null values are converted to empty strings
    + * Null keys are accepted and stay null + *

    + * + * @param map to convert + * @return resulting 'String, String' map + */ + public static Map mapify(Map map) { + if (map == null) { return new HashMap<>(); } - return segmentation.entrySet().stream() + return map.entrySet().stream() .collect(Collectors.toMap(Map.Entry::getKey, entry -> { Object value = entry.getValue(); if (value == null) { @@ -283,6 +293,15 @@ public static Map mapify(Map segmentation) { })); } + /** + * Given value is valid if it is one of the following types:
    + * Boolean, Integer, Long, String, Double, Float
    + *
    + * If value is null returns false + * + * @param value to check + * @return true if value is valid, false otherwise + */ public static boolean isValidDataType(Object value) { if (value == null) { return false; From 4645b7b78bdb6ee144fc039328fb42e39b2ea64b Mon Sep 17 00:00:00 2001 From: arifBurakDemiray Date: Thu, 21 Sep 2023 09:59:53 +0300 Subject: [PATCH 48/82] feat: serialize deserialize test --- .../ly/count/sdk/java/internal/Utils.java | 4 +- .../sdk/java/internal/EventImplTests.java | 41 +++++++++++++++++++ 2 files changed, 44 insertions(+), 1 deletion(-) diff --git a/sdk-java/src/main/java/ly/count/sdk/java/internal/Utils.java b/sdk-java/src/main/java/ly/count/sdk/java/internal/Utils.java index caf564c87..7e5d7871f 100644 --- a/sdk-java/src/main/java/ly/count/sdk/java/internal/Utils.java +++ b/sdk-java/src/main/java/ly/count/sdk/java/internal/Utils.java @@ -5,6 +5,7 @@ import java.io.InputStream; import java.io.UnsupportedEncodingException; import java.lang.reflect.Field; +import java.math.BigDecimal; import java.net.URLDecoder; import java.net.URLEncoder; import java.security.MessageDigest; @@ -295,7 +296,7 @@ public static Map mapify(Map map) { /** * Given value is valid if it is one of the following types:
    - * Boolean, Integer, Long, String, Double, Float
    + * Boolean, Integer, Long, String, Double, Float, BigDecimal
    *
    * If value is null returns false * @@ -313,6 +314,7 @@ public static boolean isValidDataType(Object value) { value instanceof Long || value instanceof String || value instanceof Double || + value instanceof BigDecimal || value instanceof Float; } diff --git a/sdk-java/src/test/java/ly/count/sdk/java/internal/EventImplTests.java b/sdk-java/src/test/java/ly/count/sdk/java/internal/EventImplTests.java index 8b2fb82c0..cbcaf3c1c 100644 --- a/sdk-java/src/test/java/ly/count/sdk/java/internal/EventImplTests.java +++ b/sdk-java/src/test/java/ly/count/sdk/java/internal/EventImplTests.java @@ -1,5 +1,6 @@ package ly.count.sdk.java.internal; +import java.math.BigDecimal; import java.util.HashMap; import java.util.Map; import org.json.JSONObject; @@ -192,6 +193,46 @@ public void validateFromJson() { Assert.assertEquals(event.segmentation, fromJson.segmentation); } + /** + * If the segmentation is same and the values + * are converted by "fromJSON" to the correct data types that are created + * by "toJSON" method "JSONObject" class. + */ + @Test + public void validateFromJson_toJson_segmentation() { + + EventImpl event = new EventImpl((event1) -> { + }, "test_sell_event", L); + + Map segmentation = new HashMap<>(); + segmentation.put("sold", true); + segmentation.put("price", 9.43); + segmentation.put("quantity", 3); + segmentation.put("name", "test"); + segmentation.put("null", null); + segmentation.put("checksum", 56476587L); + segmentation.put("divisor", 0.2f); + event.segmentation = segmentation; + + Map expectedSegmentation = new HashMap<>(); + expectedSegmentation.put("sold", true); + expectedSegmentation.put("price", BigDecimal.valueOf(9.43)); + expectedSegmentation.put("quantity", 3); + expectedSegmentation.put("name", "test"); + expectedSegmentation.put("checksum", 56476587); + expectedSegmentation.put("divisor", BigDecimal.valueOf(0.2)); + + JSONObject json = new JSONObject(); + json.put(EventImpl.KEY_KEY, "test_sell_event"); + json.put(EventImpl.SEGMENTATION_KEY, segmentation); + + EventImpl fromJson = EventImpl.fromJSON(event.toJSON(L), event1 -> { + }, L); + + Assert.assertEquals(event.key, fromJson.key); + Assert.assertEquals(expectedSegmentation, fromJson.segmentation); + } + /** * getters of EventImpl class. * It checks if the values returned by the getters are same. From d2b40746568614cc9287b2cd31a9db04c1f1db54 Mon Sep 17 00:00:00 2001 From: arifBurakDemiray Date: Thu, 21 Sep 2023 10:16:11 +0300 Subject: [PATCH 49/82] fix: remove unnecessary util func --- .../ly/count/sdk/java/internal/EventImplQueue.java | 2 +- .../main/java/ly/count/sdk/java/internal/Utils.java | 13 ------------- .../java/ly/count/sdk/java/internal/UtilsTests.java | 7 ------- 3 files changed, 1 insertion(+), 21 deletions(-) diff --git a/sdk-java/src/main/java/ly/count/sdk/java/internal/EventImplQueue.java b/sdk-java/src/main/java/ly/count/sdk/java/internal/EventImplQueue.java index a39e1a66a..278ed0283 100644 --- a/sdk-java/src/main/java/ly/count/sdk/java/internal/EventImplQueue.java +++ b/sdk-java/src/main/java/ly/count/sdk/java/internal/EventImplQueue.java @@ -33,7 +33,7 @@ String joinEvents(final Collection collection) { for (EventImpl e : collection) { strings.add(e.toJSON(L)); } - return Utils.joinStrings(strings, EventImplQueue.DELIMITER); + return Utils.join(strings, EventImplQueue.DELIMITER); } /** diff --git a/sdk-java/src/main/java/ly/count/sdk/java/internal/Utils.java b/sdk-java/src/main/java/ly/count/sdk/java/internal/Utils.java index 24b185e7b..adcd91a94 100644 --- a/sdk-java/src/main/java/ly/count/sdk/java/internal/Utils.java +++ b/sdk-java/src/main/java/ly/count/sdk/java/internal/Utils.java @@ -355,17 +355,4 @@ public static String decodeToString(String string, Log L) { } } } - - /** - * Joins all the strings in the specified collection into a single string with the specified delimiter. - */ - static String joinStrings(final Collection collection, final String delimiter) { - StringJoiner joiner = new StringJoiner(delimiter); - - for (String s : collection) { - joiner.add(s); - } - - return joiner.toString(); - } } diff --git a/sdk-java/src/test/java/ly/count/sdk/java/internal/UtilsTests.java b/sdk-java/src/test/java/ly/count/sdk/java/internal/UtilsTests.java index c366bb3bc..89eeef7e0 100644 --- a/sdk-java/src/test/java/ly/count/sdk/java/internal/UtilsTests.java +++ b/sdk-java/src/test/java/ly/count/sdk/java/internal/UtilsTests.java @@ -268,11 +268,4 @@ public void isValidDataType() { Assert.assertFalse(Utils.isValidDataType(new ArrayList<>())); Assert.assertFalse(Utils.isValidDataType(new HashMap<>())); } - - @Test - public void joinCountlyStore() { - String expected = "1:::2:::3:::4:::5"; - - Assert.assertEquals(expected, Utils.joinStrings(Arrays.asList("1", "2", "3", "4", "5"),":::")); - } } From 74ca0c8fe0ed28f5f986ff89dcf52ddb1d2aa95b Mon Sep 17 00:00:00 2001 From: arifBurakDemiray Date: Thu, 21 Sep 2023 10:21:54 +0300 Subject: [PATCH 50/82] fix: ctx t config --- .../ly/count/sdk/java/internal/EventImplQueue.java | 14 +++++++------- .../ly/count/sdk/java/internal/ModuleEvents.java | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/sdk-java/src/main/java/ly/count/sdk/java/internal/EventImplQueue.java b/sdk-java/src/main/java/ly/count/sdk/java/internal/EventImplQueue.java index 278ed0283..7f4c4461f 100644 --- a/sdk-java/src/main/java/ly/count/sdk/java/internal/EventImplQueue.java +++ b/sdk-java/src/main/java/ly/count/sdk/java/internal/EventImplQueue.java @@ -11,17 +11,17 @@ public class EventImplQueue { private final Log L; protected int size = 0; - private final CtxCore ctx; + private final InternalConfig config; - protected EventImplQueue(Log logger, CtxCore ctx) { + protected EventImplQueue(Log logger, InternalConfig config) { L = logger; - this.ctx = ctx; + this.config = config; } void addEvent(final EventImpl event) { L.d("[EventImplQueue] Adding event: " + event.key); final List events = getEventList(); - if (events.size() < ctx.getConfig().getEventsBufferSize()) { + if (events.size() < config.getEventsBufferSize()) { events.add(event); size = events.size(); setEventData(joinEvents(events)); @@ -43,7 +43,7 @@ String joinEvents(final Collection collection) { */ void setEventData(String eventData) { L.d("[EventImplQueue] Setting event data: " + eventData); - ctx.getSDK().sdkStorage.storeEventQueue(eventData); + SDKCore.instance.sdkStorage.storeEventQueue(eventData); } /** @@ -68,7 +68,7 @@ public synchronized List getEventList() { public void clear() { size = 0; - ctx.getSDK().sdkStorage.storeEventQueue(""); + SDKCore.instance.sdkStorage.storeEventQueue(""); } /** @@ -76,7 +76,7 @@ public void clear() { */ private synchronized String[] getEvents() { L.d("[EventImplQueue] Getting events from disk"); - final String joinedEventsStr = ctx.getSDK().sdkStorage.readEventQueue(); + final String joinedEventsStr = SDKCore.instance.sdkStorage.readEventQueue(); return joinedEventsStr.isEmpty() ? new String[0] : joinedEventsStr.split(DELIMITER); } } diff --git a/sdk-java/src/main/java/ly/count/sdk/java/internal/ModuleEvents.java b/sdk-java/src/main/java/ly/count/sdk/java/internal/ModuleEvents.java index 4a204f76e..a9117bcad 100644 --- a/sdk-java/src/main/java/ly/count/sdk/java/internal/ModuleEvents.java +++ b/sdk-java/src/main/java/ly/count/sdk/java/internal/ModuleEvents.java @@ -21,7 +21,7 @@ public class ModuleEvents extends ModuleBase { public void init(InternalConfig config, Log logger) { super.init(config, logger); L.d("[ModuleEvents] init: config = " + config); - eventQueue = new EventImplQueue(L, ctx); + eventQueue = new EventImplQueue(L, config); eventsInterface = new Events(); } From fe18de5a79406eb2d544414d978b9e087b9e2c2a Mon Sep 17 00:00:00 2001 From: arifBurakDemiray Date: Thu, 21 Sep 2023 10:55:31 +0300 Subject: [PATCH 51/82] fix: revert default params --- .../src/main/java/ly/count/sdk/java/internal/EventImpl.java | 4 ++-- .../test/java/ly/count/sdk/java/internal/EventImplTests.java | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/sdk-java/src/main/java/ly/count/sdk/java/internal/EventImpl.java b/sdk-java/src/main/java/ly/count/sdk/java/internal/EventImpl.java index 8b6e99650..3844f9e6f 100644 --- a/sdk-java/src/main/java/ly/count/sdk/java/internal/EventImpl.java +++ b/sdk-java/src/main/java/ly/count/sdk/java/internal/EventImpl.java @@ -19,8 +19,8 @@ class EventImpl implements Event, JSONable { protected Map segmentation; protected int count; - protected Double sum = 0.0; - protected Double duration = 0.0; + protected Double sum; + protected Double duration; protected long timestamp; protected int hour; diff --git a/sdk-java/src/test/java/ly/count/sdk/java/internal/EventImplTests.java b/sdk-java/src/test/java/ly/count/sdk/java/internal/EventImplTests.java index 79411a700..cbcaf3c1c 100644 --- a/sdk-java/src/test/java/ly/count/sdk/java/internal/EventImplTests.java +++ b/sdk-java/src/test/java/ly/count/sdk/java/internal/EventImplTests.java @@ -27,8 +27,8 @@ public void constructor_defaultValues() { Assert.assertEquals("test_event", eventImpl.key); Assert.assertEquals(1, eventImpl.count); - Assert.assertEquals(new Double(0.0), eventImpl.duration); - Assert.assertEquals(new Double(0.0), eventImpl.sum); + Assert.assertNull(eventImpl.duration); + Assert.assertNull(eventImpl.sum); Assert.assertTrue(eventImpl.getTimestamp() > 0); Assert.assertEquals(Device.dev.currentHour(), eventImpl.hour); Assert.assertEquals(Device.dev.currentDayOfWeek(), eventImpl.dow); From ce0288332669ca43bfcb35a60d3649f709c27c2e Mon Sep 17 00:00:00 2001 From: arifBurakDemiray Date: Thu, 21 Sep 2023 12:31:53 +0300 Subject: [PATCH 52/82] fix: semicolon --- .../src/main/java/ly/count/sdk/java/internal/SDKStorage.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sdk-java/src/main/java/ly/count/sdk/java/internal/SDKStorage.java b/sdk-java/src/main/java/ly/count/sdk/java/internal/SDKStorage.java index a94ee373e..415c31d25 100644 --- a/sdk-java/src/main/java/ly/count/sdk/java/internal/SDKStorage.java +++ b/sdk-java/src/main/java/ly/count/sdk/java/internal/SDKStorage.java @@ -299,8 +299,8 @@ protected void storeEventQueue(String eventQueue) { } protected String readEventQueue() { - L.d("[SDKStorage] Getting event queue") - File file = new File(ctx.getContext(), FILE_NAME_PREFIX + FILE_NAME_SEPARATOR + EVENT_QUEUE_FILE_NAME) + L.d("[SDKStorage] Getting event queue"); + File file = new File(ctx.getContext(), FILE_NAME_PREFIX + FILE_NAME_SEPARATOR + EVENT_QUEUE_FILE_NAME); if (!file.exists()) { return ""; From 65d21bfa3f813ff281a9855db0368b102c4c281a Mon Sep 17 00:00:00 2001 From: arifBurakDemiray Date: Thu, 21 Sep 2023 12:46:23 +0300 Subject: [PATCH 53/82] fix: use mutable version of instances --- .../ly/count/sdk/java/internal/EventImpl.java | 2 +- .../count/sdk/java/internal/ModuleEvents.java | 46 +++++++++---------- 2 files changed, 24 insertions(+), 24 deletions(-) diff --git a/sdk-java/src/main/java/ly/count/sdk/java/internal/EventImpl.java b/sdk-java/src/main/java/ly/count/sdk/java/internal/EventImpl.java index 3844f9e6f..822a2ebf0 100644 --- a/sdk-java/src/main/java/ly/count/sdk/java/internal/EventImpl.java +++ b/sdk-java/src/main/java/ly/count/sdk/java/internal/EventImpl.java @@ -38,7 +38,7 @@ public interface EventRecorder { */ private boolean invalid = false; - EventImpl(@Nonnull String key, int count, double sum, double duration, @Nonnull Map segmentation, @Nonnull Log givenL) { + EventImpl(@Nonnull String key, int count, Double sum, Double duration, @Nonnull Map segmentation, @Nonnull Log givenL) { L = givenL; this.recorder = null; diff --git a/sdk-java/src/main/java/ly/count/sdk/java/internal/ModuleEvents.java b/sdk-java/src/main/java/ly/count/sdk/java/internal/ModuleEvents.java index a9117bcad..00d0d53bf 100644 --- a/sdk-java/src/main/java/ly/count/sdk/java/internal/ModuleEvents.java +++ b/sdk-java/src/main/java/ly/count/sdk/java/internal/ModuleEvents.java @@ -75,7 +75,7 @@ protected void removeInvalidDataFromSegments(Map segments) { }); } - protected void recordEventInternal(String key, int count, double sum, Map segmentation, double dur) { + protected void recordEventInternal(String key, int count, Double sum, Map segmentation, Double dur) { if (count <= 0) { L.w("[ModuleEvents] recordEventInternal: Count can't be less than 1, ignoring this event."); return; @@ -113,7 +113,7 @@ boolean startEventInternal(final String key) { return true; } - boolean endEventInternal(final String key, final Map segmentation, final int count, final double sum) { + boolean endEventInternal(final String key, final Map segmentation, final int count, final Double sum) { L.d("[ModuleEvents] Ending event: [" + key + "]"); if (key == null || key.isEmpty()) { @@ -157,81 +157,81 @@ public class Events { * * @param key key for this event, cannot be null or empty * @param count how many of these events have occurred, default value is "1", must be greater than 0 - * @param sum set sum parameter of the event default value is "0" - * @param dur set duration of event, default value is "0" + * @param sum set sum parameter of the event, can be null + * @param dur set duration of event, can be null * @param segmentation additional segmentation data that you want to set, leave null if you don't want to add anything */ - public void recordEvent(String key, int count, double sum, Map segmentation, double dur) { + public void recordEvent(String key, int count, Double sum, Map segmentation, Double dur) { L.i("[Events] recordEvent: key = " + key + ", count = " + count + ", sum = " + sum + ", segmentation = " + segmentation + ", dur = " + dur); recordEventInternal(key, count, sum, segmentation, dur); } /** - * Record an event with "duration" 0 + * Record an event with "duration" null by default * * @param key key for this event, cannot be null or empty * @param count how many of these events have occurred, default value is "1", must be greater than 0 - * @param sum set sum parameter of the event default value is "0" + * @param sum set sum parameter of the event, can be null * @param segmentation additional segmentation data that you want to set, leave null if you don't want to add anything */ - public void recordEvent(String key, int count, double sum, Map segmentation) { - recordEvent(key, count, sum, segmentation, 0.0); + public void recordEvent(String key, int count, Double sum, Map segmentation) { + recordEvent(key, count, sum, segmentation, null); } /** * Record an event with "segmentation","key" and "count" value only - * "duration" is zero by default + * "duration" is null by default * * @param key key for this event, cannot be null or empty * @param count how many of these events have occurred, default value is "1", must be greater than 0 * @param segmentation additional segmentation data that you want to set, leave null if you don't want to add anything */ public void recordEvent(String key, int count, Map segmentation) { - recordEvent(key, count, 0.0, segmentation); + recordEvent(key, count, null, segmentation); } /** * Record an event with "segmentation" and "key" value only - * "sum" and "duration" is zero by default + * "sum" and "duration" is null by default * * @param key key for this event, cannot be null or empty * @param segmentation additional segmentation data that you want to set, leave null if you don't want to add anything */ public void recordEvent(String key, Map segmentation) { - recordEvent(key, 1, 0.0, segmentation); + recordEvent(key, 1, null, segmentation); } /** * Record an event with "key" only - * "sum" and "duration" is zero by default + * "sum" and "duration" is null by default * "count" is 1 by default * * @param key key for this event, cannot be null or empty */ public void recordEvent(String key) { - recordEvent(key, 1, 0, null); + recordEvent(key, 1, null, null); } /** * Record an event with "key" and "count" only - * "sum" and "duration" is zero by default + * "sum" and "duration" is null by default * * @param key key for this event, cannot be null or empty * @param count how many of these events have occurred, default value is "1", must be greater than 0 */ public void recordEvent(String key, int count) { - recordEvent(key, count, 0, null); + recordEvent(key, count, null, null); } /** * Record an event with "key", "sum" and "count" only - * "duration" is zero by default + * "duration" is null by default * * @param key key for this event, cannot be null or empty * @param count how many of these events have occurred, default value is "1", must be greater than 0 - * @param sum set sum parameter of the event default value is "0" + * @param sum set sum parameter of the event, can be null */ - public void recordEvent(String key, int count, double sum) { + public void recordEvent(String key, int count, Double sum) { recordEvent(key, count, sum, null); } @@ -254,7 +254,7 @@ public boolean startEvent(final String key) { */ public boolean endEvent(final String key) { L.i("[Events] endEvent: key = " + key); - return endEventInternal(key, null, 1, 0); + return endEventInternal(key, null, 1, null); } /** @@ -263,12 +263,12 @@ public boolean endEvent(final String key) { * @param key name of the custom event, required, must not be the empty string * @param segmentation segmentation dictionary to associate with the event, can be null * @param count count to associate with the event, should be more than zero, default value is 1 - * @param sum sum to associate with the event, default value is 0 + * @param sum sum to associate with the event, can be null * @return true if event with this key has been previously started, false otherwise * @throws IllegalStateException if Countly SDK has not been initialized * @throws IllegalArgumentException if key is null or empty, count is less than 1, or if segmentation contains null or empty keys or values */ - public boolean endEvent(final String key, final Map segmentation, final int count, final double sum) { + public boolean endEvent(final String key, final Map segmentation, final int count, final Double sum) { L.i("[Events] endEvent: key = " + key + ", segmentation = " + segmentation + ", count = " + count + ", sum = " + sum); return endEventInternal(key, segmentation, count, sum); } From d05dda9e095f64952e359929c47b83c4c24c0667 Mon Sep 17 00:00:00 2001 From: arifBurakDemiray Date: Thu, 21 Sep 2023 13:28:57 +0300 Subject: [PATCH 54/82] fix: change to lf --- build.gradle | 74 ++++++++++++++++++++++++++-------------------------- 1 file changed, 37 insertions(+), 37 deletions(-) diff --git a/build.gradle b/build.gradle index 1e78ec271..b30901824 100644 --- a/build.gradle +++ b/build.gradle @@ -1,37 +1,37 @@ -// Top-level build file where you can add configuration options common to all sub-projects/modules. - -buildscript { - repositories { - google() - mavenCentral() - jcenter() - maven { - url "https://maven.google.com" - } - } - dependencies { - classpath 'com.android.tools.build:gradle:4.1.3' - classpath 'com.github.dcendents:android-maven-plugin:1.2' - classpath 'com.google.gms:google-services:4.3.0' - - // NOTE: Do not place your application dependencies here; they belong - // in the individual module build.gradle files - } -} - -allprojects { - ext.CLY_VERSION = "23.8.0" - ext.POWERMOCK_VERSION = "1.7.4" - - tasks.withType(Javadoc) { - options.addStringOption('Xdoclint:none', '-quiet') - } - repositories { - google() - jcenter() - //mavenLocal() - maven { - url "https://maven.google.com" // Google's Maven repository - } - } -} +// Top-level build file where you can add configuration options common to all sub-projects/modules. + +buildscript { + repositories { + google() + mavenCentral() + jcenter() + maven { + url "https://maven.google.com" + } + } + dependencies { + classpath 'com.android.tools.build:gradle:4.1.3' + classpath 'com.github.dcendents:android-maven-plugin:1.2' + classpath 'com.google.gms:google-services:4.3.0' + + // NOTE: Do not place your application dependencies here; they belong + // in the individual module build.gradle files + } +} + +allprojects { + ext.CLY_VERSION = "23.8.0" + ext.POWERMOCK_VERSION = "1.7.4" + + tasks.withType(Javadoc) { + options.addStringOption('Xdoclint:none', '-quiet') + } + repositories { + google() + jcenter() + //mavenLocal() + maven { + url "https://maven.google.com" // Google's Maven repository + } + } +} From fcd0080c5afa5cd8136fc5b04f9f976ae86a4fe9 Mon Sep 17 00:00:00 2001 From: arifBurakDemiray Date: Thu, 21 Sep 2023 14:22:34 +0300 Subject: [PATCH 55/82] feat: read file content util and test --- .../ly/count/sdk/java/internal/Utils.java | 32 +++++++++ .../count/sdk/java/internal/UtilsTests.java | 70 ++++++++++++++++++- 2 files changed, 100 insertions(+), 2 deletions(-) diff --git a/sdk-java/src/main/java/ly/count/sdk/java/internal/Utils.java b/sdk-java/src/main/java/ly/count/sdk/java/internal/Utils.java index 051ef2ae4..0a9ebfcdd 100644 --- a/sdk-java/src/main/java/ly/count/sdk/java/internal/Utils.java +++ b/sdk-java/src/main/java/ly/count/sdk/java/internal/Utils.java @@ -1,6 +1,10 @@ package ly.count.sdk.java.internal; +import java.io.BufferedReader; import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileReader; import java.io.IOException; import java.io.InputStream; import java.io.UnsupportedEncodingException; @@ -8,6 +12,7 @@ import java.math.BigDecimal; import java.net.URLDecoder; import java.net.URLEncoder; +import java.nio.file.Files; import java.security.MessageDigest; import java.util.ArrayList; import java.util.Arrays; @@ -18,6 +23,7 @@ import java.util.Map; import java.util.StringJoiner; import java.util.stream.Collectors; +import java.util.stream.Stream; /** * Utility class @@ -292,6 +298,32 @@ public static boolean isValidDataType(Object value) { value instanceof Float; } + /** + * Read file content using UTF-8 encoding into a string and + * append lines to a "StringBuilder" and return it + * If file doesn't exist, return empty string + * + * @param file to read + * @return file contents or empty string + * @throws IOException if file exists but couldn't be read + */ + public static String readFileContent(File file) throws IOException { + StringBuilder fileContent = new StringBuilder(); + + if (!file.exists()) { + return fileContent.toString(); + } + + try (BufferedReader reader = new BufferedReader(new FileReader(file))) { + String line; + while ((line = reader.readLine()) != null) { + fileContent.append(line); + } + } + + return fileContent.toString(); + } + public static class Base64 { public static String encode(byte[] bytes) { return ly.count.sdk.java.internal.Base64.encodeBytes(bytes); diff --git a/sdk-java/src/test/java/ly/count/sdk/java/internal/UtilsTests.java b/sdk-java/src/test/java/ly/count/sdk/java/internal/UtilsTests.java index 419d9e2b7..5a293fd7b 100644 --- a/sdk-java/src/test/java/ly/count/sdk/java/internal/UtilsTests.java +++ b/sdk-java/src/test/java/ly/count/sdk/java/internal/UtilsTests.java @@ -1,7 +1,8 @@ package ly.count.sdk.java.internal; -import java.util.*; - +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; import java.math.BigDecimal; import java.util.ArrayList; import java.util.Collection; @@ -20,6 +21,8 @@ public class UtilsTests { Log logger; + static final String TEST_FILE_NAME = "testFile"; + @Before public void setupEveryTest() { logger = new Log(Config.LoggingLevel.VERBOSE, null); @@ -236,4 +239,67 @@ public void isValidDataType() { Assert.assertFalse(Utils.isValidDataType(new ArrayList<>())); Assert.assertFalse(Utils.isValidDataType(new HashMap<>())); } + + /** + * It checks if the "readFileContent" method is called. + * And if the created file is read correctly. + */ + @Test + public void readFileContent() throws IOException { + String fileName = "testFile"; + String fileContent = "testContent"; + + File file = new File(fileName); + file.createNewFile(); + FileWriter writer = new FileWriter(file); + writer.write(fileContent); + writer.close(); + + String result = Utils.readFileContent(file); + //delete file + file.delete(); + Assert.assertEquals(fileContent, result); + } + + /** + * If the file does not exist, + * the method should return an empty string. + */ + @Test + public void readFileContent_fileNotExist() throws IOException { + String fileName = "testFile"; + String fileContent = "testContent"; + + File file = new File(fileName); + + String result = Utils.readFileContent(file); + + Assert.assertNotEquals(fileContent, result); + Assert.assertEquals("", result); + } + + /** + * If the file is not readable for some reason, + * the method should throw exception. + */ + @Test(expected = IOException.class) + public void readFileContent_fileNotReadable() throws IOException { + try { + String fileContent = "testContent"; + + File file = new File(TEST_FILE_NAME); + file.createNewFile(); + FileWriter writer = new FileWriter(file); + writer.write(fileContent); + writer.close(); + file.setReadable(false); + + Utils.readFileContent(file); + } finally { + File file = new File(TEST_FILE_NAME); + if (file.exists()) { + file.delete(); + } + } + } } From da65f86ac951f10b8a6442ff2dbcdc2d5d66cef9 Mon Sep 17 00:00:00 2001 From: arifBurakDemiray Date: Thu, 21 Sep 2023 14:23:07 +0300 Subject: [PATCH 56/82] feat: get current event queue test util --- .../count/sdk/java/internal/SDKStorage.java | 22 +++------- .../ly/count/sdk/java/internal/TestUtils.java | 43 +++++++++++++++++-- 2 files changed, 46 insertions(+), 19 deletions(-) diff --git a/sdk-java/src/main/java/ly/count/sdk/java/internal/SDKStorage.java b/sdk-java/src/main/java/ly/count/sdk/java/internal/SDKStorage.java index 415c31d25..7291a8ade 100644 --- a/sdk-java/src/main/java/ly/count/sdk/java/internal/SDKStorage.java +++ b/sdk-java/src/main/java/ly/count/sdk/java/internal/SDKStorage.java @@ -21,11 +21,9 @@ public class SDKStorage { private Log L; private CtxCore ctx; - - private static final String FILE_NAME_PREFIX = "[CLY]"; - private static final String FILE_NAME_SEPARATOR = "_"; - - private static final String EVENT_QUEUE_FILE_NAME = "event_queue"; + protected static final String FILE_NAME_PREFIX = "[CLY]"; + protected static final String FILE_NAME_SEPARATOR = "_"; + protected static final String EVENT_QUEUE_FILE_NAME = "event_queue"; protected SDKStorage() { @@ -302,21 +300,15 @@ protected String readEventQueue() { L.d("[SDKStorage] Getting event queue"); File file = new File(ctx.getContext(), FILE_NAME_PREFIX + FILE_NAME_SEPARATOR + EVENT_QUEUE_FILE_NAME); - if (!file.exists()) { - return ""; - } + String eventQueue = ""; - StringBuilder eventQueue = new StringBuilder(); - try (BufferedReader reader = new BufferedReader(new FileReader(file))) { - String line; - while ((line = reader.readLine()) != null) { - eventQueue.append(line); - } + try { + eventQueue = Utils.readFileContent(file); } catch (IOException e) { // Handle the error if reading fails L.e("[SDKStorage] Failed to read EQ from json file: " + e); } - return eventQueue.toString(); + return eventQueue; } } diff --git a/sdk-java/src/test/java/ly/count/sdk/java/internal/TestUtils.java b/sdk-java/src/test/java/ly/count/sdk/java/internal/TestUtils.java index 2f75c15ff..0db1a43c5 100644 --- a/sdk-java/src/test/java/ly/count/sdk/java/internal/TestUtils.java +++ b/sdk-java/src/test/java/ly/count/sdk/java/internal/TestUtils.java @@ -2,15 +2,22 @@ import java.io.File; import java.io.IOException; +import java.util.Arrays; import java.util.Comparator; import java.util.HashMap; import java.util.Map; import java.util.Scanner; import java.util.stream.Stream; +import org.json.JSONArray; +import org.json.JSONObject; + +import static ly.count.sdk.java.internal.SDKStorage.EVENT_QUEUE_FILE_NAME; +import static ly.count.sdk.java.internal.SDKStorage.FILE_NAME_PREFIX; +import static ly.count.sdk.java.internal.SDKStorage.FILE_NAME_SEPARATOR; public class TestUtils { - public TestUtils() { + private TestUtils() { } /** @@ -20,7 +27,7 @@ public TestUtils() { * @param logger logger * @return array of request params */ - public Map[] getCurrentRequestQueue(File targetFolder, Log logger) { + protected static Map[] getCurrentRequestQueue(File targetFolder, Log logger) { //check whether target folder is a directory or not if (!targetFolder.isDirectory()) { @@ -48,13 +55,41 @@ public Map[] getCurrentRequestQueue(File targetFolder, Log logge return resultMapArray; } + /** + * Get current event queue from target folder + * + * @param targetFolder where events are stored + * @param logger logger + * @return array of json events + */ + protected static JSONArray getCurrentEventQueue(File targetFolder, Log logger) { + JSONArray result = new JSONArray(); + + if (!targetFolder.isDirectory()) { + logger.e("[TestUtils] " + targetFolder.getAbsolutePath() + " is not a directory"); + return result; + } + + File file = new File(targetFolder, FILE_NAME_PREFIX + FILE_NAME_SEPARATOR + EVENT_QUEUE_FILE_NAME); + String fileContent = ""; + try { + fileContent = Utils.readFileContent(file); + } catch (IOException e) { + //do nothing + } + + Arrays.stream(fileContent.split(EventImplQueue.DELIMITER)).forEach(s -> result.put(new JSONObject(s))); + + return result; + } + /** * Get request files from target folder, sorted by last modified * * @param targetFolder folder where requests are stored * @return array of request files sorted by last modified */ - private File[] getRequestFiles(File targetFolder) { + private static File[] getRequestFiles(File targetFolder) { File[] files = targetFolder.listFiles(); if (files == null) { @@ -76,7 +111,7 @@ private File[] getRequestFiles(File targetFolder) { * @return map of request params * @throws IOException if file cannot be read */ - private Map parseRequestParams(File file) throws IOException { + private static Map parseRequestParams(File file) throws IOException { try (Scanner scanner = new Scanner(file)) { String firstLine = scanner.nextLine(); String urlDecodedStr = Utils.urldecode(firstLine); From 77df326446db85bacf87ff3038fa912eebfb5724 Mon Sep 17 00:00:00 2001 From: arifBurakDemiray Date: Thu, 21 Sep 2023 16:05:38 +0300 Subject: [PATCH 57/82] feat: test utils to get last item and return event impl1 --- .../ly/count/sdk/java/internal/TestUtils.java | 31 ++++++++++++++----- 1 file changed, 24 insertions(+), 7 deletions(-) diff --git a/sdk-java/src/test/java/ly/count/sdk/java/internal/TestUtils.java b/sdk-java/src/test/java/ly/count/sdk/java/internal/TestUtils.java index 0db1a43c5..22dab3932 100644 --- a/sdk-java/src/test/java/ly/count/sdk/java/internal/TestUtils.java +++ b/sdk-java/src/test/java/ly/count/sdk/java/internal/TestUtils.java @@ -2,14 +2,14 @@ import java.io.File; import java.io.IOException; +import java.util.ArrayList; import java.util.Arrays; import java.util.Comparator; import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.Scanner; import java.util.stream.Stream; -import org.json.JSONArray; -import org.json.JSONObject; import static ly.count.sdk.java.internal.SDKStorage.EVENT_QUEUE_FILE_NAME; import static ly.count.sdk.java.internal.SDKStorage.FILE_NAME_PREFIX; @@ -62,12 +62,12 @@ protected static Map[] getCurrentRequestQueue(File targetFolder, * @param logger logger * @return array of json events */ - protected static JSONArray getCurrentEventQueue(File targetFolder, Log logger) { - JSONArray result = new JSONArray(); + protected static List getCurrentEventQueue(File targetFolder, Log logger) { + List events = new ArrayList<>(); if (!targetFolder.isDirectory()) { logger.e("[TestUtils] " + targetFolder.getAbsolutePath() + " is not a directory"); - return result; + return events; } File file = new File(targetFolder, FILE_NAME_PREFIX + FILE_NAME_SEPARATOR + EVENT_QUEUE_FILE_NAME); @@ -78,9 +78,26 @@ protected static JSONArray getCurrentEventQueue(File targetFolder, Log logger) { //do nothing } - Arrays.stream(fileContent.split(EventImplQueue.DELIMITER)).forEach(s -> result.put(new JSONObject(s))); + Arrays.stream(fileContent.split(EventImplQueue.DELIMITER)).forEach(s -> { + final EventImpl event = EventImpl.fromJSON(s, (ev) -> { + }, logger); + if (event != null) { + events.add(event); + } + }); + + return events; + } - return result; + /** + * Get last item from list + * + * @param list + * @param type of list + * @return last item from list + */ + public static T getLastItem(List list) { + return list.isEmpty() ? null : list.get(list.size() - 1); } /** From cb4aac47f7e1203a596e6a9e66583b99dfd3538e Mon Sep 17 00:00:00 2001 From: arifBurakDemiray Date: Thu, 21 Sep 2023 16:06:08 +0300 Subject: [PATCH 58/82] feat: restore event queue when starting event module1 --- .../java/ly/count/sdk/java/internal/EventImplQueue.java | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/sdk-java/src/main/java/ly/count/sdk/java/internal/EventImplQueue.java b/sdk-java/src/main/java/ly/count/sdk/java/internal/EventImplQueue.java index 7f4c4461f..e1822d1f2 100644 --- a/sdk-java/src/main/java/ly/count/sdk/java/internal/EventImplQueue.java +++ b/sdk-java/src/main/java/ly/count/sdk/java/internal/EventImplQueue.java @@ -63,6 +63,7 @@ public synchronized List getEventList() { } // order the events from least to most recent events.sort((e1, e2) -> (int) (e1.timestamp - e2.timestamp)); + size = events.size(); return events; } @@ -79,4 +80,12 @@ private synchronized String[] getEvents() { final String joinedEventsStr = SDKCore.instance.sdkStorage.readEventQueue(); return joinedEventsStr.isEmpty() ? new String[0] : joinedEventsStr.split(DELIMITER); } + + /** + * Restores events from disk + */ + void restore(){ + L.d("[EventImplQueue] Restoring events from disk"); + getEventList(); + } } From 444c5a056486666f076148cc3d29af138a1b227d Mon Sep 17 00:00:00 2001 From: arifBurakDemiray Date: Thu, 21 Sep 2023 16:06:25 +0300 Subject: [PATCH 59/82] feat: use restoore --- .../src/main/java/ly/count/sdk/java/internal/ModuleEvents.java | 1 + 1 file changed, 1 insertion(+) diff --git a/sdk-java/src/main/java/ly/count/sdk/java/internal/ModuleEvents.java b/sdk-java/src/main/java/ly/count/sdk/java/internal/ModuleEvents.java index 00d0d53bf..45e1ce0d0 100644 --- a/sdk-java/src/main/java/ly/count/sdk/java/internal/ModuleEvents.java +++ b/sdk-java/src/main/java/ly/count/sdk/java/internal/ModuleEvents.java @@ -22,6 +22,7 @@ public void init(InternalConfig config, Log logger) { super.init(config, logger); L.d("[ModuleEvents] init: config = " + config); eventQueue = new EventImplQueue(L, config); + eventQueue.restore(); eventsInterface = new Events(); } From 5a50040ed86f3f2305a910b222ab0c00da2b26d4 Mon Sep 17 00:00:00 2001 From: arifBurakDemiray Date: Thu, 21 Sep 2023 16:07:00 +0300 Subject: [PATCH 60/82] feat: storage init --- .../main/java/ly/count/sdk/java/internal/SDKStorage.java | 5 ++--- .../src/main/java/ly/count/sdk/java/internal/Storage.java | 8 +++++--- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/sdk-java/src/main/java/ly/count/sdk/java/internal/SDKStorage.java b/sdk-java/src/main/java/ly/count/sdk/java/internal/SDKStorage.java index 7291a8ade..c4905acbb 100644 --- a/sdk-java/src/main/java/ly/count/sdk/java/internal/SDKStorage.java +++ b/sdk-java/src/main/java/ly/count/sdk/java/internal/SDKStorage.java @@ -1,13 +1,11 @@ package ly.count.sdk.java.internal; -import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; -import java.io.FileReader; import java.io.FileWriter; import java.io.IOException; import java.nio.channels.FileLock; @@ -32,6 +30,7 @@ protected SDKStorage() { public void init(CtxCore ctx, Log logger) { this.L = logger; this.ctx = ctx; + Storage.init(); } public void stop(ly.count.sdk.java.internal.CtxCore ctx, boolean clear) { @@ -302,7 +301,7 @@ protected String readEventQueue() { String eventQueue = ""; - try { + try { eventQueue = Utils.readFileContent(file); } catch (IOException e) { // Handle the error if reading fails diff --git a/sdk-java/src/main/java/ly/count/sdk/java/internal/Storage.java b/sdk-java/src/main/java/ly/count/sdk/java/internal/Storage.java index b8d59081b..4bbc0a20d 100644 --- a/sdk-java/src/main/java/ly/count/sdk/java/internal/Storage.java +++ b/sdk-java/src/main/java/ly/count/sdk/java/internal/Storage.java @@ -1,7 +1,5 @@ package ly.count.sdk.java.internal; -import java.util.Collections; -import java.util.Comparator; import java.util.List; import java.util.Map; import java.util.concurrent.ExecutionException; @@ -15,7 +13,11 @@ public class Storage { - private static final Tasks tasks = new Tasks("storage", null); + private static Tasks tasks; + + static void init() { + tasks = new Tasks("storage", null); + } public static String name(Storable storable) { return storable.storagePrefix() + "_" + storable.storageId(); From 8749d07d271cc620b15ebcbe08181944e49c44b0 Mon Sep 17 00:00:00 2001 From: arifBurakDemiray Date: Thu, 21 Sep 2023 16:07:20 +0300 Subject: [PATCH 61/82] feat: module event tests for recording event --- .../sdk/java/internal/ModuleEventsTests.java | 150 ++++++++++++++++++ 1 file changed, 150 insertions(+) create mode 100644 sdk-java/src/test/java/ly/count/sdk/java/internal/ModuleEventsTests.java diff --git a/sdk-java/src/test/java/ly/count/sdk/java/internal/ModuleEventsTests.java b/sdk-java/src/test/java/ly/count/sdk/java/internal/ModuleEventsTests.java new file mode 100644 index 000000000..d16e1c10a --- /dev/null +++ b/sdk-java/src/test/java/ly/count/sdk/java/internal/ModuleEventsTests.java @@ -0,0 +1,150 @@ +package ly.count.sdk.java.internal; + +import java.io.File; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import ly.count.sdk.java.Config; +import ly.count.sdk.java.Countly; +import org.junit.AfterClass; +import org.junit.Assert; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.FixMethodOrder; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; +import org.junit.runners.MethodSorters; + +@RunWith(JUnit4.class) +@FixMethodOrder(MethodSorters.NAME_ASCENDING) +public class ModuleEventsTests { + + private ModuleEvents moduleEvents; + + @BeforeClass + public static void init() { + if(Countly.isInitialized()) return; + // System specific folder structure + String[] sdkStorageRootPath = { System.getProperty("user.home"), "__COUNTLY", "java_test" }; + File sdkStorageRootDirectory = new File(String.join(File.separator, sdkStorageRootPath)); + + if (sdkStorageRootDirectory.mkdirs()) { + throw new RuntimeException("Directory creation failed"); + } + Config cc = new Config("https://try.count.ly", "COUNTLY_APP_KEY", sdkStorageRootDirectory); + + cc.enableFeatures(Config.Feature.Events).setEventQueueSizeToSend(4); + Countly.instance().init(cc); + } + + @AfterClass + public static void stop() { + Countly.stop(false); + } + + @Before + public void start() { + moduleEvents = (ModuleEvents) SDKCore.instance.module(CoreFeature.Events.getIndex()); + } + + /** + * Records an event and checks if it was recorded correctly + * by looking into event queue + */ + @Test + public void recordEvent() { + moduleEvents.eventQueue.clear(); + Assert.assertEquals(0, moduleEvents.eventQueue.size); + + //create segmentation + Map segmentation = new HashMap<>(); + segmentation.put("name", "Johny"); + segmentation.put("weight", 67); + segmentation.put("bald", true); + + //record event with key segmentation and count + Countly.instance().events().recordEvent("test-recordEvent", 1, 45.9, segmentation, 32.0); + + //check if event was recorded correctly and size of event queue is equal to size of events in queue + List events = TestUtils.getCurrentEventQueue(moduleEvents.ctx.getContext(), moduleEvents.L); + Assert.assertEquals(1, moduleEvents.eventQueue.size); + Assert.assertEquals(1, events.size()); + + //check if event was recorded correctly + EventImpl event = events.get(0); + + Assert.assertEquals("test-recordEvent", event.key); + Assert.assertEquals(1, event.count); + Assert.assertEquals(segmentation, event.segmentation); + Assert.assertEquals(new Double(45.9), event.sum); + Assert.assertEquals(new Double(32.0), event.duration); + } + + /** + * Records an event with for the next test case + */ + @Test + public void recordEvent_fillEventQueue() { + try { + Assert.assertEquals(1, moduleEvents.eventQueue.size); + + //create segmentation + Map segmentation = new HashMap<>(); + segmentation.put("size", "xl"); + segmentation.put("height", 184); + segmentation.put("married", false); + + //record event with key segmentation + Countly.instance().events().recordEvent("test-recordEvent-Filler", segmentation); + + //check if event was recorded correctly and size of event queue is equal to size of events in queue + List events = TestUtils.getCurrentEventQueue(moduleEvents.ctx.getContext(), moduleEvents.L); + EventImpl q1 = events.get(0); + EventImpl q2 = events.get(1); + Assert.assertEquals(2, moduleEvents.eventQueue.size); + Assert.assertEquals(2, events.size()); + Assert.assertEquals("test-recordEvent", q1.key); + Assert.assertEquals(67, q1.segmentation.get("weight")); + Assert.assertNotEquals(segmentation, q1.segmentation); + Assert.assertEquals(segmentation, q2.segmentation); + Assert.assertEquals("test-recordEvent-Filler", q2.key); + } finally { + moduleEvents.eventQueue.clear(); + } + } + + /** + * This test case re-inits Countly and tries to read + * existing event queue from storage + */ + @Test + public void testInDiskEventQueue(){ + Assert.assertEquals(0, moduleEvents.eventQueue.size); + + //create segmentation + Map segmentation = new HashMap<>(); + segmentation.put("exam_name", "CENG 101"); + segmentation.put("score", 100); + segmentation.put("cheated", false); + + //record event with key segmentation + Countly.instance().events().recordEvent("testInDiskEventQueue", segmentation); + + //now purposely re-init Countly + stop(); + System.out.println("Countly stopped"); + init(); + start(); + + //check if event was recorded correctly and size of event queue is equal to size of events in queue + List events = TestUtils.getCurrentEventQueue(moduleEvents.ctx.getContext(), moduleEvents.L); + Assert.assertEquals(1, moduleEvents.eventQueue.size); + Assert.assertEquals(1, events.size()); + + //check if event was recorded correctly + EventImpl event = events.get(0); + Assert.assertEquals("testInDiskEventQueue", event.key); + Assert.assertEquals(segmentation, event.segmentation); + } +} From ca83ea0fa80c8e79712ae15c8b127276984bbce8 Mon Sep 17 00:00:00 2001 From: arifBurakDemiray Date: Fri, 22 Sep 2023 10:33:31 +0300 Subject: [PATCH 62/82] fix: rename read event queue func --- .../test/java/ly/count/sdk/java/internal/ModuleEventsTests.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk-java/src/test/java/ly/count/sdk/java/internal/ModuleEventsTests.java b/sdk-java/src/test/java/ly/count/sdk/java/internal/ModuleEventsTests.java index d16e1c10a..ff826347c 100644 --- a/sdk-java/src/test/java/ly/count/sdk/java/internal/ModuleEventsTests.java +++ b/sdk-java/src/test/java/ly/count/sdk/java/internal/ModuleEventsTests.java @@ -119,7 +119,7 @@ public void recordEvent_fillEventQueue() { * existing event queue from storage */ @Test - public void testInDiskEventQueue(){ + public void recordEvent_reInitCountly(){ Assert.assertEquals(0, moduleEvents.eventQueue.size); //create segmentation From 9dc9fe3e35989dc7ee653cc586165945e6b43276 Mon Sep 17 00:00:00 2001 From: arifBurakDemiray Date: Fri, 22 Sep 2023 11:37:12 +0300 Subject: [PATCH 63/82] feat: reearrange test order and add new tests --- .../sdk/java/internal/ModuleEventsTests.java | 146 +++++++++++++++++- 1 file changed, 140 insertions(+), 6 deletions(-) diff --git a/sdk-java/src/test/java/ly/count/sdk/java/internal/ModuleEventsTests.java b/sdk-java/src/test/java/ly/count/sdk/java/internal/ModuleEventsTests.java index ff826347c..0f40e6383 100644 --- a/sdk-java/src/test/java/ly/count/sdk/java/internal/ModuleEventsTests.java +++ b/sdk-java/src/test/java/ly/count/sdk/java/internal/ModuleEventsTests.java @@ -10,21 +10,18 @@ import org.junit.Assert; import org.junit.Before; import org.junit.BeforeClass; -import org.junit.FixMethodOrder; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; -import org.junit.runners.MethodSorters; @RunWith(JUnit4.class) -@FixMethodOrder(MethodSorters.NAME_ASCENDING) public class ModuleEventsTests { private ModuleEvents moduleEvents; @BeforeClass public static void init() { - if(Countly.isInitialized()) return; + if (Countly.isInitialized()) return; // System specific folder structure String[] sdkStorageRootPath = { System.getProperty("user.home"), "__COUNTLY", "java_test" }; File sdkStorageRootDirectory = new File(String.join(File.separator, sdkStorageRootPath)); @@ -86,6 +83,7 @@ public void recordEvent() { */ @Test public void recordEvent_fillEventQueue() { + recordEvent(); // fill the queue try { Assert.assertEquals(1, moduleEvents.eventQueue.size); @@ -119,7 +117,8 @@ public void recordEvent_fillEventQueue() { * existing event queue from storage */ @Test - public void recordEvent_reInitCountly(){ + public void recordEvent_reInitCountly() { + moduleEvents.eventQueue.clear(); Assert.assertEquals(0, moduleEvents.eventQueue.size); //create segmentation @@ -133,7 +132,6 @@ public void recordEvent_reInitCountly(){ //now purposely re-init Countly stop(); - System.out.println("Countly stopped"); init(); start(); @@ -147,4 +145,140 @@ public void recordEvent_reInitCountly(){ Assert.assertEquals("testInDiskEventQueue", event.key); Assert.assertEquals(segmentation, event.segmentation); } + + /** + * start event successfully created timed event and end it + */ + @Test + public void startEvent() { + String eventName = "test-startEvent"; + + try { + startEvent(eventName); + } finally { + endEvent(eventName, null, 1, null); + } + } + + /** + * start event should not create with empty key + */ + @Test + public void startEvent_emptyKey() { + Assert.assertFalse(Countly.instance().events().startEvent("")); + } + + /** + * start event should not create with null key + */ + @Test + public void startEvent_nullKey() { + Assert.assertFalse(Countly.instance().events().startEvent(null)); + } + + /** + * start event should not create with already started event + */ + @Test + public void startEvent_alreadyStarted() { + + String eventName = "test-startEvent_alreadyStarted"; + + try { + startEvent(eventName); + boolean result = Countly.instance().events().startEvent(eventName); + Assert.assertFalse(result); + } finally { + endEvent(eventName, null, 1, null); + } + } + + /** + * end event successfully ended the timed event + */ + @Test + public void endEvent() { + String eventName = "test-endEvent"; + + startEvent(eventName); // start event to end it + endEvent(eventName, null, 1, null); + } + + /** + * End event with empty key should not net + */ + @Test + public void endEvent_emptyKey() { + Assert.assertFalse(Countly.instance().events().endEvent("")); + } + + /** + * End event with null key should not end + */ + @Test + public void endEvent_nullKey() { + Assert.assertFalse(Countly.instance().events().endEvent(null)); + } + + /** + * End event with not started event should not work + */ + @Test + public void endEvent_notStarted() { + Assert.assertFalse(Countly.instance().events().endEvent("test-endEvent_notStarted")); + } + + /** + * End event with already ended event should not process + */ + @Test + public void endEvent_alreadyEnded() { + Assert.assertFalse(Countly.instance().events().endEvent("test-endEvent_alreadyEnded")); + } + + /** + * End event with segmentation should end successfully + */ + @Test + public void endEvent_withSegmentation() { + + String eventName = "test-endEvent_withSegmentation"; + + startEvent(eventName); // start event to end it + + Map segmentation = new HashMap<>(); + segmentation.put("hair_color", "red"); + segmentation.put("hair_length", "short"); + segmentation.put("chauffeur", "g3chauffeur"); // + + endEvent(eventName, segmentation, 1, 5.0); + } + + /** + * End event with segmentation and negative count should throw IllegalArgumentException + */ + @Test(expected = IllegalArgumentException.class) + public void endEvent_withSegmentation_negativeCount() { + + String eventName = "test-endEvent_withSegmentation_negativeCount"; + + startEvent(eventName); // start event to end it + + Map segmentation = new HashMap<>(); + segmentation.put("horse_name", "Alice"); + segmentation.put("bet_amount", 300); + segmentation.put("currency", "Dollar"); // + + endEvent(eventName, segmentation, -7, 67.0); + } + + private void endEvent(String key, Map segmentation, int count, Double sum) { + boolean result = Countly.instance().events().endEvent(key, segmentation, count, sum); + Assert.assertTrue(result); + } + + private void startEvent(String key) { + boolean result = Countly.instance().events().startEvent(key); + Assert.assertTrue(result); + } } From dcb6ce4c51aabd73953edd33776fe8cdc3e7864f Mon Sep 17 00:00:00 2001 From: arifBurakDemiray Date: Fri, 22 Sep 2023 16:59:18 +0300 Subject: [PATCH 64/82] refactor: to in memory cache --- .../sdk/java/internal/EventImplQueue.java | 71 +++++++++---------- .../count/sdk/java/internal/ModuleEvents.java | 9 +-- .../sdk/java/internal/ModuleEventsTests.java | 12 ++-- 3 files changed, 44 insertions(+), 48 deletions(-) diff --git a/sdk-java/src/main/java/ly/count/sdk/java/internal/EventImplQueue.java b/sdk-java/src/main/java/ly/count/sdk/java/internal/EventImplQueue.java index e1822d1f2..45e74aa5b 100644 --- a/sdk-java/src/main/java/ly/count/sdk/java/internal/EventImplQueue.java +++ b/sdk-java/src/main/java/ly/count/sdk/java/internal/EventImplQueue.java @@ -7,69 +7,67 @@ public class EventImplQueue { static final String DELIMITER = ":::"; - private final Log L; - - protected int size = 0; private final InternalConfig config; + final List eventQueueMemoryCache; protected EventImplQueue(Log logger, InternalConfig config) { L = logger; this.config = config; + eventQueueMemoryCache = new ArrayList<>(config.getEventsBufferSize()); } - void addEvent(final EventImpl event) { - L.d("[EventImplQueue] Adding event: " + event.key); - final List events = getEventList(); - if (events.size() < config.getEventsBufferSize()) { - events.add(event); - size = events.size(); - setEventData(joinEvents(events)); - } + /** + * Returns the number of events currently stored in the queue. + */ + protected int eqSize() { + return eventQueueMemoryCache.size(); } - String joinEvents(final Collection collection) { - final List strings = new ArrayList<>(); - for (EventImpl e : collection) { - strings.add(e.toJSON(L)); + void addEvent(final EventImpl event) { + L.d("[EventImplQueue] Adding event: " + event.key); + if (eventQueueMemoryCache.size() < config.getEventsBufferSize()) { + eventQueueMemoryCache.add(event); + writeEventQueueToStorage(); } - return Utils.join(strings, EventImplQueue.DELIMITER); } /** * set the new value in event data storage - * - * @param eventData */ - void setEventData(String eventData) { - L.d("[EventImplQueue] Setting event data: " + eventData); - SDKCore.instance.sdkStorage.storeEventQueue(eventData); + void writeEventQueueToStorage() { + final String eventQueue = joinEvents(eventQueueMemoryCache); + + L.d("[EventImplQueue] Setting event data: " + eventQueue); + SDKCore.instance.sdkStorage.storeEventQueue(eventQueue); } /** - * Returns a list of the current stored events, sorted by timestamp from oldest to newest. + * Restores events from disk */ - public synchronized List getEventList() { - L.d("[EventImplQueue] Getting event list"); + void restore() { + L.d("[EventImplQueue] Restoring events from disk"); + eventQueueMemoryCache.clear(); + final String[] array = getEvents(); - final List events = new ArrayList<>(array.length); for (String s : array) { final EventImpl event = EventImpl.fromJSON(s, (ev) -> { }, L); if (event != null) { - events.add(event); + eventQueueMemoryCache.add(event); } } // order the events from least to most recent - events.sort((e1, e2) -> (int) (e1.timestamp - e2.timestamp)); - size = events.size(); - return events; + eventQueueMemoryCache.sort((e1, e2) -> (int) (e1.timestamp - e2.timestamp)); } - public void clear() { - size = 0; - SDKCore.instance.sdkStorage.storeEventQueue(""); + private String joinEvents(final Collection collection) { + final List strings = new ArrayList<>(); + for (EventImpl e : collection) { + strings.add(e.toJSON(L)); + } + return Utils.join(strings, EventImplQueue.DELIMITER); } /** @@ -81,11 +79,8 @@ private synchronized String[] getEvents() { return joinedEventsStr.isEmpty() ? new String[0] : joinedEventsStr.split(DELIMITER); } - /** - * Restores events from disk - */ - void restore(){ - L.d("[EventImplQueue] Restoring events from disk"); - getEventList(); + public void clear() { + SDKCore.instance.sdkStorage.storeEventQueue(""); + eventQueueMemoryCache.clear(); } } diff --git a/sdk-java/src/main/java/ly/count/sdk/java/internal/ModuleEvents.java b/sdk-java/src/main/java/ly/count/sdk/java/internal/ModuleEvents.java index 45e1ce0d0..d33d003cd 100644 --- a/sdk-java/src/main/java/ly/count/sdk/java/internal/ModuleEvents.java +++ b/sdk-java/src/main/java/ly/count/sdk/java/internal/ModuleEvents.java @@ -7,6 +7,7 @@ import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; +import javax.annotation.Nonnull; import ly.count.sdk.java.Countly; public class ModuleEvents extends ModuleBase { @@ -27,9 +28,9 @@ public void init(InternalConfig config, Log logger) { } @Override - public void onContextAcquired(CtxCore ctx) { + public void onContextAcquired(@Nonnull CtxCore ctx) { this.ctx = ctx; - L.d("[ModuleEvents] onContextAcquired: " + ctx.toString()); + L.d("[ModuleEvents] onContextAcquired: " + ctx); if (ctx.getConfig().getSendUpdateEachSeconds() > 0 && executor == null) { executor = Executors.newScheduledThreadPool(1); @@ -52,7 +53,7 @@ private synchronized void addEventsToRequestQ() { Request request = new Request(); request.params.add("device_id", Countly.instance().getDeviceId()); - request.params.arr("events").put(eventQueue.getEventList()).add(); + request.params.arr("events").put(eventQueue.eventQueueMemoryCache).add(); request.own(ModuleEvents.class); eventQueue.clear(); @@ -95,7 +96,7 @@ protected void recordEventInternal(String key, int count, Double sum, Map= internalConfig.getEventsBufferSize()) { + if (eventQueue.eqSize() >= internalConfig.getEventsBufferSize()) { L.d("[ModuleEvents] addEventToQueue: eventQueue.size >= internalConfig.getEventsBufferSize()"); addEventsToRequestQ(); } diff --git a/sdk-java/src/test/java/ly/count/sdk/java/internal/ModuleEventsTests.java b/sdk-java/src/test/java/ly/count/sdk/java/internal/ModuleEventsTests.java index 0f40e6383..72818deb3 100644 --- a/sdk-java/src/test/java/ly/count/sdk/java/internal/ModuleEventsTests.java +++ b/sdk-java/src/test/java/ly/count/sdk/java/internal/ModuleEventsTests.java @@ -52,7 +52,7 @@ public void start() { @Test public void recordEvent() { moduleEvents.eventQueue.clear(); - Assert.assertEquals(0, moduleEvents.eventQueue.size); + Assert.assertEquals(0, moduleEvents.eventQueue.eqSize()); //create segmentation Map segmentation = new HashMap<>(); @@ -65,7 +65,7 @@ public void recordEvent() { //check if event was recorded correctly and size of event queue is equal to size of events in queue List events = TestUtils.getCurrentEventQueue(moduleEvents.ctx.getContext(), moduleEvents.L); - Assert.assertEquals(1, moduleEvents.eventQueue.size); + Assert.assertEquals(1, moduleEvents.eventQueue.eqSize()); Assert.assertEquals(1, events.size()); //check if event was recorded correctly @@ -85,7 +85,7 @@ public void recordEvent() { public void recordEvent_fillEventQueue() { recordEvent(); // fill the queue try { - Assert.assertEquals(1, moduleEvents.eventQueue.size); + Assert.assertEquals(1, moduleEvents.eventQueue.eqSize()); //create segmentation Map segmentation = new HashMap<>(); @@ -100,7 +100,7 @@ public void recordEvent_fillEventQueue() { List events = TestUtils.getCurrentEventQueue(moduleEvents.ctx.getContext(), moduleEvents.L); EventImpl q1 = events.get(0); EventImpl q2 = events.get(1); - Assert.assertEquals(2, moduleEvents.eventQueue.size); + Assert.assertEquals(2, moduleEvents.eventQueue.eqSize()); Assert.assertEquals(2, events.size()); Assert.assertEquals("test-recordEvent", q1.key); Assert.assertEquals(67, q1.segmentation.get("weight")); @@ -119,7 +119,7 @@ public void recordEvent_fillEventQueue() { @Test public void recordEvent_reInitCountly() { moduleEvents.eventQueue.clear(); - Assert.assertEquals(0, moduleEvents.eventQueue.size); + Assert.assertEquals(0, moduleEvents.eventQueue.eqSize()); //create segmentation Map segmentation = new HashMap<>(); @@ -137,7 +137,7 @@ public void recordEvent_reInitCountly() { //check if event was recorded correctly and size of event queue is equal to size of events in queue List events = TestUtils.getCurrentEventQueue(moduleEvents.ctx.getContext(), moduleEvents.L); - Assert.assertEquals(1, moduleEvents.eventQueue.size); + Assert.assertEquals(1, moduleEvents.eventQueue.eqSize()); Assert.assertEquals(1, events.size()); //check if event was recorded correctly From 3132b8e01c0b0804effe342dfded346a6ddfc4ba Mon Sep 17 00:00:00 2001 From: arifBurakDemiray Date: Fri, 22 Sep 2023 17:14:30 +0300 Subject: [PATCH 65/82] feat: standolaness for every test util --- .../ly/count/sdk/java/internal/TestUtils.java | 22 ++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/sdk-java/src/test/java/ly/count/sdk/java/internal/TestUtils.java b/sdk-java/src/test/java/ly/count/sdk/java/internal/TestUtils.java index 684cd309f..d66544c5f 100644 --- a/sdk-java/src/test/java/ly/count/sdk/java/internal/TestUtils.java +++ b/sdk-java/src/test/java/ly/count/sdk/java/internal/TestUtils.java @@ -10,6 +10,7 @@ import java.util.Map; import java.util.Scanner; import java.util.stream.Stream; +import ly.count.sdk.java.Config; import static ly.count.sdk.java.internal.SDKStorage.EVENT_QUEUE_FILE_NAME; import static ly.count.sdk.java.internal.SDKStorage.FILE_NAME_PREFIX; @@ -17,11 +18,30 @@ public class TestUtils { - private static String DELIMETER = ":::"; + static String SERVER_URL = "https://try.count.ly"; + static String SERVER_APP_KEY = "COUNTLY_APP_KEY"; private TestUtils() { } + static Config getBaseConfig() { + File sdkStorageRootDirectory = getSdkStorageRootDirectory(); + checkSdkStorageRootDirectoryExist(sdkStorageRootDirectory); + return new Config(SERVER_URL, SERVER_APP_KEY, sdkStorageRootDirectory); + } + + static File getSdkStorageRootDirectory() { + // System specific folder structure + String[] sdkStorageRootPath = { System.getProperty("user.home"), "__COUNTLY", "java_test" }; + return new File(String.join(File.separator, sdkStorageRootPath)); + } + + static void checkSdkStorageRootDirectoryExist(File directory) { + if (directory.mkdirs()) { + throw new RuntimeException("Directory creation failed"); + } + } + /** * Get current request queue from target folder * From 3b5aa056aef64b338236117c8ef4dced936467bf Mon Sep 17 00:00:00 2001 From: arifBurakDemiray Date: Mon, 25 Sep 2023 09:26:51 +0300 Subject: [PATCH 66/82] feat: conveint usages nd fix --- .../count/sdk/java/internal/ModuleEvents.java | 8 + .../sdk/java/internal/ModuleEventsTests.java | 161 ++++++++++-------- 2 files changed, 99 insertions(+), 70 deletions(-) diff --git a/sdk-java/src/main/java/ly/count/sdk/java/internal/ModuleEvents.java b/sdk-java/src/main/java/ly/count/sdk/java/internal/ModuleEvents.java index d33d003cd..d128856aa 100644 --- a/sdk-java/src/main/java/ly/count/sdk/java/internal/ModuleEvents.java +++ b/sdk-java/src/main/java/ly/count/sdk/java/internal/ModuleEvents.java @@ -48,6 +48,14 @@ public Boolean onRequest(Request request) { return true; } + @Override + public void stop(CtxCore ctx, boolean clear) { + super.stop(ctx, clear); + if (clear) { + eventQueue.clear(); + } + } + private synchronized void addEventsToRequestQ() { L.d("[ModuleEvents] addEventsToRequestQ"); diff --git a/sdk-java/src/test/java/ly/count/sdk/java/internal/ModuleEventsTests.java b/sdk-java/src/test/java/ly/count/sdk/java/internal/ModuleEventsTests.java index 72818deb3..924b6195d 100644 --- a/sdk-java/src/test/java/ly/count/sdk/java/internal/ModuleEventsTests.java +++ b/sdk-java/src/test/java/ly/count/sdk/java/internal/ModuleEventsTests.java @@ -1,15 +1,11 @@ package ly.count.sdk.java.internal; -import java.io.File; import java.util.HashMap; import java.util.List; import java.util.Map; import ly.count.sdk.java.Config; import ly.count.sdk.java.Countly; -import org.junit.AfterClass; import org.junit.Assert; -import org.junit.Before; -import org.junit.BeforeClass; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; @@ -19,30 +15,13 @@ public class ModuleEventsTests { private ModuleEvents moduleEvents; - @BeforeClass - public static void init() { - if (Countly.isInitialized()) return; - // System specific folder structure - String[] sdkStorageRootPath = { System.getProperty("user.home"), "__COUNTLY", "java_test" }; - File sdkStorageRootDirectory = new File(String.join(File.separator, sdkStorageRootPath)); - - if (sdkStorageRootDirectory.mkdirs()) { - throw new RuntimeException("Directory creation failed"); - } - Config cc = new Config("https://try.count.ly", "COUNTLY_APP_KEY", sdkStorageRootDirectory); - - cc.enableFeatures(Config.Feature.Events).setEventQueueSizeToSend(4); + private void init(Config cc) { Countly.instance().init(cc); + moduleEvents = (ModuleEvents) SDKCore.instance.module(CoreFeature.Events.getIndex()); } - @AfterClass - public static void stop() { - Countly.stop(false); - } - - @Before - public void start() { - moduleEvents = (ModuleEvents) SDKCore.instance.module(CoreFeature.Events.getIndex()); + private void stop() { + Countly.stop(true); } /** @@ -51,7 +30,10 @@ public void start() { */ @Test public void recordEvent() { - moduleEvents.eventQueue.clear(); + Config config = TestUtils.getBaseConfig(); + config.enableFeatures(Config.Feature.Events).setEventQueueSizeToSend(4); + init(config); + Assert.assertEquals(0, moduleEvents.eventQueue.eqSize()); //create segmentation @@ -70,12 +52,9 @@ public void recordEvent() { //check if event was recorded correctly EventImpl event = events.get(0); + validateEvent(event, "test-recordEvent", segmentation, 1, 45.9, 32.0, event.dow, event.hour, event.timestamp); - Assert.assertEquals("test-recordEvent", event.key); - Assert.assertEquals(1, event.count); - Assert.assertEquals(segmentation, event.segmentation); - Assert.assertEquals(new Double(45.9), event.sum); - Assert.assertEquals(new Double(32.0), event.duration); + stop(); } /** @@ -83,33 +62,27 @@ public void recordEvent() { */ @Test public void recordEvent_fillEventQueue() { - recordEvent(); // fill the queue - try { - Assert.assertEquals(1, moduleEvents.eventQueue.eqSize()); - - //create segmentation - Map segmentation = new HashMap<>(); - segmentation.put("size", "xl"); - segmentation.put("height", 184); - segmentation.put("married", false); - - //record event with key segmentation - Countly.instance().events().recordEvent("test-recordEvent-Filler", segmentation); - - //check if event was recorded correctly and size of event queue is equal to size of events in queue - List events = TestUtils.getCurrentEventQueue(moduleEvents.ctx.getContext(), moduleEvents.L); - EventImpl q1 = events.get(0); - EventImpl q2 = events.get(1); - Assert.assertEquals(2, moduleEvents.eventQueue.eqSize()); - Assert.assertEquals(2, events.size()); - Assert.assertEquals("test-recordEvent", q1.key); - Assert.assertEquals(67, q1.segmentation.get("weight")); - Assert.assertNotEquals(segmentation, q1.segmentation); - Assert.assertEquals(segmentation, q2.segmentation); - Assert.assertEquals("test-recordEvent-Filler", q2.key); - } finally { - moduleEvents.eventQueue.clear(); - } + Config config = TestUtils.getBaseConfig(); + config.enableFeatures(Config.Feature.Events).setEventQueueSizeToSend(4); + init(config); + Assert.assertEquals(0, moduleEvents.eventQueue.eqSize()); + + //create segmentation + Map segmentation = new HashMap<>(); + segmentation.put("size", "xl"); + segmentation.put("height", 184); + segmentation.put("married", false); + + //record event with key segmentation + Countly.instance().events().recordEvent("test-recordEvent-Filler", segmentation); + + //check if event was recorded correctly and size of event queue is equal to size of events in queue + List events = TestUtils.getCurrentEventQueue(moduleEvents.ctx.getContext(), moduleEvents.L); + EventImpl q1 = events.get(0); + Assert.assertEquals(1, moduleEvents.eventQueue.eqSize()); + Assert.assertEquals(1, events.size()); + validateEvent(q1, "test-recordEvent-Filler", segmentation, 1, null, null, q1.dow, q1.hour, q1.timestamp); + stop(); } /** @@ -118,6 +91,9 @@ public void recordEvent_fillEventQueue() { */ @Test public void recordEvent_reInitCountly() { + Config config = TestUtils.getBaseConfig(); + config.enableFeatures(Config.Feature.Events).setEventQueueSizeToSend(4); + init(config); moduleEvents.eventQueue.clear(); Assert.assertEquals(0, moduleEvents.eventQueue.eqSize()); @@ -132,18 +108,6 @@ public void recordEvent_reInitCountly() { //now purposely re-init Countly stop(); - init(); - start(); - - //check if event was recorded correctly and size of event queue is equal to size of events in queue - List events = TestUtils.getCurrentEventQueue(moduleEvents.ctx.getContext(), moduleEvents.L); - Assert.assertEquals(1, moduleEvents.eventQueue.eqSize()); - Assert.assertEquals(1, events.size()); - - //check if event was recorded correctly - EventImpl event = events.get(0); - Assert.assertEquals("testInDiskEventQueue", event.key); - Assert.assertEquals(segmentation, event.segmentation); } /** @@ -151,6 +115,9 @@ public void recordEvent_reInitCountly() { */ @Test public void startEvent() { + Config config = TestUtils.getBaseConfig(); + config.enableFeatures(Config.Feature.Events).setEventQueueSizeToSend(4); + init(config); String eventName = "test-startEvent"; try { @@ -158,6 +125,7 @@ public void startEvent() { } finally { endEvent(eventName, null, 1, null); } + stop(); } /** @@ -165,7 +133,11 @@ public void startEvent() { */ @Test public void startEvent_emptyKey() { + Config config = TestUtils.getBaseConfig(); + config.enableFeatures(Config.Feature.Events).setEventQueueSizeToSend(4); + init(config); Assert.assertFalse(Countly.instance().events().startEvent("")); + stop(); } /** @@ -173,7 +145,11 @@ public void startEvent_emptyKey() { */ @Test public void startEvent_nullKey() { + Config config = TestUtils.getBaseConfig(); + config.enableFeatures(Config.Feature.Events).setEventQueueSizeToSend(4); + init(config); Assert.assertFalse(Countly.instance().events().startEvent(null)); + stop(); } /** @@ -181,6 +157,9 @@ public void startEvent_nullKey() { */ @Test public void startEvent_alreadyStarted() { + Config config = TestUtils.getBaseConfig(); + config.enableFeatures(Config.Feature.Events).setEventQueueSizeToSend(4); + init(config); String eventName = "test-startEvent_alreadyStarted"; @@ -191,6 +170,7 @@ public void startEvent_alreadyStarted() { } finally { endEvent(eventName, null, 1, null); } + stop(); } /** @@ -198,10 +178,15 @@ public void startEvent_alreadyStarted() { */ @Test public void endEvent() { + Config config = TestUtils.getBaseConfig(); + config.enableFeatures(Config.Feature.Events).setEventQueueSizeToSend(4); + init(config); String eventName = "test-endEvent"; startEvent(eventName); // start event to end it endEvent(eventName, null, 1, null); + + stop(); } /** @@ -209,7 +194,11 @@ public void endEvent() { */ @Test public void endEvent_emptyKey() { + Config config = TestUtils.getBaseConfig(); + config.enableFeatures(Config.Feature.Events).setEventQueueSizeToSend(4); + init(config); Assert.assertFalse(Countly.instance().events().endEvent("")); + stop(); } /** @@ -217,7 +206,11 @@ public void endEvent_emptyKey() { */ @Test public void endEvent_nullKey() { + Config config = TestUtils.getBaseConfig(); + config.enableFeatures(Config.Feature.Events).setEventQueueSizeToSend(4); + init(config); Assert.assertFalse(Countly.instance().events().endEvent(null)); + stop(); } /** @@ -225,7 +218,11 @@ public void endEvent_nullKey() { */ @Test public void endEvent_notStarted() { + Config config = TestUtils.getBaseConfig(); + config.enableFeatures(Config.Feature.Events).setEventQueueSizeToSend(4); + init(config); Assert.assertFalse(Countly.instance().events().endEvent("test-endEvent_notStarted")); + stop(); } /** @@ -233,7 +230,11 @@ public void endEvent_notStarted() { */ @Test public void endEvent_alreadyEnded() { + Config config = TestUtils.getBaseConfig(); + config.enableFeatures(Config.Feature.Events).setEventQueueSizeToSend(4); + init(config); Assert.assertFalse(Countly.instance().events().endEvent("test-endEvent_alreadyEnded")); + stop(); } /** @@ -241,6 +242,9 @@ public void endEvent_alreadyEnded() { */ @Test public void endEvent_withSegmentation() { + Config config = TestUtils.getBaseConfig(); + config.enableFeatures(Config.Feature.Events).setEventQueueSizeToSend(4); + init(config); String eventName = "test-endEvent_withSegmentation"; @@ -252,6 +256,7 @@ public void endEvent_withSegmentation() { segmentation.put("chauffeur", "g3chauffeur"); // endEvent(eventName, segmentation, 1, 5.0); + stop(); } /** @@ -259,6 +264,9 @@ public void endEvent_withSegmentation() { */ @Test(expected = IllegalArgumentException.class) public void endEvent_withSegmentation_negativeCount() { + Config config = TestUtils.getBaseConfig(); + config.enableFeatures(Config.Feature.Events).setEventQueueSizeToSend(4); + init(config); String eventName = "test-endEvent_withSegmentation_negativeCount"; @@ -270,6 +278,19 @@ public void endEvent_withSegmentation_negativeCount() { segmentation.put("currency", "Dollar"); // endEvent(eventName, segmentation, -7, 67.0); + stop(); + } + + private void validateEvent(EventImpl gonnaValidate, String key, Map segmentation, + int count, Double sum, Double duration, int dow, int hour, long timestamp) { + Assert.assertEquals(key, gonnaValidate.key); + Assert.assertEquals(segmentation, gonnaValidate.segmentation); + Assert.assertEquals(count, gonnaValidate.count); + Assert.assertEquals(sum, gonnaValidate.sum); + Assert.assertEquals(duration, gonnaValidate.duration); + Assert.assertEquals(dow, gonnaValidate.dow); + Assert.assertEquals(hour, gonnaValidate.hour); + Assert.assertEquals(timestamp, gonnaValidate.timestamp); } private void endEvent(String key, Map segmentation, int count, Double sum) { From 58fd752c876bdd665ee5490f9099927cd0352ffb Mon Sep 17 00:00:00 2001 From: arifBurakDemiray Date: Mon, 25 Sep 2023 11:20:34 +0300 Subject: [PATCH 67/82] refactor: recordEvent function --- .../sdk/java/internal/ModuleEventsTests.java | 21 ++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/sdk-java/src/test/java/ly/count/sdk/java/internal/ModuleEventsTests.java b/sdk-java/src/test/java/ly/count/sdk/java/internal/ModuleEventsTests.java index 924b6195d..2eac2cc7d 100644 --- a/sdk-java/src/test/java/ly/count/sdk/java/internal/ModuleEventsTests.java +++ b/sdk-java/src/test/java/ly/count/sdk/java/internal/ModuleEventsTests.java @@ -25,8 +25,9 @@ private void stop() { } /** - * Records an event and checks if it was recorded correctly - * by looking into event queue + * Recording an event with segmentation + * "recordEvent" function should create an event with given key and segmentation and add it to event queue + * recorded event should have correct key, segmentation, count, sum, duration, dow, hour and timestamp */ @Test public void recordEvent() { @@ -34,7 +35,8 @@ public void recordEvent() { config.enableFeatures(Config.Feature.Events).setEventQueueSizeToSend(4); init(config); - Assert.assertEquals(0, moduleEvents.eventQueue.eqSize()); + List events = TestUtils.getCurrentEventQueue(moduleEvents.ctx.getContext(), moduleEvents.L); + validateQueueSize(0, events); //create segmentation Map segmentation = new HashMap<>(); @@ -46,14 +48,14 @@ public void recordEvent() { Countly.instance().events().recordEvent("test-recordEvent", 1, 45.9, segmentation, 32.0); //check if event was recorded correctly and size of event queue is equal to size of events in queue - List events = TestUtils.getCurrentEventQueue(moduleEvents.ctx.getContext(), moduleEvents.L); - Assert.assertEquals(1, moduleEvents.eventQueue.eqSize()); - Assert.assertEquals(1, events.size()); + events = TestUtils.getCurrentEventQueue(moduleEvents.ctx.getContext(), moduleEvents.L); + validateQueueSize(1, events); //check if event was recorded correctly EventImpl event = events.get(0); + EventImpl eventInMemory = moduleEvents.eventQueue.eventQueueMemoryCache.get(0); validateEvent(event, "test-recordEvent", segmentation, 1, 45.9, 32.0, event.dow, event.hour, event.timestamp); - + validateEvent(eventInMemory, "test-recordEvent", segmentation, 1, 45.9, 32.0, event.dow, event.hour, event.timestamp); stop(); } @@ -281,6 +283,11 @@ public void endEvent_withSegmentation_negativeCount() { stop(); } + private void validateQueueSize(int expectedSize, List events) { + Assert.assertEquals(expectedSize, events.size()); + Assert.assertEquals(expectedSize, moduleEvents.eventQueue.eqSize()); + } + private void validateEvent(EventImpl gonnaValidate, String key, Map segmentation, int count, Double sum, Double duration, int dow, int hour, long timestamp) { Assert.assertEquals(key, gonnaValidate.key); From f7502b628d98eb315e0e689d21305a252b15000b Mon Sep 17 00:00:00 2001 From: arifBurakDemiray Date: Mon, 25 Sep 2023 12:17:34 +0300 Subject: [PATCH 68/82] refactor: reformat imports --- sdk-java/src/main/java/ly/count/sdk/java/internal/Utils.java | 5 ----- 1 file changed, 5 deletions(-) diff --git a/sdk-java/src/main/java/ly/count/sdk/java/internal/Utils.java b/sdk-java/src/main/java/ly/count/sdk/java/internal/Utils.java index cfcc092b2..ddc60ad8d 100644 --- a/sdk-java/src/main/java/ly/count/sdk/java/internal/Utils.java +++ b/sdk-java/src/main/java/ly/count/sdk/java/internal/Utils.java @@ -3,7 +3,6 @@ import java.io.BufferedReader; import java.io.ByteArrayOutputStream; import java.io.File; -import java.io.FileNotFoundException; import java.io.FileReader; import java.io.IOException; import java.io.InputStream; @@ -12,7 +11,6 @@ import java.math.BigDecimal; import java.net.URLDecoder; import java.net.URLEncoder; -import java.nio.file.Files; import java.security.MessageDigest; import java.util.ArrayList; import java.util.Arrays; @@ -21,9 +19,6 @@ import java.util.Iterator; import java.util.List; import java.util.Map; -import java.util.StringJoiner; -import java.util.stream.Collectors; -import java.util.stream.Stream; /** * Utility class From ae84c77520b0756ce9891d0b8acca14fa3e2dbc8 Mon Sep 17 00:00:00 2001 From: arifBurakDemiray Date: Tue, 26 Sep 2023 16:11:13 +0300 Subject: [PATCH 69/82] refactor: module events test --- .../count/sdk/java/internal/ModuleEvents.java | 2 +- .../sdk/java/internal/ModuleEventsTests.java | 281 ++++++++++++------ .../ly/count/sdk/java/internal/TestUtils.java | 9 + 3 files changed, 201 insertions(+), 91 deletions(-) diff --git a/sdk-java/src/main/java/ly/count/sdk/java/internal/ModuleEvents.java b/sdk-java/src/main/java/ly/count/sdk/java/internal/ModuleEvents.java index d128856aa..36c024faf 100644 --- a/sdk-java/src/main/java/ly/count/sdk/java/internal/ModuleEvents.java +++ b/sdk-java/src/main/java/ly/count/sdk/java/internal/ModuleEvents.java @@ -14,7 +14,7 @@ public class ModuleEvents extends ModuleBase { protected CtxCore ctx = null; protected EventImplQueue eventQueue = null; - static final Map timedEvents = new HashMap<>(); + final Map timedEvents = new HashMap<>(); private ScheduledExecutorService executor = null; protected Events eventsInterface = null; diff --git a/sdk-java/src/test/java/ly/count/sdk/java/internal/ModuleEventsTests.java b/sdk-java/src/test/java/ly/count/sdk/java/internal/ModuleEventsTests.java index 2eac2cc7d..018f74bd7 100644 --- a/sdk-java/src/test/java/ly/count/sdk/java/internal/ModuleEventsTests.java +++ b/sdk-java/src/test/java/ly/count/sdk/java/internal/ModuleEventsTests.java @@ -5,6 +5,7 @@ import java.util.Map; import ly.count.sdk.java.Config; import ly.count.sdk.java.Countly; +import org.junit.After; import org.junit.Assert; import org.junit.Test; import org.junit.runner.RunWith; @@ -20,7 +21,8 @@ private void init(Config cc) { moduleEvents = (ModuleEvents) SDKCore.instance.module(CoreFeature.Events.getIndex()); } - private void stop() { + @After + public void stop() { Countly.stop(true); } @@ -44,8 +46,10 @@ public void recordEvent() { segmentation.put("weight", 67); segmentation.put("bald", true); + String eventKey = TestUtils.randomUUID(); + //record event with key segmentation and count - Countly.instance().events().recordEvent("test-recordEvent", 1, 45.9, segmentation, 32.0); + Countly.instance().events().recordEvent(eventKey, 1, 45.9, segmentation, 32.0); //check if event was recorded correctly and size of event queue is equal to size of events in queue events = TestUtils.getCurrentEventQueue(moduleEvents.ctx.getContext(), moduleEvents.L); @@ -54,108 +58,181 @@ public void recordEvent() { //check if event was recorded correctly EventImpl event = events.get(0); EventImpl eventInMemory = moduleEvents.eventQueue.eventQueueMemoryCache.get(0); - validateEvent(event, "test-recordEvent", segmentation, 1, 45.9, 32.0, event.dow, event.hour, event.timestamp); - validateEvent(eventInMemory, "test-recordEvent", segmentation, 1, 45.9, 32.0, event.dow, event.hour, event.timestamp); - stop(); + validateEvent(event, eventKey, segmentation, 1, 45.9, 32.0, event.dow, event.hour, event.timestamp); + validateEvent(eventInMemory, eventKey, segmentation, 1, 45.9, 32.0, event.dow, event.hour, event.timestamp); } /** - * Records an event with for the next test case + * Recording an event with negative count + * "recordEvent" function should not create an event with given key and negative count + * in memory and cache queue should be empty */ @Test - public void recordEvent_fillEventQueue() { + public void recordEvent_negativeCount() { Config config = TestUtils.getBaseConfig(); config.enableFeatures(Config.Feature.Events).setEventQueueSizeToSend(4); init(config); - Assert.assertEquals(0, moduleEvents.eventQueue.eqSize()); - //create segmentation - Map segmentation = new HashMap<>(); - segmentation.put("size", "xl"); - segmentation.put("height", 184); - segmentation.put("married", false); + List events = TestUtils.getCurrentEventQueue(moduleEvents.ctx.getContext(), moduleEvents.L); - //record event with key segmentation - Countly.instance().events().recordEvent("test-recordEvent-Filler", segmentation); + validateQueueSize(0, events); + + Countly.instance().events().recordEvent(TestUtils.randomUUID(), -1); + events = TestUtils.getCurrentEventQueue(moduleEvents.ctx.getContext(), moduleEvents.L); + + validateQueueSize(0, events); + } + + /** + * Recording an event with null key + * "recordEvent" function should not create an event with given key null key + * in memory and cache queue should be empty + */ + @Test + public void recordEvent_nullKey() { + Config config = TestUtils.getBaseConfig(); + config.enableFeatures(Config.Feature.Events).setEventQueueSizeToSend(4); + init(config); + + List events = TestUtils.getCurrentEventQueue(moduleEvents.ctx.getContext(), moduleEvents.L); + + validateQueueSize(0, events); + + Countly.instance().events().recordEvent(null); + events = TestUtils.getCurrentEventQueue(moduleEvents.ctx.getContext(), moduleEvents.L); + + validateQueueSize(0, events); + } + + /** + * Recording an event with empty key + * "recordEvent" function should not create an event with given key empty key + * in memory and cache queue should be empty + */ + @Test + public void recordEvent_emptyKey() { + Config config = TestUtils.getBaseConfig(); + config.enableFeatures(Config.Feature.Events).setEventQueueSizeToSend(4); + init(config); - //check if event was recorded correctly and size of event queue is equal to size of events in queue List events = TestUtils.getCurrentEventQueue(moduleEvents.ctx.getContext(), moduleEvents.L); - EventImpl q1 = events.get(0); - Assert.assertEquals(1, moduleEvents.eventQueue.eqSize()); - Assert.assertEquals(1, events.size()); - validateEvent(q1, "test-recordEvent-Filler", segmentation, 1, null, null, q1.dow, q1.hour, q1.timestamp); - stop(); + validateQueueSize(0, events); + + Countly.instance().events().recordEvent(""); + events = TestUtils.getCurrentEventQueue(moduleEvents.ctx.getContext(), moduleEvents.L); + + validateQueueSize(0, events); } /** - * This test case re-inits Countly and tries to read - * existing event queue from storage + * Recording an event with invalid segment data + * "recordEvent" function should create an event with given segment + * in memory and cache queue should contain it and invalid segment should not exist */ @Test - public void recordEvent_reInitCountly() { + public void recordEvent_invalidSegment() { Config config = TestUtils.getBaseConfig(); config.enableFeatures(Config.Feature.Events).setEventQueueSizeToSend(4); init(config); - moduleEvents.eventQueue.clear(); - Assert.assertEquals(0, moduleEvents.eventQueue.eqSize()); + List events = TestUtils.getCurrentEventQueue(moduleEvents.ctx.getContext(), moduleEvents.L); + validateQueueSize(0, events); //create segmentation Map segmentation = new HashMap<>(); segmentation.put("exam_name", "CENG 101"); - segmentation.put("score", 100); + segmentation.put("score", 67); segmentation.put("cheated", false); + segmentation.put("invalid", new HashMap<>()); + Map expectedSegmentation = new HashMap<>(); + expectedSegmentation.put("exam_name", "CENG 101"); + expectedSegmentation.put("score", 67); + expectedSegmentation.put("cheated", false); + + String eventKey = TestUtils.randomUUID(); //record event with key segmentation - Countly.instance().events().recordEvent("testInDiskEventQueue", segmentation); + Countly.instance().events().recordEvent(eventKey, segmentation); - //now purposely re-init Countly - stop(); + events = TestUtils.getCurrentEventQueue(moduleEvents.ctx.getContext(), moduleEvents.L); + validateQueueSize(1, events); + + //check if event was recorded correctly + EventImpl event = events.get(0); + EventImpl eventInMemory = moduleEvents.eventQueue.eventQueueMemoryCache.get(0); + validateEvent(event, eventKey, expectedSegmentation, 1, null, null, event.dow, event.hour, event.timestamp); + validateEvent(eventInMemory, eventKey, expectedSegmentation, 1, null, null, event.dow, event.hour, event.timestamp); + Assert.assertEquals(3, event.segmentation.size()); + Assert.assertEquals(3, eventInMemory.segmentation.size()); + Assert.assertNull(event.getSegment("invalid")); + Assert.assertNull(eventInMemory.getSegment("invalid")); } /** - * start event successfully created timed event and end it + * Start an event + * "startEvent" function should create a timed event + * in memory and cache queue should contain it */ @Test public void startEvent() { Config config = TestUtils.getBaseConfig(); config.enableFeatures(Config.Feature.Events).setEventQueueSizeToSend(4); init(config); - String eventName = "test-startEvent"; - - try { - startEvent(eventName); - } finally { - endEvent(eventName, null, 1, null); - } - stop(); + + List events; + validateSize(0, 0); + String eventName = TestUtils.randomUUID(); + + startEvent(eventName); + validateSize(0, 1); + + EventImpl timedEvent = moduleEvents.timedEvents.get(eventName); + validateEvent(timedEvent, eventName, null, 1, null, null, timedEvent.dow, timedEvent.hour, timedEvent.timestamp); + + endEvent(eventName, null, 1, null); + //duration testing is not possible because division is error-prone for small numbers like .212 and .211 + events = TestUtils.getCurrentEventQueue(moduleEvents.ctx.getContext(), moduleEvents.L); + validateSize(1, 0); + + validateEvent(events.get(0), eventName, null, 1, null, events.get(0).duration, events.get(0).dow, events.get(0).hour, events.get(0).timestamp); + validateEvent(moduleEvents.eventQueue.eventQueueMemoryCache.get(0), eventName, null, 1, null, events.get(0).duration, events.get(0).dow, events.get(0).hour, events.get(0).timestamp); } /** - * start event should not create with empty key + * Start an event + * "startEvent" function should not create a timed event with empty key + * in memory , timed events and cache queue should not contain it */ @Test public void startEvent_emptyKey() { Config config = TestUtils.getBaseConfig(); config.enableFeatures(Config.Feature.Events).setEventQueueSizeToSend(4); init(config); + + validateSize(0, 0); Assert.assertFalse(Countly.instance().events().startEvent("")); - stop(); + validateSize(0, 0); } /** - * start event should not create with null key + * Start an event + * "startEvent" function should not create a timed event with null key + * in memory , timed events and cache queue should not contain it */ @Test public void startEvent_nullKey() { Config config = TestUtils.getBaseConfig(); config.enableFeatures(Config.Feature.Events).setEventQueueSizeToSend(4); init(config); + + validateSize(0, 0); Assert.assertFalse(Countly.instance().events().startEvent(null)); - stop(); + validateSize(0, 0); } /** - * start event should not create with already started event + * Start an event with already started key + * "startEvent" function should not create a timed event with same key as already started + * in memory , timed events and cache queue not contain it */ @Test public void startEvent_alreadyStarted() { @@ -163,84 +240,84 @@ public void startEvent_alreadyStarted() { config.enableFeatures(Config.Feature.Events).setEventQueueSizeToSend(4); init(config); - String eventName = "test-startEvent_alreadyStarted"; + List events = null; + validateSize(0, 0); - try { - startEvent(eventName); - boolean result = Countly.instance().events().startEvent(eventName); - Assert.assertFalse(result); - } finally { - endEvent(eventName, null, 1, null); - } - stop(); - } + String eventName = TestUtils.randomUUID(); + startEvent(eventName); - /** - * end event successfully ended the timed event - */ - @Test - public void endEvent() { - Config config = TestUtils.getBaseConfig(); - config.enableFeatures(Config.Feature.Events).setEventQueueSizeToSend(4); - init(config); - String eventName = "test-endEvent"; + validateSize(0, 1); + + EventImpl timedEvent = moduleEvents.timedEvents.get(eventName); + validateEvent(timedEvent, eventName, null, 1, null, null, timedEvent.dow, timedEvent.hour, timedEvent.timestamp); + + boolean result = Countly.instance().events().startEvent(eventName); + Assert.assertFalse(result); + + events = TestUtils.getCurrentEventQueue(moduleEvents.ctx.getContext(), moduleEvents.L); + validateQueueSize(0, events); + Assert.assertEquals(1, moduleEvents.timedEvents.size()); - startEvent(eventName); // start event to end it endEvent(eventName, null, 1, null); - stop(); + events = TestUtils.getCurrentEventQueue(moduleEvents.ctx.getContext(), moduleEvents.L); + + validateSize(1, 0); + validateEvent(events.get(0), eventName, null, 1, null, events.get(0).duration, events.get(0).dow, events.get(0).hour, events.get(0).timestamp); } /** - * End event with empty key should not net + * End an event with empty key + * "endEvent" function should not work with empty key + * in memory , timed events and cache queue not contain it */ @Test public void endEvent_emptyKey() { Config config = TestUtils.getBaseConfig(); config.enableFeatures(Config.Feature.Events).setEventQueueSizeToSend(4); init(config); + + validateSize(0, 0); Assert.assertFalse(Countly.instance().events().endEvent("")); - stop(); + validateSize(0, 0); } /** - * End event with null key should not end + * End an event with empty key + * "endEvent" function should not work with null key + * in memory , timed events and cache queue not contain it */ @Test public void endEvent_nullKey() { Config config = TestUtils.getBaseConfig(); config.enableFeatures(Config.Feature.Events).setEventQueueSizeToSend(4); init(config); + + validateSize(0, 0); Assert.assertFalse(Countly.instance().events().endEvent(null)); - stop(); + validateSize(0, 0); } /** - * End event with not started event should not work + * End a not existing event + * "endEvent" function should not work with not existing event key + * in memory , timed events and cache queue not contain it */ @Test - public void endEvent_notStarted() { + public void endEvent_notExist() { Config config = TestUtils.getBaseConfig(); config.enableFeatures(Config.Feature.Events).setEventQueueSizeToSend(4); init(config); - Assert.assertFalse(Countly.instance().events().endEvent("test-endEvent_notStarted")); - stop(); - } - /** - * End event with already ended event should not process - */ - @Test - public void endEvent_alreadyEnded() { - Config config = TestUtils.getBaseConfig(); - config.enableFeatures(Config.Feature.Events).setEventQueueSizeToSend(4); - init(config); - Assert.assertFalse(Countly.instance().events().endEvent("test-endEvent_alreadyEnded")); - stop(); + validateSize(0, 0); + Assert.assertFalse(Countly.instance().events().endEvent(TestUtils.randomUUID())); + validateSize(0, 0); } /** - * End event with segmentation should end successfully + * End an event with segmentation + * "endEvent" function should end an event with segmentation + * in memory , timed events and cache queue should contain it */ @Test public void endEvent_withSegmentation() { @@ -248,9 +325,14 @@ public void endEvent_withSegmentation() { config.enableFeatures(Config.Feature.Events).setEventQueueSizeToSend(4); init(config); - String eventName = "test-endEvent_withSegmentation"; + validateSize(0, 0); + String eventName = TestUtils.randomUUID(); startEvent(eventName); // start event to end it + validateSize(0, 1); + + EventImpl timedEvent = moduleEvents.timedEvents.get(eventName); + validateEvent(timedEvent, eventName, null, 1, null, null, timedEvent.dow, timedEvent.hour, timedEvent.timestamp); Map segmentation = new HashMap<>(); segmentation.put("hair_color", "red"); @@ -258,11 +340,19 @@ public void endEvent_withSegmentation() { segmentation.put("chauffeur", "g3chauffeur"); // endEvent(eventName, segmentation, 1, 5.0); - stop(); + validateSize(1, 0); + List events = TestUtils.getCurrentEventQueue(moduleEvents.ctx.getContext(), moduleEvents.L); + EventImpl imEvent = moduleEvents.eventQueue.eventQueueMemoryCache.get(0); + + validateEvent(imEvent, eventName, segmentation, 1, 5.0, events.get(0).duration, imEvent.dow, imEvent.hour, imEvent.timestamp); + validateEvent(events.get(0), eventName, segmentation, 1, 5.0, events.get(0).duration, events.get(0).dow, events.get(0).hour, events.get(0).timestamp); } /** - * End event with segmentation and negative count should throw IllegalArgumentException + * End an event with segmentation and negative count + * "endEvent" function should not end an event with negative count + * in memory and cache queue should not contain it, timed events should + * and data should not be set */ @Test(expected = IllegalArgumentException.class) public void endEvent_withSegmentation_negativeCount() { @@ -270,9 +360,13 @@ public void endEvent_withSegmentation_negativeCount() { config.enableFeatures(Config.Feature.Events).setEventQueueSizeToSend(4); init(config); - String eventName = "test-endEvent_withSegmentation_negativeCount"; + validateSize(0, 0); + String eventName = TestUtils.randomUUID(); startEvent(eventName); // start event to end it + validateSize(0, 1); + EventImpl timedEvent = moduleEvents.timedEvents.get(eventName); + validateEvent(timedEvent, eventName, null, 1, null, null, timedEvent.dow, timedEvent.hour, timedEvent.timestamp); Map segmentation = new HashMap<>(); segmentation.put("horse_name", "Alice"); @@ -280,7 +374,14 @@ public void endEvent_withSegmentation_negativeCount() { segmentation.put("currency", "Dollar"); // endEvent(eventName, segmentation, -7, 67.0); - stop(); + validateSize(0, 1); + timedEvent = moduleEvents.timedEvents.get(eventName); + validateEvent(timedEvent, eventName, null, 1, null, null, timedEvent.dow, timedEvent.hour, timedEvent.timestamp); + } + + private void validateSize(int expectedQueueSize, int expectedTimedEventSize) { + validateQueueSize(expectedQueueSize, TestUtils.getCurrentEventQueue(moduleEvents.ctx.getContext(), moduleEvents.L)); + Assert.assertEquals(expectedTimedEventSize, moduleEvents.timedEvents.size()); } private void validateQueueSize(int expectedSize, List events) { diff --git a/sdk-java/src/test/java/ly/count/sdk/java/internal/TestUtils.java b/sdk-java/src/test/java/ly/count/sdk/java/internal/TestUtils.java index d66544c5f..91ee94f0c 100644 --- a/sdk-java/src/test/java/ly/count/sdk/java/internal/TestUtils.java +++ b/sdk-java/src/test/java/ly/count/sdk/java/internal/TestUtils.java @@ -170,4 +170,13 @@ private static Map parseRequestParams(File file) throws IOExcept return paramMap; } } + + /** + * Returns random UUID for test ids + * + * @return random UUID starting with test- + */ + protected static String randomUUID() { + return "test-" + java.util.UUID.randomUUID(); + } } \ No newline at end of file From da0657e8e4fa48a064798819f3bc3f03f0725364 Mon Sep 17 00:00:00 2001 From: arifBurakDemiray Date: Tue, 26 Sep 2023 16:24:16 +0300 Subject: [PATCH 70/82] feat: add halt to sdk --- CHANGELOG.md | 1 + .../main/java/ly/count/sdk/java/Countly.java | 17 +++++++++-- .../ly/count/sdk/java/internal/SDKCore.java | 28 ++++++++++++++++--- .../sdk/java/internal/ModuleEventsTests.java | 2 +- 4 files changed, 41 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1c90def4a..c5106ea3b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ * In Countly class, the old "init(directory,config)" method is deprecated, use "init(config)" instead via "instance()" call. * Deprecated "Countly::event" call, deprecated builder pattern. Use "Countly::events" instead. * Deprecated "Usage::event" call, deprecated builder pattern. Use "Countly::events" instead. +* Deprecated "Countly::stop(boolean)" call, use "Countly::halt" instead via "instance()" call. * The following methods are deprecated from the "Event" interface: * "record" diff --git a/sdk-java/src/main/java/ly/count/sdk/java/Countly.java b/sdk-java/src/main/java/ly/count/sdk/java/Countly.java index f019052c4..0e4d249f5 100644 --- a/sdk-java/src/main/java/ly/count/sdk/java/Countly.java +++ b/sdk-java/src/main/java/ly/count/sdk/java/Countly.java @@ -2,8 +2,13 @@ import java.io.File; import java.util.Map; - -import ly.count.sdk.java.internal.*; +import ly.count.sdk.java.internal.CtxCore; +import ly.count.sdk.java.internal.Device; +import ly.count.sdk.java.internal.InternalConfig; +import ly.count.sdk.java.internal.Log; +import ly.count.sdk.java.internal.ModuleBackendMode; +import ly.count.sdk.java.internal.ModuleEvents; +import ly.count.sdk.java.internal.SDKCore; /** * Main Countly SDK API class. @@ -128,6 +133,7 @@ public static void init(final File sdkStorageRootDirectory, final Config config) * Also clears all the data if called with {@code clearData = true}. * * @param clearData whether to clear all Countly data or not + * @deprecated use {@link #halt()} instead via instance() call */ public static void stop(boolean clearData) { if (isInitialized()) { @@ -144,6 +150,13 @@ public static void stop(boolean clearData) { } } + /** + * Stop Countly SDK. Stops all tasks and releases resources. + */ + public void halt() { + stop(true); + } + /** * Returns whether Countly SDK has been already initialized or not. * diff --git a/sdk-java/src/main/java/ly/count/sdk/java/internal/SDKCore.java b/sdk-java/src/main/java/ly/count/sdk/java/internal/SDKCore.java index 4db4e4006..7ccfab886 100644 --- a/sdk-java/src/main/java/ly/count/sdk/java/internal/SDKCore.java +++ b/sdk-java/src/main/java/ly/count/sdk/java/internal/SDKCore.java @@ -1,10 +1,14 @@ package ly.count.sdk.java.internal; -import ly.count.sdk.java.Config; -import org.json.JSONObject; - -import java.util.*; +import java.util.ArrayDeque; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Queue; +import java.util.TreeMap; import java.util.concurrent.Future; +import ly.count.sdk.java.Config; public class SDKCore { @@ -93,6 +97,13 @@ public void init(CtxCore ctx) { prepareMappings(ctx); } + /** + * Stop sdk core + * + * @param ctx {@link CtxCore} object + * @param clear if true, clear all data + * @deprecated use {@link #halt(CtxCore)} instead + */ public void stop(final CtxCore ctx, final boolean clear) { if (instance == null) { return; @@ -121,6 +132,15 @@ public void stop(final CtxCore ctx, final boolean clear) { sdkStorage.stop(ctx, clear);//from original super class } + /** + * Stop sdk core + * + * @param ctxCore {@link CtxCore} object + */ + public void halt(CtxCore ctxCore) { + stop(ctxCore, true); + } + private boolean addingConsent(int adding, CoreFeature feature) { return (consents & feature.getIndex()) == 0 && (adding & feature.getIndex()) > 0; } diff --git a/sdk-java/src/test/java/ly/count/sdk/java/internal/ModuleEventsTests.java b/sdk-java/src/test/java/ly/count/sdk/java/internal/ModuleEventsTests.java index 018f74bd7..37d9f049c 100644 --- a/sdk-java/src/test/java/ly/count/sdk/java/internal/ModuleEventsTests.java +++ b/sdk-java/src/test/java/ly/count/sdk/java/internal/ModuleEventsTests.java @@ -23,7 +23,7 @@ private void init(Config cc) { @After public void stop() { - Countly.stop(true); + Countly.instance().halt(); } /** From 17887f7518f6fbba3cc4719cf1432199a95767d7 Mon Sep 17 00:00:00 2001 From: ArtursK Date: Wed, 27 Sep 2023 13:36:43 +0300 Subject: [PATCH 71/82] Added a validation function for events in tests --- .../sdk/java/internal/ModuleEventsTests.java | 111 +++++++++--------- .../ly/count/sdk/java/internal/TestUtils.java | 4 +- .../count/sdk/java/internal/UtilsTests.java | 2 +- 3 files changed, 58 insertions(+), 59 deletions(-) diff --git a/sdk-java/src/test/java/ly/count/sdk/java/internal/ModuleEventsTests.java b/sdk-java/src/test/java/ly/count/sdk/java/internal/ModuleEventsTests.java index 37d9f049c..74b6cf0bb 100644 --- a/sdk-java/src/test/java/ly/count/sdk/java/internal/ModuleEventsTests.java +++ b/sdk-java/src/test/java/ly/count/sdk/java/internal/ModuleEventsTests.java @@ -1,5 +1,6 @@ package ly.count.sdk.java.internal; +import java.io.File; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -52,14 +53,19 @@ public void recordEvent() { Countly.instance().events().recordEvent(eventKey, 1, 45.9, segmentation, 32.0); //check if event was recorded correctly and size of event queue is equal to size of events in queue - events = TestUtils.getCurrentEventQueue(moduleEvents.ctx.getContext(), moduleEvents.L); - validateQueueSize(1, events); + validateEventInQueue(TestUtils.getTestSDirectory(), eventKey, segmentation, 1, 45.9, 32.0, 1, 0, moduleEvents.L); + } + + void validateEventInQueue(File targetFolder, String key, Map segmentation, + int count, Double sum, Double duration, int queueSize, int elementInQueue, Log L) { + List events = TestUtils.getCurrentEventQueue(targetFolder, moduleEvents.L); + validateQueueSize(queueSize, events); //check if event was recorded correctly - EventImpl event = events.get(0); + EventImpl event = events.get(elementInQueue); EventImpl eventInMemory = moduleEvents.eventQueue.eventQueueMemoryCache.get(0); - validateEvent(event, eventKey, segmentation, 1, 45.9, 32.0, event.dow, event.hour, event.timestamp); - validateEvent(eventInMemory, eventKey, segmentation, 1, 45.9, 32.0, event.dow, event.hour, event.timestamp); + validateEvent(event, key, segmentation, count, sum, duration); + validateEvent(eventInMemory, key, segmentation, count, sum, duration); } /** @@ -153,18 +159,7 @@ public void recordEvent_invalidSegment() { //record event with key segmentation Countly.instance().events().recordEvent(eventKey, segmentation); - events = TestUtils.getCurrentEventQueue(moduleEvents.ctx.getContext(), moduleEvents.L); - validateQueueSize(1, events); - - //check if event was recorded correctly - EventImpl event = events.get(0); - EventImpl eventInMemory = moduleEvents.eventQueue.eventQueueMemoryCache.get(0); - validateEvent(event, eventKey, expectedSegmentation, 1, null, null, event.dow, event.hour, event.timestamp); - validateEvent(eventInMemory, eventKey, expectedSegmentation, 1, null, null, event.dow, event.hour, event.timestamp); - Assert.assertEquals(3, event.segmentation.size()); - Assert.assertEquals(3, eventInMemory.segmentation.size()); - Assert.assertNull(event.getSegment("invalid")); - Assert.assertNull(eventInMemory.getSegment("invalid")); + validateEventInQueue(TestUtils.getTestSDirectory(), eventKey, expectedSegmentation, 1, null, null, 1, 0, moduleEvents.L); } /** @@ -179,22 +174,22 @@ public void startEvent() { init(config); List events; - validateSize(0, 0); + validateTimedEventSize(0, 0); String eventName = TestUtils.randomUUID(); startEvent(eventName); - validateSize(0, 1); + validateTimedEventSize(0, 1); EventImpl timedEvent = moduleEvents.timedEvents.get(eventName); - validateEvent(timedEvent, eventName, null, 1, null, null, timedEvent.dow, timedEvent.hour, timedEvent.timestamp); + validateEvent(timedEvent, eventName, null, 1, null, null); endEvent(eventName, null, 1, null); //duration testing is not possible because division is error-prone for small numbers like .212 and .211 events = TestUtils.getCurrentEventQueue(moduleEvents.ctx.getContext(), moduleEvents.L); - validateSize(1, 0); + validateTimedEventSize(1, 0); - validateEvent(events.get(0), eventName, null, 1, null, events.get(0).duration, events.get(0).dow, events.get(0).hour, events.get(0).timestamp); - validateEvent(moduleEvents.eventQueue.eventQueueMemoryCache.get(0), eventName, null, 1, null, events.get(0).duration, events.get(0).dow, events.get(0).hour, events.get(0).timestamp); + validateEvent(events.get(0), eventName, null, 1, null, events.get(0).duration); + validateEvent(moduleEvents.eventQueue.eventQueueMemoryCache.get(0), eventName, null, 1, null, events.get(0).duration); } /** @@ -208,9 +203,9 @@ public void startEvent_emptyKey() { config.enableFeatures(Config.Feature.Events).setEventQueueSizeToSend(4); init(config); - validateSize(0, 0); + validateTimedEventSize(0, 0); Assert.assertFalse(Countly.instance().events().startEvent("")); - validateSize(0, 0); + validateTimedEventSize(0, 0); } /** @@ -224,9 +219,9 @@ public void startEvent_nullKey() { config.enableFeatures(Config.Feature.Events).setEventQueueSizeToSend(4); init(config); - validateSize(0, 0); + validateTimedEventSize(0, 0); Assert.assertFalse(Countly.instance().events().startEvent(null)); - validateSize(0, 0); + validateTimedEventSize(0, 0); } /** @@ -241,15 +236,15 @@ public void startEvent_alreadyStarted() { init(config); List events = null; - validateSize(0, 0); + validateTimedEventSize(0, 0); String eventName = TestUtils.randomUUID(); startEvent(eventName); - validateSize(0, 1); + validateTimedEventSize(0, 1); EventImpl timedEvent = moduleEvents.timedEvents.get(eventName); - validateEvent(timedEvent, eventName, null, 1, null, null, timedEvent.dow, timedEvent.hour, timedEvent.timestamp); + validateEvent(timedEvent, eventName, null, 1, null, null); boolean result = Countly.instance().events().startEvent(eventName); Assert.assertFalse(result); @@ -262,8 +257,8 @@ public void startEvent_alreadyStarted() { events = TestUtils.getCurrentEventQueue(moduleEvents.ctx.getContext(), moduleEvents.L); - validateSize(1, 0); - validateEvent(events.get(0), eventName, null, 1, null, events.get(0).duration, events.get(0).dow, events.get(0).hour, events.get(0).timestamp); + validateTimedEventSize(1, 0); + validateEvent(events.get(0), eventName, null, 1, null, events.get(0).duration); } /** @@ -277,9 +272,9 @@ public void endEvent_emptyKey() { config.enableFeatures(Config.Feature.Events).setEventQueueSizeToSend(4); init(config); - validateSize(0, 0); + validateTimedEventSize(0, 0); Assert.assertFalse(Countly.instance().events().endEvent("")); - validateSize(0, 0); + validateTimedEventSize(0, 0); } /** @@ -293,9 +288,9 @@ public void endEvent_nullKey() { config.enableFeatures(Config.Feature.Events).setEventQueueSizeToSend(4); init(config); - validateSize(0, 0); + validateTimedEventSize(0, 0); Assert.assertFalse(Countly.instance().events().endEvent(null)); - validateSize(0, 0); + validateTimedEventSize(0, 0); } /** @@ -309,9 +304,9 @@ public void endEvent_notExist() { config.enableFeatures(Config.Feature.Events).setEventQueueSizeToSend(4); init(config); - validateSize(0, 0); + validateTimedEventSize(0, 0); Assert.assertFalse(Countly.instance().events().endEvent(TestUtils.randomUUID())); - validateSize(0, 0); + validateTimedEventSize(0, 0); } /** @@ -325,14 +320,14 @@ public void endEvent_withSegmentation() { config.enableFeatures(Config.Feature.Events).setEventQueueSizeToSend(4); init(config); - validateSize(0, 0); + validateTimedEventSize(0, 0); String eventName = TestUtils.randomUUID(); startEvent(eventName); // start event to end it - validateSize(0, 1); + validateTimedEventSize(0, 1); EventImpl timedEvent = moduleEvents.timedEvents.get(eventName); - validateEvent(timedEvent, eventName, null, 1, null, null, timedEvent.dow, timedEvent.hour, timedEvent.timestamp); + validateEvent(timedEvent, eventName, null, 1, null, null); Map segmentation = new HashMap<>(); segmentation.put("hair_color", "red"); @@ -340,12 +335,12 @@ public void endEvent_withSegmentation() { segmentation.put("chauffeur", "g3chauffeur"); // endEvent(eventName, segmentation, 1, 5.0); - validateSize(1, 0); + validateTimedEventSize(1, 0); List events = TestUtils.getCurrentEventQueue(moduleEvents.ctx.getContext(), moduleEvents.L); EventImpl imEvent = moduleEvents.eventQueue.eventQueueMemoryCache.get(0); - validateEvent(imEvent, eventName, segmentation, 1, 5.0, events.get(0).duration, imEvent.dow, imEvent.hour, imEvent.timestamp); - validateEvent(events.get(0), eventName, segmentation, 1, 5.0, events.get(0).duration, events.get(0).dow, events.get(0).hour, events.get(0).timestamp); + validateEvent(imEvent, eventName, segmentation, 1, 5.0, events.get(0).duration); + validateEvent(events.get(0), eventName, segmentation, 1, 5.0, events.get(0).duration); } /** @@ -360,13 +355,13 @@ public void endEvent_withSegmentation_negativeCount() { config.enableFeatures(Config.Feature.Events).setEventQueueSizeToSend(4); init(config); - validateSize(0, 0); + validateTimedEventSize(0, 0); String eventName = TestUtils.randomUUID(); startEvent(eventName); // start event to end it - validateSize(0, 1); + validateTimedEventSize(0, 1); EventImpl timedEvent = moduleEvents.timedEvents.get(eventName); - validateEvent(timedEvent, eventName, null, 1, null, null, timedEvent.dow, timedEvent.hour, timedEvent.timestamp); + validateEvent(timedEvent, eventName, null, 1, null, null); Map segmentation = new HashMap<>(); segmentation.put("horse_name", "Alice"); @@ -374,12 +369,12 @@ public void endEvent_withSegmentation_negativeCount() { segmentation.put("currency", "Dollar"); // endEvent(eventName, segmentation, -7, 67.0); - validateSize(0, 1); + validateTimedEventSize(0, 1); timedEvent = moduleEvents.timedEvents.get(eventName); - validateEvent(timedEvent, eventName, null, 1, null, null, timedEvent.dow, timedEvent.hour, timedEvent.timestamp); + validateEvent(timedEvent, eventName, null, 1, null, null); } - private void validateSize(int expectedQueueSize, int expectedTimedEventSize) { + private void validateTimedEventSize(int expectedQueueSize, int expectedTimedEventSize) { validateQueueSize(expectedQueueSize, TestUtils.getCurrentEventQueue(moduleEvents.ctx.getContext(), moduleEvents.L)); Assert.assertEquals(expectedTimedEventSize, moduleEvents.timedEvents.size()); } @@ -389,16 +384,20 @@ private void validateQueueSize(int expectedSize, List events) { Assert.assertEquals(expectedSize, moduleEvents.eventQueue.eqSize()); } - private void validateEvent(EventImpl gonnaValidate, String key, Map segmentation, - int count, Double sum, Double duration, int dow, int hour, long timestamp) { + private void validateEvent(EventImpl gonnaValidate, String key, Map segmentation, int count, Double sum, Double duration) { Assert.assertEquals(key, gonnaValidate.key); Assert.assertEquals(segmentation, gonnaValidate.segmentation); Assert.assertEquals(count, gonnaValidate.count); Assert.assertEquals(sum, gonnaValidate.sum); - Assert.assertEquals(duration, gonnaValidate.duration); - Assert.assertEquals(dow, gonnaValidate.dow); - Assert.assertEquals(hour, gonnaValidate.hour); - Assert.assertEquals(timestamp, gonnaValidate.timestamp); + + if (duration != null) { + double acceptableDelta = 0.00001; + Assert.assertTrue(Math.abs(duration - gonnaValidate.duration) < acceptableDelta); + } + + Assert.assertTrue(gonnaValidate.dow >= 0 && gonnaValidate.dow < 7); + Assert.assertTrue(gonnaValidate.hour >= 0 && gonnaValidate.hour < 24); + Assert.assertTrue(gonnaValidate.timestamp >= 0); } private void endEvent(String key, Map segmentation, int count, Double sum) { diff --git a/sdk-java/src/test/java/ly/count/sdk/java/internal/TestUtils.java b/sdk-java/src/test/java/ly/count/sdk/java/internal/TestUtils.java index 91ee94f0c..f6b531e9a 100644 --- a/sdk-java/src/test/java/ly/count/sdk/java/internal/TestUtils.java +++ b/sdk-java/src/test/java/ly/count/sdk/java/internal/TestUtils.java @@ -25,12 +25,12 @@ private TestUtils() { } static Config getBaseConfig() { - File sdkStorageRootDirectory = getSdkStorageRootDirectory(); + File sdkStorageRootDirectory = getTestSDirectory(); checkSdkStorageRootDirectoryExist(sdkStorageRootDirectory); return new Config(SERVER_URL, SERVER_APP_KEY, sdkStorageRootDirectory); } - static File getSdkStorageRootDirectory() { + public static File getTestSDirectory() { // System specific folder structure String[] sdkStorageRootPath = { System.getProperty("user.home"), "__COUNTLY", "java_test" }; return new File(String.join(File.separator, sdkStorageRootPath)); diff --git a/sdk-java/src/test/java/ly/count/sdk/java/internal/UtilsTests.java b/sdk-java/src/test/java/ly/count/sdk/java/internal/UtilsTests.java index 717bf2e31..73d1d7126 100644 --- a/sdk-java/src/test/java/ly/count/sdk/java/internal/UtilsTests.java +++ b/sdk-java/src/test/java/ly/count/sdk/java/internal/UtilsTests.java @@ -295,7 +295,7 @@ public void readFileContent_fileNotReadable() throws IOException { file.setReadable(false); String content = Utils.readFileContent(file, logger); - Assert.assertEquals("", content); + Assert.assertEquals("", content);//todo this might fail on windows. Potentiall add a platform check for this test } finally { File file = new File(TEST_FILE_NAME); if (file.exists()) { From 75b687a2429cf625042f51e18645f3e27976ab38 Mon Sep 17 00:00:00 2001 From: arifBurakDemiray Date: Wed, 27 Sep 2023 17:27:44 +0300 Subject: [PATCH 72/82] fix: test utils --- .../sdk/java/internal/ModuleEventsTests.java | 16 ++++++++-------- .../ly/count/sdk/java/internal/TestUtils.java | 15 ++++----------- 2 files changed, 12 insertions(+), 19 deletions(-) diff --git a/sdk-java/src/test/java/ly/count/sdk/java/internal/ModuleEventsTests.java b/sdk-java/src/test/java/ly/count/sdk/java/internal/ModuleEventsTests.java index 74b6cf0bb..67e975470 100644 --- a/sdk-java/src/test/java/ly/count/sdk/java/internal/ModuleEventsTests.java +++ b/sdk-java/src/test/java/ly/count/sdk/java/internal/ModuleEventsTests.java @@ -47,7 +47,7 @@ public void recordEvent() { segmentation.put("weight", 67); segmentation.put("bald", true); - String eventKey = TestUtils.randomUUID(); + String eventKey = "recordEvent"; //record event with key segmentation and count Countly.instance().events().recordEvent(eventKey, 1, 45.9, segmentation, 32.0); @@ -83,7 +83,7 @@ public void recordEvent_negativeCount() { validateQueueSize(0, events); - Countly.instance().events().recordEvent(TestUtils.randomUUID(), -1); + Countly.instance().events().recordEvent("recordEvent_negativeCount", -1); events = TestUtils.getCurrentEventQueue(moduleEvents.ctx.getContext(), moduleEvents.L); validateQueueSize(0, events); @@ -155,7 +155,7 @@ public void recordEvent_invalidSegment() { expectedSegmentation.put("score", 67); expectedSegmentation.put("cheated", false); - String eventKey = TestUtils.randomUUID(); + String eventKey = "recordEvent_invalidSegment"; //record event with key segmentation Countly.instance().events().recordEvent(eventKey, segmentation); @@ -175,7 +175,7 @@ public void startEvent() { List events; validateTimedEventSize(0, 0); - String eventName = TestUtils.randomUUID(); + String eventName = "startEvent"; startEvent(eventName); validateTimedEventSize(0, 1); @@ -238,7 +238,7 @@ public void startEvent_alreadyStarted() { List events = null; validateTimedEventSize(0, 0); - String eventName = TestUtils.randomUUID(); + String eventName = "startEvent_alreadyStarted"; startEvent(eventName); validateTimedEventSize(0, 1); @@ -305,7 +305,7 @@ public void endEvent_notExist() { init(config); validateTimedEventSize(0, 0); - Assert.assertFalse(Countly.instance().events().endEvent(TestUtils.randomUUID())); + Assert.assertFalse(Countly.instance().events().endEvent("endEvent_notExist")); validateTimedEventSize(0, 0); } @@ -321,7 +321,7 @@ public void endEvent_withSegmentation() { init(config); validateTimedEventSize(0, 0); - String eventName = TestUtils.randomUUID(); + String eventName = "endEvent_withSegmentation"; startEvent(eventName); // start event to end it validateTimedEventSize(0, 1); @@ -356,7 +356,7 @@ public void endEvent_withSegmentation_negativeCount() { init(config); validateTimedEventSize(0, 0); - String eventName = TestUtils.randomUUID(); + String eventName = "endEvent_withSegmentation_negativeCount"; startEvent(eventName); // start event to end it validateTimedEventSize(0, 1); diff --git a/sdk-java/src/test/java/ly/count/sdk/java/internal/TestUtils.java b/sdk-java/src/test/java/ly/count/sdk/java/internal/TestUtils.java index 4a20b4e8e..b052b1027 100644 --- a/sdk-java/src/test/java/ly/count/sdk/java/internal/TestUtils.java +++ b/sdk-java/src/test/java/ly/count/sdk/java/internal/TestUtils.java @@ -17,8 +17,6 @@ import static ly.count.sdk.java.internal.SDKStorage.FILE_NAME_SEPARATOR; public class TestUtils { - - static String DELIMETER = ":::"; static String SERVER_URL = "https://test.count.ly"; static String SERVER_APP_KEY = "COUNTLY_APP_KEY"; static String DEVICE_ID = "some_random_test_device_id"; @@ -29,7 +27,10 @@ private TestUtils() { static Config getBaseConfig() { File sdkStorageRootDirectory = getTestSDirectory(); checkSdkStorageRootDirectoryExist(sdkStorageRootDirectory); - return new Config(SERVER_URL, SERVER_APP_KEY, sdkStorageRootDirectory); + Config config = new Config(SERVER_URL, SERVER_APP_KEY, sdkStorageRootDirectory); + config.setCustomDeviceId(DEVICE_ID); + + return config; } public static File getTestSDirectory() { @@ -180,12 +181,4 @@ static File getSdkStorageRootDirectory() { String[] sdkStorageRootPath = { System.getProperty("user.home"), "__COUNTLY", "java_test" }; return new File(String.join(File.separator, sdkStorageRootPath)); } - - static void checkSdkStorageRootDirectoryExist(File directory) { - if (!(directory.exists() && directory.isDirectory())) { - if (!directory.mkdirs()) { - throw new RuntimeException("Directory creation failed"); - } - } - } } \ No newline at end of file From a96e839beb55ee8803f4eacf2602583b7329e5b9 Mon Sep 17 00:00:00 2001 From: arifBurakDemiray Date: Wed, 27 Sep 2023 17:31:39 +0300 Subject: [PATCH 73/82] feat: rename event impl queeu to event queue --- .../{EventImplQueue.java => EventQueue.java} | 14 +++++++------- .../ly/count/sdk/java/internal/ModuleEvents.java | 4 ++-- .../ly/count/sdk/java/internal/TestUtils.java | 16 ++++------------ 3 files changed, 13 insertions(+), 21 deletions(-) rename sdk-java/src/main/java/ly/count/sdk/java/internal/{EventImplQueue.java => EventQueue.java} (84%) diff --git a/sdk-java/src/main/java/ly/count/sdk/java/internal/EventImplQueue.java b/sdk-java/src/main/java/ly/count/sdk/java/internal/EventQueue.java similarity index 84% rename from sdk-java/src/main/java/ly/count/sdk/java/internal/EventImplQueue.java rename to sdk-java/src/main/java/ly/count/sdk/java/internal/EventQueue.java index 45e74aa5b..654cf0cc3 100644 --- a/sdk-java/src/main/java/ly/count/sdk/java/internal/EventImplQueue.java +++ b/sdk-java/src/main/java/ly/count/sdk/java/internal/EventQueue.java @@ -4,14 +4,14 @@ import java.util.Collection; import java.util.List; -public class EventImplQueue { +public class EventQueue { static final String DELIMITER = ":::"; private final Log L; private final InternalConfig config; final List eventQueueMemoryCache; - protected EventImplQueue(Log logger, InternalConfig config) { + protected EventQueue(Log logger, InternalConfig config) { L = logger; this.config = config; eventQueueMemoryCache = new ArrayList<>(config.getEventsBufferSize()); @@ -25,7 +25,7 @@ protected int eqSize() { } void addEvent(final EventImpl event) { - L.d("[EventImplQueue] Adding event: " + event.key); + L.d("[EventQueue] Adding event: " + event.key); if (eventQueueMemoryCache.size() < config.getEventsBufferSize()) { eventQueueMemoryCache.add(event); writeEventQueueToStorage(); @@ -38,7 +38,7 @@ void addEvent(final EventImpl event) { void writeEventQueueToStorage() { final String eventQueue = joinEvents(eventQueueMemoryCache); - L.d("[EventImplQueue] Setting event data: " + eventQueue); + L.d("[EventQueue] Setting event data: " + eventQueue); SDKCore.instance.sdkStorage.storeEventQueue(eventQueue); } @@ -46,7 +46,7 @@ void writeEventQueueToStorage() { * Restores events from disk */ void restore() { - L.d("[EventImplQueue] Restoring events from disk"); + L.d("[EventQueue] Restoring events from disk"); eventQueueMemoryCache.clear(); final String[] array = getEvents(); @@ -67,14 +67,14 @@ private String joinEvents(final Collection collection) { for (EventImpl e : collection) { strings.add(e.toJSON(L)); } - return Utils.join(strings, EventImplQueue.DELIMITER); + return Utils.join(strings, EventQueue.DELIMITER); } /** * Returns an unsorted array of the current stored event JSON strings. */ private synchronized String[] getEvents() { - L.d("[EventImplQueue] Getting events from disk"); + L.d("[EventQueue] Getting events from disk"); final String joinedEventsStr = SDKCore.instance.sdkStorage.readEventQueue(); return joinedEventsStr.isEmpty() ? new String[0] : joinedEventsStr.split(DELIMITER); } diff --git a/sdk-java/src/main/java/ly/count/sdk/java/internal/ModuleEvents.java b/sdk-java/src/main/java/ly/count/sdk/java/internal/ModuleEvents.java index 36c024faf..28c4275f0 100644 --- a/sdk-java/src/main/java/ly/count/sdk/java/internal/ModuleEvents.java +++ b/sdk-java/src/main/java/ly/count/sdk/java/internal/ModuleEvents.java @@ -13,7 +13,7 @@ public class ModuleEvents extends ModuleBase { protected CtxCore ctx = null; - protected EventImplQueue eventQueue = null; + protected EventQueue eventQueue = null; final Map timedEvents = new HashMap<>(); private ScheduledExecutorService executor = null; protected Events eventsInterface = null; @@ -22,7 +22,7 @@ public class ModuleEvents extends ModuleBase { public void init(InternalConfig config, Log logger) { super.init(config, logger); L.d("[ModuleEvents] init: config = " + config); - eventQueue = new EventImplQueue(L, config); + eventQueue = new EventQueue(L, config); eventQueue.restore(); eventsInterface = new Events(); } diff --git a/sdk-java/src/test/java/ly/count/sdk/java/internal/TestUtils.java b/sdk-java/src/test/java/ly/count/sdk/java/internal/TestUtils.java index b052b1027..ec041d41b 100644 --- a/sdk-java/src/test/java/ly/count/sdk/java/internal/TestUtils.java +++ b/sdk-java/src/test/java/ly/count/sdk/java/internal/TestUtils.java @@ -1,20 +1,12 @@ package ly.count.sdk.java.internal; +import ly.count.sdk.java.Config; import java.io.File; import java.io.IOException; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Comparator; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Scanner; +import java.util.*; import java.util.stream.Stream; -import ly.count.sdk.java.Config; -import static ly.count.sdk.java.internal.SDKStorage.EVENT_QUEUE_FILE_NAME; -import static ly.count.sdk.java.internal.SDKStorage.FILE_NAME_PREFIX; -import static ly.count.sdk.java.internal.SDKStorage.FILE_NAME_SEPARATOR; +import static ly.count.sdk.java.internal.SDKStorage.*; public class TestUtils { static String SERVER_URL = "https://test.count.ly"; @@ -105,7 +97,7 @@ protected static List getCurrentEventQueue(File targetFolder, Log log //do nothing } - Arrays.stream(fileContent.split(EventImplQueue.DELIMITER)).forEach(s -> { + Arrays.stream(fileContent.split(EventQueue.DELIMITER)).forEach(s -> { final EventImpl event = EventImpl.fromJSON(s, (ev) -> { }, logger); if (event != null) { From 71bc4c21d9f36d6c6d7dd8710d564b9bf800eda1 Mon Sep 17 00:00:00 2001 From: arifBurakDemiray Date: Wed, 27 Sep 2023 17:36:23 +0300 Subject: [PATCH 74/82] feat: add force sending --- .../java/ly/count/sdk/java/internal/EventQueue.java | 8 +++----- .../java/ly/count/sdk/java/internal/ModuleEvents.java | 10 +++++++--- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/sdk-java/src/main/java/ly/count/sdk/java/internal/EventQueue.java b/sdk-java/src/main/java/ly/count/sdk/java/internal/EventQueue.java index 654cf0cc3..5eae9d876 100644 --- a/sdk-java/src/main/java/ly/count/sdk/java/internal/EventQueue.java +++ b/sdk-java/src/main/java/ly/count/sdk/java/internal/EventQueue.java @@ -26,10 +26,8 @@ protected int eqSize() { void addEvent(final EventImpl event) { L.d("[EventQueue] Adding event: " + event.key); - if (eventQueueMemoryCache.size() < config.getEventsBufferSize()) { - eventQueueMemoryCache.add(event); - writeEventQueueToStorage(); - } + eventQueueMemoryCache.add(event); + writeEventQueueToStorage(); } /** @@ -45,7 +43,7 @@ void writeEventQueueToStorage() { /** * Restores events from disk */ - void restore() { + void restoreFromDisk() { L.d("[EventQueue] Restoring events from disk"); eventQueueMemoryCache.clear(); diff --git a/sdk-java/src/main/java/ly/count/sdk/java/internal/ModuleEvents.java b/sdk-java/src/main/java/ly/count/sdk/java/internal/ModuleEvents.java index 28c4275f0..2aba19a2c 100644 --- a/sdk-java/src/main/java/ly/count/sdk/java/internal/ModuleEvents.java +++ b/sdk-java/src/main/java/ly/count/sdk/java/internal/ModuleEvents.java @@ -23,7 +23,7 @@ public void init(InternalConfig config, Log logger) { super.init(config, logger); L.d("[ModuleEvents] init: config = " + config); eventQueue = new EventQueue(L, config); - eventQueue.restore(); + eventQueue.restoreFromDisk(); eventsInterface = new Events(); } @@ -104,8 +104,12 @@ protected void recordEventInternal(String key, int count, Double sum, Map= internalConfig.getEventsBufferSize()) { - L.d("[ModuleEvents] addEventToQueue: eventQueue.size >= internalConfig.getEventsBufferSize()"); + checkEventQueueToSend(false); + } + + private void checkEventQueueToSend(boolean forceSend) { + if (forceSend || (eventQueue.eqSize() >= internalConfig.getEventsBufferSize())) { + L.d("[ModuleEvents] addEventToQueue: eventQueue.size >= internalConfig.getEventsBufferSize() || forceSend: " + forceSend); addEventsToRequestQ(); } } From 89d955adbf4618fd65fdd50559bb7bc9fc8a0201 Mon Sep 17 00:00:00 2001 From: arifBurakDemiray Date: Wed, 27 Sep 2023 17:57:23 +0300 Subject: [PATCH 75/82] refactor: use reduced way --- .../sdk/java/internal/ModuleEventsTests.java | 60 +++++++++---------- 1 file changed, 27 insertions(+), 33 deletions(-) diff --git a/sdk-java/src/test/java/ly/count/sdk/java/internal/ModuleEventsTests.java b/sdk-java/src/test/java/ly/count/sdk/java/internal/ModuleEventsTests.java index 67e975470..caad9e6f1 100644 --- a/sdk-java/src/test/java/ly/count/sdk/java/internal/ModuleEventsTests.java +++ b/sdk-java/src/test/java/ly/count/sdk/java/internal/ModuleEventsTests.java @@ -56,18 +56,6 @@ public void recordEvent() { validateEventInQueue(TestUtils.getTestSDirectory(), eventKey, segmentation, 1, 45.9, 32.0, 1, 0, moduleEvents.L); } - void validateEventInQueue(File targetFolder, String key, Map segmentation, - int count, Double sum, Double duration, int queueSize, int elementInQueue, Log L) { - List events = TestUtils.getCurrentEventQueue(targetFolder, moduleEvents.L); - validateQueueSize(queueSize, events); - - //check if event was recorded correctly - EventImpl event = events.get(elementInQueue); - EventImpl eventInMemory = moduleEvents.eventQueue.eventQueueMemoryCache.get(0); - validateEvent(event, key, segmentation, count, sum, duration); - validateEvent(eventInMemory, key, segmentation, count, sum, duration); - } - /** * Recording an event with negative count * "recordEvent" function should not create an event with given key and negative count @@ -173,23 +161,21 @@ public void startEvent() { config.enableFeatures(Config.Feature.Events).setEventQueueSizeToSend(4); init(config); - List events; validateTimedEventSize(0, 0); String eventName = "startEvent"; startEvent(eventName); + long start = System.currentTimeMillis(); validateTimedEventSize(0, 1); EventImpl timedEvent = moduleEvents.timedEvents.get(eventName); validateEvent(timedEvent, eventName, null, 1, null, null); endEvent(eventName, null, 1, null); + long end = System.currentTimeMillis(); //duration testing is not possible because division is error-prone for small numbers like .212 and .211 - events = TestUtils.getCurrentEventQueue(moduleEvents.ctx.getContext(), moduleEvents.L); - validateTimedEventSize(1, 0); - - validateEvent(events.get(0), eventName, null, 1, null, events.get(0).duration); - validateEvent(moduleEvents.eventQueue.eventQueueMemoryCache.get(0), eventName, null, 1, null, events.get(0).duration); + Assert.assertEquals(0, moduleEvents.timedEvents.size()); + validateEventInQueue(TestUtils.getTestSDirectory(), eventName, null, 1, null, (double) (end - start) / 1000, 1, 0, moduleEvents.L); } /** @@ -235,11 +221,11 @@ public void startEvent_alreadyStarted() { config.enableFeatures(Config.Feature.Events).setEventQueueSizeToSend(4); init(config); - List events = null; validateTimedEventSize(0, 0); String eventName = "startEvent_alreadyStarted"; startEvent(eventName); + long start = System.currentTimeMillis(); validateTimedEventSize(0, 1); @@ -249,16 +235,13 @@ public void startEvent_alreadyStarted() { boolean result = Countly.instance().events().startEvent(eventName); Assert.assertFalse(result); - events = TestUtils.getCurrentEventQueue(moduleEvents.ctx.getContext(), moduleEvents.L); - validateQueueSize(0, events); - Assert.assertEquals(1, moduleEvents.timedEvents.size()); + validateTimedEventSize(0, 1); endEvent(eventName, null, 1, null); + long end = System.currentTimeMillis(); - events = TestUtils.getCurrentEventQueue(moduleEvents.ctx.getContext(), moduleEvents.L); - - validateTimedEventSize(1, 0); - validateEvent(events.get(0), eventName, null, 1, null, events.get(0).duration); + Assert.assertEquals(0, moduleEvents.timedEvents.size()); + validateEventInQueue(TestUtils.getTestSDirectory(), eventName, null, 1, null, (double) (end - start) / 1000, 1, 0, moduleEvents.L); } /** @@ -324,6 +307,7 @@ public void endEvent_withSegmentation() { String eventName = "endEvent_withSegmentation"; startEvent(eventName); // start event to end it + long start = System.currentTimeMillis(); validateTimedEventSize(0, 1); EventImpl timedEvent = moduleEvents.timedEvents.get(eventName); @@ -335,12 +319,10 @@ public void endEvent_withSegmentation() { segmentation.put("chauffeur", "g3chauffeur"); // endEvent(eventName, segmentation, 1, 5.0); - validateTimedEventSize(1, 0); - List events = TestUtils.getCurrentEventQueue(moduleEvents.ctx.getContext(), moduleEvents.L); - EventImpl imEvent = moduleEvents.eventQueue.eventQueueMemoryCache.get(0); + long end = System.currentTimeMillis(); - validateEvent(imEvent, eventName, segmentation, 1, 5.0, events.get(0).duration); - validateEvent(events.get(0), eventName, segmentation, 1, 5.0, events.get(0).duration); + Assert.assertEquals(0, moduleEvents.timedEvents.size()); + validateEventInQueue(TestUtils.getTestSDirectory(), eventName, segmentation, 1, 5.0, (double) (end - start) / 1000, 1, 0, moduleEvents.L); } /** @@ -391,8 +373,8 @@ private void validateEvent(EventImpl gonnaValidate, String key, Map= 0 && gonnaValidate.dow < 7); @@ -409,4 +391,16 @@ private void startEvent(String key) { boolean result = Countly.instance().events().startEvent(key); Assert.assertTrue(result); } + + void validateEventInQueue(File targetFolder, String key, Map segmentation, + int count, Double sum, Double duration, int queueSize, int elementInQueue, Log L) { + List events = TestUtils.getCurrentEventQueue(targetFolder, moduleEvents.L); + validateQueueSize(queueSize, events); + + //check if event was recorded correctly + EventImpl event = events.get(elementInQueue); + EventImpl eventInMemory = moduleEvents.eventQueue.eventQueueMemoryCache.get(0); + validateEvent(event, key, segmentation, count, sum, duration); + validateEvent(eventInMemory, key, segmentation, count, sum, duration); + } } From f5a4fca20fedb0ec0b82fd9175e0f6065363a1d0 Mon Sep 17 00:00:00 2001 From: arifBurakDemiray Date: Thu, 28 Sep 2023 09:28:12 +0300 Subject: [PATCH 76/82] fix: os specific test --- .../java/ly/count/sdk/java/internal/ModuleEventsTests.java | 2 +- .../test/java/ly/count/sdk/java/internal/UtilsTests.java | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/sdk-java/src/test/java/ly/count/sdk/java/internal/ModuleEventsTests.java b/sdk-java/src/test/java/ly/count/sdk/java/internal/ModuleEventsTests.java index caad9e6f1..a5aacbf30 100644 --- a/sdk-java/src/test/java/ly/count/sdk/java/internal/ModuleEventsTests.java +++ b/sdk-java/src/test/java/ly/count/sdk/java/internal/ModuleEventsTests.java @@ -173,7 +173,7 @@ public void startEvent() { endEvent(eventName, null, 1, null); long end = System.currentTimeMillis(); - //duration testing is not possible because division is error-prone for small numbers like .212 and .211 + Assert.assertEquals(0, moduleEvents.timedEvents.size()); validateEventInQueue(TestUtils.getTestSDirectory(), eventName, null, 1, null, (double) (end - start) / 1000, 1, 0, moduleEvents.L); } diff --git a/sdk-java/src/test/java/ly/count/sdk/java/internal/UtilsTests.java b/sdk-java/src/test/java/ly/count/sdk/java/internal/UtilsTests.java index 73d1d7126..046f1567a 100644 --- a/sdk-java/src/test/java/ly/count/sdk/java/internal/UtilsTests.java +++ b/sdk-java/src/test/java/ly/count/sdk/java/internal/UtilsTests.java @@ -295,7 +295,11 @@ public void readFileContent_fileNotReadable() throws IOException { file.setReadable(false); String content = Utils.readFileContent(file, logger); - Assert.assertEquals("", content);//todo this might fail on windows. Potentiall add a platform check for this test + if (System.getProperty("os.name").toLowerCase().contains("win")) { + Assert.assertEquals(fileContent, content); + } else { + Assert.assertEquals("", content); + } } finally { File file = new File(TEST_FILE_NAME); if (file.exists()) { From fcae3886644aca4bde9628eb998069d3efb8b24d Mon Sep 17 00:00:00 2001 From: arifBurakDemiray Date: Thu, 28 Sep 2023 09:35:26 +0300 Subject: [PATCH 77/82] refactor: remove unused param --- .../main/java/ly/count/sdk/java/internal/EventQueue.java | 6 ++---- .../main/java/ly/count/sdk/java/internal/ModuleEvents.java | 2 +- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/sdk-java/src/main/java/ly/count/sdk/java/internal/EventQueue.java b/sdk-java/src/main/java/ly/count/sdk/java/internal/EventQueue.java index 5eae9d876..85bf254fe 100644 --- a/sdk-java/src/main/java/ly/count/sdk/java/internal/EventQueue.java +++ b/sdk-java/src/main/java/ly/count/sdk/java/internal/EventQueue.java @@ -8,13 +8,11 @@ public class EventQueue { static final String DELIMITER = ":::"; private final Log L; - private final InternalConfig config; final List eventQueueMemoryCache; - protected EventQueue(Log logger, InternalConfig config) { + protected EventQueue(Log logger, int bufferSize) { L = logger; - this.config = config; - eventQueueMemoryCache = new ArrayList<>(config.getEventsBufferSize()); + eventQueueMemoryCache = new ArrayList<>(bufferSize); } /** diff --git a/sdk-java/src/main/java/ly/count/sdk/java/internal/ModuleEvents.java b/sdk-java/src/main/java/ly/count/sdk/java/internal/ModuleEvents.java index 2aba19a2c..cada83265 100644 --- a/sdk-java/src/main/java/ly/count/sdk/java/internal/ModuleEvents.java +++ b/sdk-java/src/main/java/ly/count/sdk/java/internal/ModuleEvents.java @@ -22,7 +22,7 @@ public class ModuleEvents extends ModuleBase { public void init(InternalConfig config, Log logger) { super.init(config, logger); L.d("[ModuleEvents] init: config = " + config); - eventQueue = new EventQueue(L, config); + eventQueue = new EventQueue(L, config.getEventsBufferSize()); eventQueue.restoreFromDisk(); eventsInterface = new Events(); } From b26fb1c4a954063fd9ce9de02395f276e097e544 Mon Sep 17 00:00:00 2001 From: arifBurakDemiray Date: Thu, 28 Sep 2023 11:04:43 +0300 Subject: [PATCH 78/82] feat: event queue test --- .../count/sdk/java/internal/EventQueue.java | 17 +- .../sdk/java/internal/BackendModeTests.java | 2 +- .../sdk/java/internal/EventQueueTests.java | 291 ++++++++++++++++++ .../sdk/java/internal/ModuleEventsTests.java | 30 +- .../ly/count/sdk/java/internal/TestUtils.java | 33 +- 5 files changed, 339 insertions(+), 34 deletions(-) create mode 100644 sdk-java/src/test/java/ly/count/sdk/java/internal/EventQueueTests.java diff --git a/sdk-java/src/main/java/ly/count/sdk/java/internal/EventQueue.java b/sdk-java/src/main/java/ly/count/sdk/java/internal/EventQueue.java index 85bf254fe..74d3c6f1c 100644 --- a/sdk-java/src/main/java/ly/count/sdk/java/internal/EventQueue.java +++ b/sdk-java/src/main/java/ly/count/sdk/java/internal/EventQueue.java @@ -23,15 +23,24 @@ protected int eqSize() { } void addEvent(final EventImpl event) { - L.d("[EventQueue] Adding event: " + event.key); - eventQueueMemoryCache.add(event); - writeEventQueueToStorage(); + if (event == null) { + L.w("[EventQueue] Event is null, skipping"); + } else { + L.d("[EventQueue] Adding event: " + event.key); + eventQueueMemoryCache.add(event); + writeEventQueueToStorage(); + } } /** * set the new value in event data storage */ void writeEventQueueToStorage() { + if (eventQueueMemoryCache.isEmpty()) { + L.d("[EventQueue] No events to write to disk"); + return; + } + final String eventQueue = joinEvents(eventQueueMemoryCache); L.d("[EventQueue] Setting event data: " + eventQueue); @@ -58,7 +67,7 @@ void restoreFromDisk() { eventQueueMemoryCache.sort((e1, e2) -> (int) (e1.timestamp - e2.timestamp)); } - private String joinEvents(final Collection collection) { + String joinEvents(final Collection collection) { final List strings = new ArrayList<>(); for (EventImpl e : collection) { strings.add(e.toJSON(L)); diff --git a/sdk-java/src/test/java/ly/count/sdk/java/internal/BackendModeTests.java b/sdk-java/src/test/java/ly/count/sdk/java/internal/BackendModeTests.java index becdeadd5..c1cddd126 100644 --- a/sdk-java/src/test/java/ly/count/sdk/java/internal/BackendModeTests.java +++ b/sdk-java/src/test/java/ly/count/sdk/java/internal/BackendModeTests.java @@ -31,7 +31,7 @@ public static void init() { cc.setEventQueueSizeToSend(4).enableBackendMode(); // System specific folder structure - File sdkStorageRootDirectory = TestUtils.getSdkStorageRootDirectory(); + File sdkStorageRootDirectory = TestUtils.getTestSDirectory(); TestUtils.checkSdkStorageRootDirectoryExist(sdkStorageRootDirectory); Countly.init(sdkStorageRootDirectory, cc); diff --git a/sdk-java/src/test/java/ly/count/sdk/java/internal/EventQueueTests.java b/sdk-java/src/test/java/ly/count/sdk/java/internal/EventQueueTests.java new file mode 100644 index 000000000..0eb902f35 --- /dev/null +++ b/sdk-java/src/test/java/ly/count/sdk/java/internal/EventQueueTests.java @@ -0,0 +1,291 @@ +package ly.count.sdk.java.internal; + +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import ly.count.sdk.java.Config; +import ly.count.sdk.java.Countly; +import org.junit.After; +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +import static ly.count.sdk.java.internal.SDKStorage.EVENT_QUEUE_FILE_NAME; +import static ly.count.sdk.java.internal.SDKStorage.FILE_NAME_PREFIX; +import static ly.count.sdk.java.internal.SDKStorage.FILE_NAME_SEPARATOR; +import static ly.count.sdk.java.internal.TestUtils.validateEvent; +import static org.mockito.ArgumentMatchers.anyCollection; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; + +@RunWith(JUnit4.class) +public class EventQueueTests { + + Log L = mock(Log.class); + + EventQueue eventQueue; + + private void init(Config cc) { + Countly.instance().init(cc); + eventQueue = new EventQueue(L, cc.getEventsBufferSize()); + } + + @After + public void stop() { + Countly.instance().halt(); + eventQueue = null; + } + + /** + * Add an event to queue + * "addEvent" function should add event to both queue and memory + * in memory and cache queue should contain it + */ + @Test + public void addEvent() { + Config config = TestUtils.getBaseConfig(); + config.setEventQueueSizeToSend(2); + init(config); + + validateQueueSize(0); + EventImpl event = createEvent("test-addEvent", null, 1, null, null); + eventQueue.addEvent(event); + validateQueueSize(1); + + validateEventInQueue(event.key, null, 1, null, null, 1, 0); + } + + /** + * Add a null event to queue + * "addEvent" function should not add event to both queue and memory + * in memory and cache queue size should be 0 + */ + @Test + public void addEvent_null() { + Config config = TestUtils.getBaseConfig(); + config.setEventQueueSizeToSend(2); + init(config); + + validateQueueSize(0); + eventQueue.addEvent(null); + validateQueueSize(0); + } + + /** + * Write in memory events to storage + * "writeEventQueueToStorage" function should write events from memory to storage + * in memory and cache queue size should be 1 and should contain event + */ + @Test + public void writeEventQueueToStorage() { + Config config = TestUtils.getBaseConfig(); + config.setEventQueueSizeToSend(2); + init(config); + + validateQueueSize(0); + EventImpl event = createEvent("test-writeEventQueueToStorage", null, 1, null, null); + eventQueue.eventQueueMemoryCache.add(event); + eventQueue.writeEventQueueToStorage(); + validateEventInQueue(event.key, null, 1, null, null, 1, 0); + } + + /** + * Write empty in memory events + * "writeEventQueueToStorage" function should not call "joinEvents" + * joinEvents should not be called + */ + @Test + public void writeEventQueueToStorage_emptyCache() { + eventQueue = mock(EventQueue.class); + + eventQueue.writeEventQueueToStorage(); + verify(eventQueue, never()).joinEvents(anyCollection()); + } + + /** + * Join events with delimiter + * "joinEvents" function should join events + * joinEvents should return expected String + */ + @Test + public void joinEvents() { + Config config = TestUtils.getBaseConfig(); + config.setEventQueueSizeToSend(2); + init(config); + + List list = new ArrayList<>(); + list.add(createEvent("test-joinEvents-1", null, 1, null, null)); + list.add(createEvent("test-joinEvents-2", null, 1, null, null)); + + String result = eventQueue.joinEvents(list); + + String expected = list.stream().map(event -> event.toJSON(L)).reduce((a, b) -> a + EventQueue.DELIMITER + b).orElse(""); + Assert.assertEquals(expected, result); + } + + /** + * Join events with empty collection + * "joinEvents" function should join events + * joinEvents should return expected String + */ + @Test + public void joinEvents_emptyCollection() { + Config config = TestUtils.getBaseConfig(); + config.setEventQueueSizeToSend(2); + init(config); + + List list = new ArrayList<>(); + + String result = eventQueue.joinEvents(list); + Assert.assertEquals("", result); + } + + /** + * Join events with delimiter null collection + * "joinEvents" function should not work + * joinEvents should throw NullPointerException + */ + @Test(expected = NullPointerException.class) + public void joinEvents_nullCollection() { + Config config = TestUtils.getBaseConfig(); + config.setEventQueueSizeToSend(2); + init(config); + + eventQueue.joinEvents(null); + } + + /** + * Clear events from storage and cache + * "clear" function should work + * both memory and cache should be empty + */ + @Test + public void clear() { + Config config = TestUtils.getBaseConfig(); + config.setEventQueueSizeToSend(2); + init(config); + + validateQueueSize(0); + + eventQueue.addEvent(createEvent("test-clear", null, 1, null, null)); + validateQueueSize(1); + eventQueue.addEvent(createEvent("test-clear", null, 1, null, null)); + validateQueueSize(2); + + eventQueue.clear(); + validateQueueSize(0); + } + + /** + * Restore events from storage + * "restoreFromDisk" function should work + * both memory and cache should have desired size + */ + @Test + public void restoreFromDisk() throws IOException { + Config config = TestUtils.getBaseConfig(); + config.setEventQueueSizeToSend(2); + init(config); + + validateQueueSize(0); + writeToEventQueue("{\"hour\":10,\"count\":1,\"dow\":4,\"key\":\"test-joinEvents-1\",\"timestamp\":1695887006647}:::{\"hour\":10,\"count\":1,\"dow\":4,\"key\":\"test-joinEvents-2\",\"timestamp\":1695887006657}", false); + + eventQueue.restoreFromDisk(); + validateQueueSize(2); + validateEvent(eventQueue.eventQueueMemoryCache.get(0), "test-joinEvents-1", null, 1, null, null); + validateEvent(eventQueue.eventQueueMemoryCache.get(1), "test-joinEvents-2", null, 1, null, null); + } + + /** + * Restore events from storage not existing file + * "restoreFromDisk" function should work + * both memory and cache should be empty + */ + @Test + public void restoreFromDisk_notExist() throws IOException { + Config config = TestUtils.getBaseConfig(); + config.setEventQueueSizeToSend(2); + init(config); + + validateQueueSize(0); + writeToEventQueue(null, true); + + eventQueue.restoreFromDisk(); + validateQueueSize(0); + } + + /** + * Restore events from storage garbage file + * "restoreFromDisk" function should work + * both memory and cache should be empty + */ + @Test + public void restoreFromDisk_garbageFile() throws IOException { + Config config = TestUtils.getBaseConfig(); + config.setEventQueueSizeToSend(2); + init(config); + + validateQueueSize(0); + writeToEventQueue("{\"hour\":10,\"asdasd\":\"askjdn\",\"timestamp\":1695887006647}::{\"hour\":10,\"count\":1,\"dow\":4,\"asda\":\"test-joinEvents-2\"}", false); + + eventQueue.restoreFromDisk(); + validateQueueSize(0); + } + + /** + * Restore events from storage corrupted file + * "restoreFromDisk" function should read only not corrupted events + * both memory and cache should have desired size and contain only not corrupted events + */ + @Test + public void restoreFromDisk_corruptedData() throws IOException { + Config config = TestUtils.getBaseConfig(); + config.setEventQueueSizeToSend(2); + init(config); + + validateQueueSize(0); + writeToEventQueue("{\"hour\":10,\"count\":1,\"dow\":4,\"key\":\"test-joinEvents-1\",\"timestamp\":1695887006647}:::{\"hour\":10,\"count\":1,\"dow\":4,\"keya\":\"test-joinEvents-2\",\"timestamp\":1695887006657}", false); + + eventQueue.restoreFromDisk(); + validateQueueSize(1); + validateEvent(eventQueue.eventQueueMemoryCache.get(0), "test-joinEvents-1", null, 1, null, null); + } + + private void writeToEventQueue(String fileContent, boolean delete) throws IOException { + File file = new File(TestUtils.getTestSDirectory(), FILE_NAME_PREFIX + FILE_NAME_SEPARATOR + EVENT_QUEUE_FILE_NAME); + file.createNewFile(); + if (delete) { + file.delete(); + return; + } + FileWriter writer = new FileWriter(file); + writer.write(fileContent); + writer.close(); + } + + private EventImpl createEvent(String key, Map segmentation, int count, Double sum, Double dur) { + return new EventImpl(key, count, sum, dur, segmentation, L); + } + + private void validateQueueSize(int expectedSize) { + Assert.assertEquals(expectedSize, TestUtils.getCurrentEventQueue(TestUtils.getTestSDirectory(), L).size()); + Assert.assertEquals(expectedSize, eventQueue.eqSize()); + } + + void validateEventInQueue(String key, Map segmentation, + int count, Double sum, Double duration, int queueSize, int elementInQueue) { + List events = TestUtils.getCurrentEventQueue(TestUtils.getTestSDirectory(), L); + validateQueueSize(queueSize); + + //check if event was recorded correctly + EventImpl event = events.get(elementInQueue); + EventImpl eventInMemory = eventQueue.eventQueueMemoryCache.get(0); + validateEvent(event, key, segmentation, count, sum, duration); + validateEvent(eventInMemory, key, segmentation, count, sum, duration); + } +} diff --git a/sdk-java/src/test/java/ly/count/sdk/java/internal/ModuleEventsTests.java b/sdk-java/src/test/java/ly/count/sdk/java/internal/ModuleEventsTests.java index a5aacbf30..adf9a6bc9 100644 --- a/sdk-java/src/test/java/ly/count/sdk/java/internal/ModuleEventsTests.java +++ b/sdk-java/src/test/java/ly/count/sdk/java/internal/ModuleEventsTests.java @@ -12,6 +12,8 @@ import org.junit.runner.RunWith; import org.junit.runners.JUnit4; +import static ly.count.sdk.java.internal.TestUtils.validateEvent; + @RunWith(JUnit4.class) public class ModuleEventsTests { @@ -53,7 +55,7 @@ public void recordEvent() { Countly.instance().events().recordEvent(eventKey, 1, 45.9, segmentation, 32.0); //check if event was recorded correctly and size of event queue is equal to size of events in queue - validateEventInQueue(TestUtils.getTestSDirectory(), eventKey, segmentation, 1, 45.9, 32.0, 1, 0, moduleEvents.L); + validateEventInQueue(TestUtils.getTestSDirectory(), eventKey, segmentation, 1, 45.9, 32.0, 1, 0); } /** @@ -147,7 +149,7 @@ public void recordEvent_invalidSegment() { //record event with key segmentation Countly.instance().events().recordEvent(eventKey, segmentation); - validateEventInQueue(TestUtils.getTestSDirectory(), eventKey, expectedSegmentation, 1, null, null, 1, 0, moduleEvents.L); + validateEventInQueue(TestUtils.getTestSDirectory(), eventKey, expectedSegmentation, 1, null, null, 1, 0); } /** @@ -175,7 +177,7 @@ public void startEvent() { long end = System.currentTimeMillis(); Assert.assertEquals(0, moduleEvents.timedEvents.size()); - validateEventInQueue(TestUtils.getTestSDirectory(), eventName, null, 1, null, (double) (end - start) / 1000, 1, 0, moduleEvents.L); + validateEventInQueue(TestUtils.getTestSDirectory(), eventName, null, 1, null, (double) (end - start) / 1000, 1, 0); } /** @@ -241,7 +243,7 @@ public void startEvent_alreadyStarted() { long end = System.currentTimeMillis(); Assert.assertEquals(0, moduleEvents.timedEvents.size()); - validateEventInQueue(TestUtils.getTestSDirectory(), eventName, null, 1, null, (double) (end - start) / 1000, 1, 0, moduleEvents.L); + validateEventInQueue(TestUtils.getTestSDirectory(), eventName, null, 1, null, (double) (end - start) / 1000, 1, 0); } /** @@ -322,7 +324,7 @@ public void endEvent_withSegmentation() { long end = System.currentTimeMillis(); Assert.assertEquals(0, moduleEvents.timedEvents.size()); - validateEventInQueue(TestUtils.getTestSDirectory(), eventName, segmentation, 1, 5.0, (double) (end - start) / 1000, 1, 0, moduleEvents.L); + validateEventInQueue(TestUtils.getTestSDirectory(), eventName, segmentation, 1, 5.0, (double) (end - start) / 1000, 1, 0); } /** @@ -366,22 +368,6 @@ private void validateQueueSize(int expectedSize, List events) { Assert.assertEquals(expectedSize, moduleEvents.eventQueue.eqSize()); } - private void validateEvent(EventImpl gonnaValidate, String key, Map segmentation, int count, Double sum, Double duration) { - Assert.assertEquals(key, gonnaValidate.key); - Assert.assertEquals(segmentation, gonnaValidate.segmentation); - Assert.assertEquals(count, gonnaValidate.count); - Assert.assertEquals(sum, gonnaValidate.sum); - - if (duration != null) { - double delta = 0.1; - Assert.assertTrue(Math.abs(duration - gonnaValidate.duration) < delta); - } - - Assert.assertTrue(gonnaValidate.dow >= 0 && gonnaValidate.dow < 7); - Assert.assertTrue(gonnaValidate.hour >= 0 && gonnaValidate.hour < 24); - Assert.assertTrue(gonnaValidate.timestamp >= 0); - } - private void endEvent(String key, Map segmentation, int count, Double sum) { boolean result = Countly.instance().events().endEvent(key, segmentation, count, sum); Assert.assertTrue(result); @@ -393,7 +379,7 @@ private void startEvent(String key) { } void validateEventInQueue(File targetFolder, String key, Map segmentation, - int count, Double sum, Double duration, int queueSize, int elementInQueue, Log L) { + int count, Double sum, Double duration, int queueSize, int elementInQueue) { List events = TestUtils.getCurrentEventQueue(targetFolder, moduleEvents.L); validateQueueSize(queueSize, events); diff --git a/sdk-java/src/test/java/ly/count/sdk/java/internal/TestUtils.java b/sdk-java/src/test/java/ly/count/sdk/java/internal/TestUtils.java index ec041d41b..c64eef5e9 100644 --- a/sdk-java/src/test/java/ly/count/sdk/java/internal/TestUtils.java +++ b/sdk-java/src/test/java/ly/count/sdk/java/internal/TestUtils.java @@ -1,12 +1,21 @@ package ly.count.sdk.java.internal; -import ly.count.sdk.java.Config; import java.io.File; import java.io.IOException; -import java.util.*; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Comparator; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Scanner; import java.util.stream.Stream; +import ly.count.sdk.java.Config; +import org.junit.Assert; -import static ly.count.sdk.java.internal.SDKStorage.*; +import static ly.count.sdk.java.internal.SDKStorage.EVENT_QUEUE_FILE_NAME; +import static ly.count.sdk.java.internal.SDKStorage.FILE_NAME_PREFIX; +import static ly.count.sdk.java.internal.SDKStorage.FILE_NAME_SEPARATOR; public class TestUtils { static String SERVER_URL = "https://test.count.ly"; @@ -168,9 +177,19 @@ private static Map parseRequestParams(File file) throws IOExcept } } - static File getSdkStorageRootDirectory() { - // System specific folder structure - String[] sdkStorageRootPath = { System.getProperty("user.home"), "__COUNTLY", "java_test" }; - return new File(String.join(File.separator, sdkStorageRootPath)); + static void validateEvent(EventImpl gonnaValidate, String key, Map segmentation, int count, Double sum, Double duration) { + Assert.assertEquals(key, gonnaValidate.key); + Assert.assertEquals(segmentation, gonnaValidate.segmentation); + Assert.assertEquals(count, gonnaValidate.count); + Assert.assertEquals(sum, gonnaValidate.sum); + + if (duration != null) { + double delta = 0.1; + Assert.assertTrue(Math.abs(duration - gonnaValidate.duration) < delta); + } + + Assert.assertTrue(gonnaValidate.dow >= 0 && gonnaValidate.dow < 7); + Assert.assertTrue(gonnaValidate.hour >= 0 && gonnaValidate.hour < 24); + Assert.assertTrue(gonnaValidate.timestamp >= 0); } } \ No newline at end of file From 0857eb3acf143902acb773ae2c117a41526c53f3 Mon Sep 17 00:00:00 2001 From: arifBurakDemiray Date: Thu, 28 Sep 2023 11:18:19 +0300 Subject: [PATCH 79/82] feat: event queue size over test --- .../sdk/java/internal/EventQueueTests.java | 2 +- .../sdk/java/internal/ModuleEventsTests.java | 42 +++++++++++++++++++ 2 files changed, 43 insertions(+), 1 deletion(-) diff --git a/sdk-java/src/test/java/ly/count/sdk/java/internal/EventQueueTests.java b/sdk-java/src/test/java/ly/count/sdk/java/internal/EventQueueTests.java index 0eb902f35..19cf115f5 100644 --- a/sdk-java/src/test/java/ly/count/sdk/java/internal/EventQueueTests.java +++ b/sdk-java/src/test/java/ly/count/sdk/java/internal/EventQueueTests.java @@ -256,7 +256,7 @@ public void restoreFromDisk_corruptedData() throws IOException { validateEvent(eventQueue.eventQueueMemoryCache.get(0), "test-joinEvents-1", null, 1, null, null); } - private void writeToEventQueue(String fileContent, boolean delete) throws IOException { + static void writeToEventQueue(String fileContent, boolean delete) throws IOException { File file = new File(TestUtils.getTestSDirectory(), FILE_NAME_PREFIX + FILE_NAME_SEPARATOR + EVENT_QUEUE_FILE_NAME); file.createNewFile(); if (delete) { diff --git a/sdk-java/src/test/java/ly/count/sdk/java/internal/ModuleEventsTests.java b/sdk-java/src/test/java/ly/count/sdk/java/internal/ModuleEventsTests.java index adf9a6bc9..105a6b8fe 100644 --- a/sdk-java/src/test/java/ly/count/sdk/java/internal/ModuleEventsTests.java +++ b/sdk-java/src/test/java/ly/count/sdk/java/internal/ModuleEventsTests.java @@ -1,6 +1,7 @@ package ly.count.sdk.java.internal; import java.io.File; +import java.io.IOException; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -58,6 +59,43 @@ public void recordEvent() { validateEventInQueue(TestUtils.getTestSDirectory(), eventKey, segmentation, 1, 45.9, 32.0, 1, 0); } + /** + * Recording an event and no event to recover + * "recordEvent" function should create an event with given key + * event queue should be empty when reached to event queue size to send + */ + @Test + public void recordEvent_queueSizeOver() { + Config config = TestUtils.getBaseConfig(); + config.enableFeatures(Config.Feature.Events).setEventQueueSizeToSend(2); + init(config); + + validateQueueSize(0); + + Countly.instance().events().recordEvent("recordEvent_queueSizeOver", 1, 45.9, null, 32.0); + validateQueueSize(1); + + Countly.instance().events().recordEvent("recordEvent_queueSizeOver", 1, 45.9, null, 32.0); + validateQueueSize(0); + } + + /** + * Recording an event with recovered events + * "recordEvent" function should create an event with given key and create a request with memory data + * event queue should be empty when reached to event queue size to send + */ + @Test + public void recordEvent_queueSizeOverMemory() throws IOException { + EventQueueTests.writeToEventQueue("{\"hour\":10,\"count\":1,\"dow\":4,\"key\":\"test-joinEvents-1\",\"timestamp\":1695887006647}:::{\"hour\":10,\"count\":1,\"dow\":4,\"key\":\"test-joinEvents-2\",\"timestamp\":1695887006657}", false); + Config config = TestUtils.getBaseConfig(); + config.enableFeatures(Config.Feature.Events).setEventQueueSizeToSend(2); + init(config); + + validateQueueSize(2); + Countly.instance().events().recordEvent("recordEvent_queueSizeOver", 1, 45.9, null, 32.0); + validateQueueSize(0); + } + /** * Recording an event with negative count * "recordEvent" function should not create an event with given key and negative count @@ -368,6 +406,10 @@ private void validateQueueSize(int expectedSize, List events) { Assert.assertEquals(expectedSize, moduleEvents.eventQueue.eqSize()); } + private void validateQueueSize(int expectedSize) { + validateQueueSize(expectedSize, TestUtils.getCurrentEventQueue(moduleEvents.ctx.getContext(), moduleEvents.L)); + } + private void endEvent(String key, Map segmentation, int count, Double sum) { boolean result = Countly.instance().events().endEvent(key, segmentation, count, sum); Assert.assertTrue(result); From 45aee6cc26b07e9a0f5a84e4a38fc52766e499a2 Mon Sep 17 00:00:00 2001 From: arifBurakDemiray Date: Thu, 28 Sep 2023 11:23:38 +0300 Subject: [PATCH 80/82] feat: cancel event tests --- .../sdk/java/internal/ModuleEventsTests.java | 75 ++++++++++++++++++- 1 file changed, 74 insertions(+), 1 deletion(-) diff --git a/sdk-java/src/test/java/ly/count/sdk/java/internal/ModuleEventsTests.java b/sdk-java/src/test/java/ly/count/sdk/java/internal/ModuleEventsTests.java index 105a6b8fe..9a8c156ff 100644 --- a/sdk-java/src/test/java/ly/count/sdk/java/internal/ModuleEventsTests.java +++ b/sdk-java/src/test/java/ly/count/sdk/java/internal/ModuleEventsTests.java @@ -301,7 +301,7 @@ public void endEvent_emptyKey() { } /** - * End an event with empty key + * End an event with null key * "endEvent" function should not work with null key * in memory , timed events and cache queue not contain it */ @@ -396,6 +396,79 @@ public void endEvent_withSegmentation_negativeCount() { validateEvent(timedEvent, eventName, null, 1, null, null); } + /** + * Cancel an event with empty key + * "cancelEvent" function should not work with empty key + * in memory , timed events and cache queue not contain it + */ + @Test + public void cancelEvent_emptyKey() { + Config config = TestUtils.getBaseConfig(); + config.enableFeatures(Config.Feature.Events).setEventQueueSizeToSend(4); + init(config); + + validateTimedEventSize(0, 0); + Assert.assertFalse(Countly.instance().events().cancelEvent("")); + validateTimedEventSize(0, 0); + } + + /** + * Cancel an event with null key + * "cancelEvent" function should not work with null key + * in memory , timed events and cache queue not contain it + */ + @Test + public void cancelEvent_nullKey() { + Config config = TestUtils.getBaseConfig(); + config.enableFeatures(Config.Feature.Events).setEventQueueSizeToSend(4); + init(config); + + validateTimedEventSize(0, 0); + Assert.assertFalse(Countly.instance().events().cancelEvent(null)); + validateTimedEventSize(0, 0); + } + + /** + * Cancel a not existing event + * "cancelEvent" function should not work with not existing event key + * in memory , timed events and cache queue not contain it + */ + @Test + public void cancelEvent_notExist() { + Config config = TestUtils.getBaseConfig(); + config.enableFeatures(Config.Feature.Events).setEventQueueSizeToSend(4); + init(config); + + validateTimedEventSize(0, 0); + Assert.assertFalse(Countly.instance().events().cancelEvent("cancelEvent_notExist")); + validateTimedEventSize(0, 0); + } + + /** + * Cancel an event + * "cancelEvent" function should cancel an event + * in memory , timed events and cache queue should contain it + */ + @Test + public void cancelEvent() { + Config config = TestUtils.getBaseConfig(); + config.enableFeatures(Config.Feature.Events).setEventQueueSizeToSend(4); + init(config); + + validateTimedEventSize(0, 0); + String eventName = "cancelEvent"; + + startEvent(eventName); // start event to end it + validateTimedEventSize(0, 1); + + EventImpl timedEvent = moduleEvents.timedEvents.get(eventName); + validateEvent(timedEvent, eventName, null, 1, null, null); + + Assert.assertTrue(Countly.instance().events().cancelEvent(eventName)); + Assert.assertEquals(0, moduleEvents.timedEvents.size()); + validateQueueSize(0); + } + private void validateTimedEventSize(int expectedQueueSize, int expectedTimedEventSize) { validateQueueSize(expectedQueueSize, TestUtils.getCurrentEventQueue(moduleEvents.ctx.getContext(), moduleEvents.L)); Assert.assertEquals(expectedTimedEventSize, moduleEvents.timedEvents.size()); From 605e6c7c1efe56b5a163e874762666b8cb2ac555 Mon Sep 17 00:00:00 2001 From: ArtursK Date: Thu, 28 Sep 2023 13:29:18 +0300 Subject: [PATCH 81/82] Refactoring test and variable names --- .../main/java/ly/count/sdk/java/Config.java | 19 +- .../main/java/ly/count/sdk/java/Event.java | 6 +- .../count/sdk/java/internal/EventQueue.java | 12 +- .../sdk/java/internal/InternalConfig.java | 4 +- .../count/sdk/java/internal/ModuleEvents.java | 2 +- .../sdk/java/internal/ModuleEventsTests.java | 190 ++++++++---------- .../ly/count/sdk/java/internal/TestUtils.java | 17 ++ 7 files changed, 120 insertions(+), 130 deletions(-) diff --git a/sdk-java/src/main/java/ly/count/sdk/java/Config.java b/sdk-java/src/main/java/ly/count/sdk/java/Config.java index 9fceca8b5..291d55930 100644 --- a/sdk-java/src/main/java/ly/count/sdk/java/Config.java +++ b/sdk-java/src/main/java/ly/count/sdk/java/Config.java @@ -368,7 +368,7 @@ public boolean restore(byte[] data, Log L) { * Maximum amount of time in seconds between two update requests to the server * reporting session duration and other parameters if any added between update requests. * - * Update request is also sent when number of unsent events reached {@link #eventsBufferSize}. + * Update request is also sent when number of unsent events reached {@link #eventQueueThreshold}. * * Set to 0 to disable update requests based on time. */ @@ -381,7 +381,7 @@ public boolean restore(byte[] data, Log L) { * * Set to 0 to disable buffering. */ - protected int eventsBufferSize = 10; + protected int eventQueueThreshold = 10; /** * {@link CrashProcessor}-implementing class which is instantiated when application @@ -909,12 +909,12 @@ public Config setUpdateSessionTimerDelay(int delay) { * * Update request is also sent when last update request was sent more than {@link #setSendUpdateEachSeconds(int)} seconds ago. * - * @param eventsBufferSize max number of events between two update requests, set to 0 to disable update requests based on events. + * @param eventQueueThreshold max number of events between two update requests, set to 0 to disable update requests based on events. * @return {@code this} instance for method chaining * @deprecated this will be removed, please use {@link #setEventQueueSizeToSend(int)} */ - public Config setEventsBufferSize(int eventsBufferSize) { - return setEventQueueSizeToSend(eventsBufferSize); + public Config setEventsBufferSize(int eventQueueThreshold) { + return setEventQueueSizeToSend(eventQueueThreshold); } /** @@ -931,7 +931,7 @@ public Config setEventQueueSizeToSend(int eventsQueueSize) { configLog.e("[Config] eventsQueueSize cannot be negative"); } } else { - this.eventsBufferSize = eventsQueueSize; + this.eventQueueThreshold = eventsQueueSize; } return this; } @@ -1412,12 +1412,13 @@ public int getSendUpdateEachSeconds() { } /** - * Getter for {@link #eventsBufferSize} + * Getter for {@link #eventQueueThreshold} * - * @return {@link #eventsBufferSize} value + * @return {@link #eventQueueThreshold} value + * @deprecated */ public int getEventsBufferSize() { - return eventsBufferSize; + return eventQueueThreshold; } /** diff --git a/sdk-java/src/main/java/ly/count/sdk/java/Event.java b/sdk-java/src/main/java/ly/count/sdk/java/Event.java index 8389d4b1e..90cf054e0 100644 --- a/sdk-java/src/main/java/ly/count/sdk/java/Event.java +++ b/sdk-java/src/main/java/ly/count/sdk/java/Event.java @@ -11,7 +11,8 @@ public interface Event { /** * Add event to the buffer, send it to the server in case number of events in the session - * is equal or bigger than {@link Config#eventsBufferSize} or wait until next {@link Session#update()}. + * is equal or bigger than {@link Config#eventQueueThreshold} or wait until next {@link Session#update()}. + * * @deprecated this function is deprecated, use {@link ModuleEvents.Events#recordEvent(String, int, double, Map, double)} instead */ void record(); @@ -20,7 +21,8 @@ public interface Event { * Set timed {@link Event} duration as difference between moment {@link Event} was created * and current time in seconds. Then add the event to its session (if they're enabled), * send it to the server in case number of events in the session is equal or bigger - * than {@link Config#eventsBufferSize} or wait until next {@link Session#update()}. + * than {@link Config#eventQueueThreshold} or wait until next {@link Session#update()}. + * * @deprecated this function is deprecated, use {@link ModuleEvents.Events#endEvent(String, Map, int, double)} instead */ void endAndRecord(); diff --git a/sdk-java/src/main/java/ly/count/sdk/java/internal/EventQueue.java b/sdk-java/src/main/java/ly/count/sdk/java/internal/EventQueue.java index 74d3c6f1c..dacf02a72 100644 --- a/sdk-java/src/main/java/ly/count/sdk/java/internal/EventQueue.java +++ b/sdk-java/src/main/java/ly/count/sdk/java/internal/EventQueue.java @@ -10,9 +10,9 @@ public class EventQueue { private final Log L; final List eventQueueMemoryCache; - protected EventQueue(Log logger, int bufferSize) { + protected EventQueue(Log logger, int eventThreshold) { L = logger; - eventQueueMemoryCache = new ArrayList<>(bufferSize); + eventQueueMemoryCache = new ArrayList<>(eventThreshold); } /** @@ -25,11 +25,11 @@ protected int eqSize() { void addEvent(final EventImpl event) { if (event == null) { L.w("[EventQueue] Event is null, skipping"); - } else { - L.d("[EventQueue] Adding event: " + event.key); - eventQueueMemoryCache.add(event); - writeEventQueueToStorage(); + return; } + L.d("[EventQueue] Adding event: " + event.key); + eventQueueMemoryCache.add(event); + writeEventQueueToStorage(); } /** diff --git a/sdk-java/src/main/java/ly/count/sdk/java/internal/InternalConfig.java b/sdk-java/src/main/java/ly/count/sdk/java/internal/InternalConfig.java index c65efe240..98c418417 100644 --- a/sdk-java/src/main/java/ly/count/sdk/java/internal/InternalConfig.java +++ b/sdk-java/src/main/java/ly/count/sdk/java/internal/InternalConfig.java @@ -138,7 +138,7 @@ public byte[] store(Log L) { } } stream.writeInt(sendUpdateEachSeconds); - stream.writeInt(eventsBufferSize); + stream.writeInt(eventQueueThreshold); stream.writeInt(0);//for keeping backwards compatibility, remove in the future. sessionCooldownPeriod stream.writeBoolean(false);//for keeping backwards compatibility, remove in the future stream.writeInt(5);//for keeping backwards compatibility, remove in the future (crashReportingANRCheckingPeriod) @@ -235,7 +235,7 @@ public boolean restore(byte[] data, Log L) { certificatePins.add(stream.readUTF()); } sendUpdateEachSeconds = stream.readInt(); - eventsBufferSize = stream.readInt(); + eventQueueThreshold = stream.readInt(); int throwawaySessionCooldownPeriod = stream.readInt();//we are only reading this for backwards compatibility. Throw away in the future boolean throwawayCountlyTestMode = stream.readBoolean();//we are only reading this for backwards compatibility. Throw away in the future int throwawayCrashReportingANRCheckingPeriod = stream.readInt();//we are only reading this for backwards compatibility. Throw away in the future. crashReportingANRCheckingPeriod diff --git a/sdk-java/src/main/java/ly/count/sdk/java/internal/ModuleEvents.java b/sdk-java/src/main/java/ly/count/sdk/java/internal/ModuleEvents.java index cada83265..28ea85e81 100644 --- a/sdk-java/src/main/java/ly/count/sdk/java/internal/ModuleEvents.java +++ b/sdk-java/src/main/java/ly/count/sdk/java/internal/ModuleEvents.java @@ -108,8 +108,8 @@ private void addEventToQueue(EventImpl event) { } private void checkEventQueueToSend(boolean forceSend) { + L.d("[ModuleEvents] queue size:[" + eventQueue.eqSize() + "] || forceSend: " + forceSend); if (forceSend || (eventQueue.eqSize() >= internalConfig.getEventsBufferSize())) { - L.d("[ModuleEvents] addEventToQueue: eventQueue.size >= internalConfig.getEventsBufferSize() || forceSend: " + forceSend); addEventsToRequestQ(); } } diff --git a/sdk-java/src/test/java/ly/count/sdk/java/internal/ModuleEventsTests.java b/sdk-java/src/test/java/ly/count/sdk/java/internal/ModuleEventsTests.java index 9a8c156ff..e4c360361 100644 --- a/sdk-java/src/test/java/ly/count/sdk/java/internal/ModuleEventsTests.java +++ b/sdk-java/src/test/java/ly/count/sdk/java/internal/ModuleEventsTests.java @@ -37,9 +37,7 @@ public void stop() { */ @Test public void recordEvent() { - Config config = TestUtils.getBaseConfig(); - config.enableFeatures(Config.Feature.Events).setEventQueueSizeToSend(4); - init(config); + init(TestUtils.getConfigEvents(4)); List events = TestUtils.getCurrentEventQueue(moduleEvents.ctx.getContext(), moduleEvents.L); validateQueueSize(0, events); @@ -50,13 +48,11 @@ public void recordEvent() { segmentation.put("weight", 67); segmentation.put("bald", true); - String eventKey = "recordEvent"; - //record event with key segmentation and count - Countly.instance().events().recordEvent(eventKey, 1, 45.9, segmentation, 32.0); + Countly.instance().events().recordEvent(TestUtils.eKeys[0], 1, 45.9, segmentation, 32.0); //check if event was recorded correctly and size of event queue is equal to size of events in queue - validateEventInQueue(TestUtils.getTestSDirectory(), eventKey, segmentation, 1, 45.9, 32.0, 1, 0); + validateEventInEventQueue(TestUtils.getTestSDirectory(), TestUtils.eKeys[0], segmentation, 1, 45.9, 32.0, 1, 0); } /** @@ -66,9 +62,7 @@ public void recordEvent() { */ @Test public void recordEvent_queueSizeOver() { - Config config = TestUtils.getBaseConfig(); - config.enableFeatures(Config.Feature.Events).setEventQueueSizeToSend(2); - init(config); + init(TestUtils.getConfigEvents(2)); validateQueueSize(0); @@ -87,9 +81,7 @@ public void recordEvent_queueSizeOver() { @Test public void recordEvent_queueSizeOverMemory() throws IOException { EventQueueTests.writeToEventQueue("{\"hour\":10,\"count\":1,\"dow\":4,\"key\":\"test-joinEvents-1\",\"timestamp\":1695887006647}:::{\"hour\":10,\"count\":1,\"dow\":4,\"key\":\"test-joinEvents-2\",\"timestamp\":1695887006657}", false); - Config config = TestUtils.getBaseConfig(); - config.enableFeatures(Config.Feature.Events).setEventQueueSizeToSend(2); - init(config); + init(TestUtils.getConfigEvents(2)); validateQueueSize(2); Countly.instance().events().recordEvent("recordEvent_queueSizeOver", 1, 45.9, null, 32.0); @@ -103,9 +95,7 @@ public void recordEvent_queueSizeOverMemory() throws IOException { */ @Test public void recordEvent_negativeCount() { - Config config = TestUtils.getBaseConfig(); - config.enableFeatures(Config.Feature.Events).setEventQueueSizeToSend(4); - init(config); + init(TestUtils.getConfigEvents(4)); List events = TestUtils.getCurrentEventQueue(moduleEvents.ctx.getContext(), moduleEvents.L); @@ -124,9 +114,7 @@ public void recordEvent_negativeCount() { */ @Test public void recordEvent_nullKey() { - Config config = TestUtils.getBaseConfig(); - config.enableFeatures(Config.Feature.Events).setEventQueueSizeToSend(4); - init(config); + init(TestUtils.getConfigEvents(4)); List events = TestUtils.getCurrentEventQueue(moduleEvents.ctx.getContext(), moduleEvents.L); @@ -145,9 +133,7 @@ public void recordEvent_nullKey() { */ @Test public void recordEvent_emptyKey() { - Config config = TestUtils.getBaseConfig(); - config.enableFeatures(Config.Feature.Events).setEventQueueSizeToSend(4); - init(config); + init(TestUtils.getConfigEvents(4)); List events = TestUtils.getCurrentEventQueue(moduleEvents.ctx.getContext(), moduleEvents.L); validateQueueSize(0, events); @@ -165,9 +151,7 @@ public void recordEvent_emptyKey() { */ @Test public void recordEvent_invalidSegment() { - Config config = TestUtils.getBaseConfig(); - config.enableFeatures(Config.Feature.Events).setEventQueueSizeToSend(4); - init(config); + init(TestUtils.getConfigEvents(4)); List events = TestUtils.getCurrentEventQueue(moduleEvents.ctx.getContext(), moduleEvents.L); validateQueueSize(0, events); @@ -183,11 +167,10 @@ public void recordEvent_invalidSegment() { expectedSegmentation.put("score", 67); expectedSegmentation.put("cheated", false); - String eventKey = "recordEvent_invalidSegment"; //record event with key segmentation - Countly.instance().events().recordEvent(eventKey, segmentation); + Countly.instance().events().recordEvent(TestUtils.eKeys[0], segmentation); - validateEventInQueue(TestUtils.getTestSDirectory(), eventKey, expectedSegmentation, 1, null, null, 1, 0); + validateEventInEventQueue(TestUtils.getTestSDirectory(), TestUtils.eKeys[0], expectedSegmentation, 1, null, null, 1, 0); } /** @@ -197,25 +180,20 @@ public void recordEvent_invalidSegment() { */ @Test public void startEvent() { - Config config = TestUtils.getBaseConfig(); - config.enableFeatures(Config.Feature.Events).setEventQueueSizeToSend(4); - init(config); + init(TestUtils.getConfigEvents(4)); validateTimedEventSize(0, 0); - String eventName = "startEvent"; - startEvent(eventName); - long start = System.currentTimeMillis(); + startEvent(TestUtils.eKeys[0]); validateTimedEventSize(0, 1); - EventImpl timedEvent = moduleEvents.timedEvents.get(eventName); - validateEvent(timedEvent, eventName, null, 1, null, null); + EventImpl timedEvent = moduleEvents.timedEvents.get(TestUtils.eKeys[0]); + validateEvent(timedEvent, TestUtils.eKeys[0], null, 1, null, null); - endEvent(eventName, null, 1, null); - long end = System.currentTimeMillis(); + endEvent(TestUtils.eKeys[0], null, 1, null); Assert.assertEquals(0, moduleEvents.timedEvents.size()); - validateEventInQueue(TestUtils.getTestSDirectory(), eventName, null, 1, null, (double) (end - start) / 1000, 1, 0); + validateEventInEventQueue(TestUtils.getTestSDirectory(), TestUtils.eKeys[0], null, 1, null, 0.0, 1, 0); } /** @@ -225,9 +203,7 @@ public void startEvent() { */ @Test public void startEvent_emptyKey() { - Config config = TestUtils.getBaseConfig(); - config.enableFeatures(Config.Feature.Events).setEventQueueSizeToSend(4); - init(config); + init(TestUtils.getConfigEvents(4)); validateTimedEventSize(0, 0); Assert.assertFalse(Countly.instance().events().startEvent("")); @@ -241,9 +217,7 @@ public void startEvent_emptyKey() { */ @Test public void startEvent_nullKey() { - Config config = TestUtils.getBaseConfig(); - config.enableFeatures(Config.Feature.Events).setEventQueueSizeToSend(4); - init(config); + init(TestUtils.getConfigEvents(4)); validateTimedEventSize(0, 0); Assert.assertFalse(Countly.instance().events().startEvent(null)); @@ -257,31 +231,26 @@ public void startEvent_nullKey() { */ @Test public void startEvent_alreadyStarted() { - Config config = TestUtils.getBaseConfig(); - config.enableFeatures(Config.Feature.Events).setEventQueueSizeToSend(4); - init(config); + init(TestUtils.getConfigEvents(4)); validateTimedEventSize(0, 0); - String eventName = "startEvent_alreadyStarted"; - startEvent(eventName); - long start = System.currentTimeMillis(); + startEvent(TestUtils.eKeys[0]); validateTimedEventSize(0, 1); - EventImpl timedEvent = moduleEvents.timedEvents.get(eventName); - validateEvent(timedEvent, eventName, null, 1, null, null); + EventImpl timedEvent = moduleEvents.timedEvents.get(TestUtils.eKeys[0]); + validateEvent(timedEvent, TestUtils.eKeys[0], null, 1, null, null); - boolean result = Countly.instance().events().startEvent(eventName); + boolean result = Countly.instance().events().startEvent(TestUtils.eKeys[0]); Assert.assertFalse(result); validateTimedEventSize(0, 1); - endEvent(eventName, null, 1, null); - long end = System.currentTimeMillis(); + endEvent(TestUtils.eKeys[0], null, 1, null); Assert.assertEquals(0, moduleEvents.timedEvents.size()); - validateEventInQueue(TestUtils.getTestSDirectory(), eventName, null, 1, null, (double) (end - start) / 1000, 1, 0); + validateEventInEventQueue(TestUtils.getTestSDirectory(), TestUtils.eKeys[0], null, 1, null, 0.0, 1, 0); } /** @@ -291,9 +260,7 @@ public void startEvent_alreadyStarted() { */ @Test public void endEvent_emptyKey() { - Config config = TestUtils.getBaseConfig(); - config.enableFeatures(Config.Feature.Events).setEventQueueSizeToSend(4); - init(config); + init(TestUtils.getConfigEvents(4)); validateTimedEventSize(0, 0); Assert.assertFalse(Countly.instance().events().endEvent("")); @@ -307,9 +274,7 @@ public void endEvent_emptyKey() { */ @Test public void endEvent_nullKey() { - Config config = TestUtils.getBaseConfig(); - config.enableFeatures(Config.Feature.Events).setEventQueueSizeToSend(4); - init(config); + init(TestUtils.getConfigEvents(4)); validateTimedEventSize(0, 0); Assert.assertFalse(Countly.instance().events().endEvent(null)); @@ -323,9 +288,7 @@ public void endEvent_nullKey() { */ @Test public void endEvent_notExist() { - Config config = TestUtils.getBaseConfig(); - config.enableFeatures(Config.Feature.Events).setEventQueueSizeToSend(4); - init(config); + init(TestUtils.getConfigEvents(4)); validateTimedEventSize(0, 0); Assert.assertFalse(Countly.instance().events().endEvent("endEvent_notExist")); @@ -339,30 +302,25 @@ public void endEvent_notExist() { */ @Test public void endEvent_withSegmentation() { - Config config = TestUtils.getBaseConfig(); - config.enableFeatures(Config.Feature.Events).setEventQueueSizeToSend(4); - init(config); + init(TestUtils.getConfigEvents(4)); validateTimedEventSize(0, 0); - String eventName = "endEvent_withSegmentation"; - startEvent(eventName); // start event to end it - long start = System.currentTimeMillis(); + startEvent(TestUtils.eKeys[0]); // start event to end it validateTimedEventSize(0, 1); - EventImpl timedEvent = moduleEvents.timedEvents.get(eventName); - validateEvent(timedEvent, eventName, null, 1, null, null); + EventImpl timedEvent = moduleEvents.timedEvents.get(TestUtils.eKeys[0]); + validateEvent(timedEvent, TestUtils.eKeys[0], null, 1, null, null); Map segmentation = new HashMap<>(); segmentation.put("hair_color", "red"); segmentation.put("hair_length", "short"); segmentation.put("chauffeur", "g3chauffeur"); // - endEvent(eventName, segmentation, 1, 5.0); - long end = System.currentTimeMillis(); + endEvent(TestUtils.eKeys[0], segmentation, 1, 5.0); Assert.assertEquals(0, moduleEvents.timedEvents.size()); - validateEventInQueue(TestUtils.getTestSDirectory(), eventName, segmentation, 1, 5.0, (double) (end - start) / 1000, 1, 0); + validateEventInEventQueue(TestUtils.getTestSDirectory(), TestUtils.eKeys[0], segmentation, 1, 5.0, 0.0, 1, 0); } /** @@ -373,39 +331,34 @@ public void endEvent_withSegmentation() { */ @Test(expected = IllegalArgumentException.class) public void endEvent_withSegmentation_negativeCount() { - Config config = TestUtils.getBaseConfig(); - config.enableFeatures(Config.Feature.Events).setEventQueueSizeToSend(4); - init(config); + init(TestUtils.getConfigEvents(4)); validateTimedEventSize(0, 0); - String eventName = "endEvent_withSegmentation_negativeCount"; - startEvent(eventName); // start event to end it + startEvent(TestUtils.eKeys[0]); // start event to end it validateTimedEventSize(0, 1); - EventImpl timedEvent = moduleEvents.timedEvents.get(eventName); - validateEvent(timedEvent, eventName, null, 1, null, null); + EventImpl timedEvent = moduleEvents.timedEvents.get(TestUtils.eKeys[0]); + validateEvent(timedEvent, TestUtils.eKeys[0], null, 1, null, null); Map segmentation = new HashMap<>(); segmentation.put("horse_name", "Alice"); segmentation.put("bet_amount", 300); segmentation.put("currency", "Dollar"); // - endEvent(eventName, segmentation, -7, 67.0); + endEvent(TestUtils.eKeys[0], segmentation, -7, 67.0); validateTimedEventSize(0, 1); - timedEvent = moduleEvents.timedEvents.get(eventName); - validateEvent(timedEvent, eventName, null, 1, null, null); + timedEvent = moduleEvents.timedEvents.get(TestUtils.eKeys[0]); + validateEvent(timedEvent, TestUtils.eKeys[0], null, 1, null, null); } /** * Cancel an event with empty key * "cancelEvent" function should not work with empty key - * in memory , timed events and cache queue not contain it + * in memory, timed events and cache queue not contain it */ @Test public void cancelEvent_emptyKey() { - Config config = TestUtils.getBaseConfig(); - config.enableFeatures(Config.Feature.Events).setEventQueueSizeToSend(4); - init(config); + init(TestUtils.getConfigEvents(4)); validateTimedEventSize(0, 0); Assert.assertFalse(Countly.instance().events().cancelEvent("")); @@ -415,13 +368,11 @@ public void cancelEvent_emptyKey() { /** * Cancel an event with null key * "cancelEvent" function should not work with null key - * in memory , timed events and cache queue not contain it + * in memory, timed events and cache queue not contain it */ @Test public void cancelEvent_nullKey() { - Config config = TestUtils.getBaseConfig(); - config.enableFeatures(Config.Feature.Events).setEventQueueSizeToSend(4); - init(config); + init(TestUtils.getConfigEvents(4)); validateTimedEventSize(0, 0); Assert.assertFalse(Countly.instance().events().cancelEvent(null)); @@ -431,13 +382,11 @@ public void cancelEvent_nullKey() { /** * Cancel a not existing event * "cancelEvent" function should not work with not existing event key - * in memory , timed events and cache queue not contain it + * in memory, timed events and cache queue not contain it */ @Test public void cancelEvent_notExist() { - Config config = TestUtils.getBaseConfig(); - config.enableFeatures(Config.Feature.Events).setEventQueueSizeToSend(4); - init(config); + init(TestUtils.getConfigEvents(4)); validateTimedEventSize(0, 0); Assert.assertFalse(Countly.instance().events().cancelEvent("cancelEvent_notExist")); @@ -447,28 +396,49 @@ public void cancelEvent_notExist() { /** * Cancel an event * "cancelEvent" function should cancel an event - * in memory , timed events and cache queue should contain it + * in memory, timed events and cache queue should contain it */ @Test public void cancelEvent() { - Config config = TestUtils.getBaseConfig(); - config.enableFeatures(Config.Feature.Events).setEventQueueSizeToSend(4); - init(config); + init(TestUtils.getConfigEvents(4)); validateTimedEventSize(0, 0); - String eventName = "cancelEvent"; - startEvent(eventName); // start event to end it + startEvent(TestUtils.eKeys[0]); // start event to end it validateTimedEventSize(0, 1); - EventImpl timedEvent = moduleEvents.timedEvents.get(eventName); - validateEvent(timedEvent, eventName, null, 1, null, null); + EventImpl timedEvent = moduleEvents.timedEvents.get(TestUtils.eKeys[0]); + validateEvent(timedEvent, TestUtils.eKeys[0], null, 1, null, null); - Assert.assertTrue(Countly.instance().events().cancelEvent(eventName)); + Assert.assertTrue(Countly.instance().events().cancelEvent(TestUtils.eKeys[0])); Assert.assertEquals(0, moduleEvents.timedEvents.size()); validateQueueSize(0); } + @Test + public void timedEventFlow() throws InterruptedException { + init(TestUtils.getConfigEvents(4)); + validateTimedEventSize(0, 0); + + startEvent(TestUtils.eKeys[0]); // start event to end it + validateTimedEventSize(0, 1); + + Thread.sleep(1000); + startEvent(TestUtils.eKeys[1]); // start event to end it + validateTimedEventSize(0, 2); + + Thread.sleep(1000); + endEvent(TestUtils.eKeys[1], null, 3, 15.0); + + Assert.assertEquals(1, moduleEvents.timedEvents.size()); + validateEventInEventQueue(TestUtils.getTestSDirectory(), TestUtils.eKeys[1], null, 3, 15.0, 1.0, 1, 0); + + endEvent(TestUtils.eKeys[0], null, 2, 4.0); + + Assert.assertEquals(0, moduleEvents.timedEvents.size()); + validateEventInEventQueue(TestUtils.getTestSDirectory(), TestUtils.eKeys[0], null, 2, 4.0, 2.0, 2, 1); + } + private void validateTimedEventSize(int expectedQueueSize, int expectedTimedEventSize) { validateQueueSize(expectedQueueSize, TestUtils.getCurrentEventQueue(moduleEvents.ctx.getContext(), moduleEvents.L)); Assert.assertEquals(expectedTimedEventSize, moduleEvents.timedEvents.size()); @@ -493,14 +463,14 @@ private void startEvent(String key) { Assert.assertTrue(result); } - void validateEventInQueue(File targetFolder, String key, Map segmentation, + void validateEventInEventQueue(File targetFolder, String key, Map segmentation, int count, Double sum, Double duration, int queueSize, int elementInQueue) { List events = TestUtils.getCurrentEventQueue(targetFolder, moduleEvents.L); validateQueueSize(queueSize, events); //check if event was recorded correctly EventImpl event = events.get(elementInQueue); - EventImpl eventInMemory = moduleEvents.eventQueue.eventQueueMemoryCache.get(0); + EventImpl eventInMemory = moduleEvents.eventQueue.eventQueueMemoryCache.get(elementInQueue); validateEvent(event, key, segmentation, count, sum, duration); validateEvent(eventInMemory, key, segmentation, count, sum, duration); } diff --git a/sdk-java/src/test/java/ly/count/sdk/java/internal/TestUtils.java b/sdk-java/src/test/java/ly/count/sdk/java/internal/TestUtils.java index c64eef5e9..07e04ae52 100644 --- a/sdk-java/src/test/java/ly/count/sdk/java/internal/TestUtils.java +++ b/sdk-java/src/test/java/ly/count/sdk/java/internal/TestUtils.java @@ -21,6 +21,8 @@ public class TestUtils { static String SERVER_URL = "https://test.count.ly"; static String SERVER_APP_KEY = "COUNTLY_APP_KEY"; static String DEVICE_ID = "some_random_test_device_id"; + + public static final String[] eKeys = new String[] { "eventKey1", "eventKey2", "eventKey3", "eventKey4", "eventKey5", "eventKey6", "eventKey7" }; private TestUtils() { } @@ -34,6 +36,21 @@ static Config getBaseConfig() { return config; } + static Config getConfigEvents(Integer eventThreshold) { + File sdkStorageRootDirectory = getTestSDirectory(); + checkSdkStorageRootDirectoryExist(sdkStorageRootDirectory); + Config config = new Config(SERVER_URL, SERVER_APP_KEY, sdkStorageRootDirectory); + config.setCustomDeviceId(DEVICE_ID); + + config.enableFeatures(Config.Feature.Events); + + if (eventThreshold != null) { + config.setEventQueueSizeToSend(eventThreshold); + } + + return config; + } + public static File getTestSDirectory() { // System specific folder structure String[] sdkStorageRootPath = { System.getProperty("user.home"), "__COUNTLY", "java_test" }; From a8314c12dab90dc75d24364ec1e64b625cf9b74c Mon Sep 17 00:00:00 2001 From: ArtursK Date: Thu, 28 Sep 2023 13:59:43 +0300 Subject: [PATCH 82/82] Refactoring tests --- .../count/sdk/java/internal/EventQueue.java | 17 ++++--- .../sdk/java/internal/EventQueueTests.java | 51 ++++++------------- .../sdk/java/internal/ModuleEventsTests.java | 15 +++++- .../ly/count/sdk/java/internal/TestUtils.java | 7 ++- 4 files changed, 45 insertions(+), 45 deletions(-) diff --git a/sdk-java/src/main/java/ly/count/sdk/java/internal/EventQueue.java b/sdk-java/src/main/java/ly/count/sdk/java/internal/EventQueue.java index dacf02a72..ececc5622 100644 --- a/sdk-java/src/main/java/ly/count/sdk/java/internal/EventQueue.java +++ b/sdk-java/src/main/java/ly/count/sdk/java/internal/EventQueue.java @@ -1,5 +1,7 @@ package ly.count.sdk.java.internal; +import ly.count.sdk.java.Countly; +import javax.annotation.Nonnull; import java.util.ArrayList; import java.util.Collection; import java.util.List; @@ -7,10 +9,13 @@ public class EventQueue { static final String DELIMITER = ":::"; - private final Log L; - final List eventQueueMemoryCache; + Log L; + List eventQueueMemoryCache; - protected EventQueue(Log logger, int eventThreshold) { + protected EventQueue() { + } + + protected EventQueue(@Nonnull Log logger, int eventThreshold) { L = logger; eventQueueMemoryCache = new ArrayList<>(eventThreshold); } @@ -22,7 +27,7 @@ protected int eqSize() { return eventQueueMemoryCache.size(); } - void addEvent(final EventImpl event) { + void addEvent(@Nonnull final EventImpl event) { if (event == null) { L.w("[EventQueue] Event is null, skipping"); return; @@ -67,7 +72,7 @@ void restoreFromDisk() { eventQueueMemoryCache.sort((e1, e2) -> (int) (e1.timestamp - e2.timestamp)); } - String joinEvents(final Collection collection) { + @Nonnull String joinEvents(@Nonnull final Collection collection) { final List strings = new ArrayList<>(); for (EventImpl e : collection) { strings.add(e.toJSON(L)); @@ -78,7 +83,7 @@ String joinEvents(final Collection collection) { /** * Returns an unsorted array of the current stored event JSON strings. */ - private synchronized String[] getEvents() { + private synchronized @Nonnull String[] getEvents() { L.d("[EventQueue] Getting events from disk"); final String joinedEventsStr = SDKCore.instance.sdkStorage.readEventQueue(); return joinedEventsStr.isEmpty() ? new String[0] : joinedEventsStr.split(DELIMITER); diff --git a/sdk-java/src/test/java/ly/count/sdk/java/internal/EventQueueTests.java b/sdk-java/src/test/java/ly/count/sdk/java/internal/EventQueueTests.java index 19cf115f5..f6481f77e 100644 --- a/sdk-java/src/test/java/ly/count/sdk/java/internal/EventQueueTests.java +++ b/sdk-java/src/test/java/ly/count/sdk/java/internal/EventQueueTests.java @@ -21,6 +21,7 @@ import static org.mockito.ArgumentMatchers.anyCollection; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; +import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; @RunWith(JUnit4.class) @@ -48,15 +49,11 @@ public void stop() { */ @Test public void addEvent() { - Config config = TestUtils.getBaseConfig(); - config.setEventQueueSizeToSend(2); - init(config); + init(TestUtils.getConfigEvents(2)); validateQueueSize(0); EventImpl event = createEvent("test-addEvent", null, 1, null, null); eventQueue.addEvent(event); - validateQueueSize(1); - validateEventInQueue(event.key, null, 1, null, null, 1, 0); } @@ -67,9 +64,7 @@ public void addEvent() { */ @Test public void addEvent_null() { - Config config = TestUtils.getBaseConfig(); - config.setEventQueueSizeToSend(2); - init(config); + init(TestUtils.getConfigEvents(2)); validateQueueSize(0); eventQueue.addEvent(null); @@ -83,9 +78,7 @@ public void addEvent_null() { */ @Test public void writeEventQueueToStorage() { - Config config = TestUtils.getBaseConfig(); - config.setEventQueueSizeToSend(2); - init(config); + init(TestUtils.getConfigEvents(2)); validateQueueSize(0); EventImpl event = createEvent("test-writeEventQueueToStorage", null, 1, null, null); @@ -101,7 +94,9 @@ public void writeEventQueueToStorage() { */ @Test public void writeEventQueueToStorage_emptyCache() { - eventQueue = mock(EventQueue.class); + eventQueue = spy(EventQueue.class); + eventQueue.L = mock(Log.class); + eventQueue.eventQueueMemoryCache = new ArrayList<>(); eventQueue.writeEventQueueToStorage(); verify(eventQueue, never()).joinEvents(anyCollection()); @@ -114,9 +109,7 @@ public void writeEventQueueToStorage_emptyCache() { */ @Test public void joinEvents() { - Config config = TestUtils.getBaseConfig(); - config.setEventQueueSizeToSend(2); - init(config); + init(TestUtils.getConfigEvents(2)); List list = new ArrayList<>(); list.add(createEvent("test-joinEvents-1", null, 1, null, null)); @@ -135,9 +128,7 @@ public void joinEvents() { */ @Test public void joinEvents_emptyCollection() { - Config config = TestUtils.getBaseConfig(); - config.setEventQueueSizeToSend(2); - init(config); + init(TestUtils.getConfigEvents(2)); List list = new ArrayList<>(); @@ -152,9 +143,7 @@ public void joinEvents_emptyCollection() { */ @Test(expected = NullPointerException.class) public void joinEvents_nullCollection() { - Config config = TestUtils.getBaseConfig(); - config.setEventQueueSizeToSend(2); - init(config); + init(TestUtils.getConfigEvents(2)); eventQueue.joinEvents(null); } @@ -166,9 +155,7 @@ public void joinEvents_nullCollection() { */ @Test public void clear() { - Config config = TestUtils.getBaseConfig(); - config.setEventQueueSizeToSend(2); - init(config); + init(TestUtils.getConfigEvents(2)); validateQueueSize(0); @@ -188,9 +175,7 @@ public void clear() { */ @Test public void restoreFromDisk() throws IOException { - Config config = TestUtils.getBaseConfig(); - config.setEventQueueSizeToSend(2); - init(config); + init(TestUtils.getConfigEvents(2)); validateQueueSize(0); writeToEventQueue("{\"hour\":10,\"count\":1,\"dow\":4,\"key\":\"test-joinEvents-1\",\"timestamp\":1695887006647}:::{\"hour\":10,\"count\":1,\"dow\":4,\"key\":\"test-joinEvents-2\",\"timestamp\":1695887006657}", false); @@ -208,9 +193,7 @@ public void restoreFromDisk() throws IOException { */ @Test public void restoreFromDisk_notExist() throws IOException { - Config config = TestUtils.getBaseConfig(); - config.setEventQueueSizeToSend(2); - init(config); + init(TestUtils.getConfigEvents(2)); validateQueueSize(0); writeToEventQueue(null, true); @@ -226,9 +209,7 @@ public void restoreFromDisk_notExist() throws IOException { */ @Test public void restoreFromDisk_garbageFile() throws IOException { - Config config = TestUtils.getBaseConfig(); - config.setEventQueueSizeToSend(2); - init(config); + init(TestUtils.getConfigEvents(2)); validateQueueSize(0); writeToEventQueue("{\"hour\":10,\"asdasd\":\"askjdn\",\"timestamp\":1695887006647}::{\"hour\":10,\"count\":1,\"dow\":4,\"asda\":\"test-joinEvents-2\"}", false); @@ -244,9 +225,7 @@ public void restoreFromDisk_garbageFile() throws IOException { */ @Test public void restoreFromDisk_corruptedData() throws IOException { - Config config = TestUtils.getBaseConfig(); - config.setEventQueueSizeToSend(2); - init(config); + init(TestUtils.getConfigEvents(2)); validateQueueSize(0); writeToEventQueue("{\"hour\":10,\"count\":1,\"dow\":4,\"key\":\"test-joinEvents-1\",\"timestamp\":1695887006647}:::{\"hour\":10,\"count\":1,\"dow\":4,\"keya\":\"test-joinEvents-2\",\"timestamp\":1695887006657}", false); diff --git a/sdk-java/src/test/java/ly/count/sdk/java/internal/ModuleEventsTests.java b/sdk-java/src/test/java/ly/count/sdk/java/internal/ModuleEventsTests.java index e4c360361..f9ce2be44 100644 --- a/sdk-java/src/test/java/ly/count/sdk/java/internal/ModuleEventsTests.java +++ b/sdk-java/src/test/java/ly/count/sdk/java/internal/ModuleEventsTests.java @@ -65,12 +65,18 @@ public void recordEvent_queueSizeOver() { init(TestUtils.getConfigEvents(2)); validateQueueSize(0); + Assert.assertEquals(0, TestUtils.getCurrentRequestQueue().length); - Countly.instance().events().recordEvent("recordEvent_queueSizeOver", 1, 45.9, null, 32.0); + Countly.instance().events().recordEvent("recordEvent_queueSizeOver1", 1, 45.9, null, 32.0); validateQueueSize(1); + Assert.assertEquals(0, TestUtils.getCurrentRequestQueue().length); - Countly.instance().events().recordEvent("recordEvent_queueSizeOver", 1, 45.9, null, 32.0); + Countly.instance().events().recordEvent("recordEvent_queueSizeOver2", 1, 45.9, null, 32.0); validateQueueSize(0); + Assert.assertEquals(1, TestUtils.getCurrentRequestQueue().length); + + Map request = TestUtils.getCurrentRequestQueue()[0]; + Assert.assertTrue(request.get("events").contains("recordEvent_queueSizeOver1") && request.get("events").contains("recordEvent_queueSizeOver2")); } /** @@ -83,9 +89,14 @@ public void recordEvent_queueSizeOverMemory() throws IOException { EventQueueTests.writeToEventQueue("{\"hour\":10,\"count\":1,\"dow\":4,\"key\":\"test-joinEvents-1\",\"timestamp\":1695887006647}:::{\"hour\":10,\"count\":1,\"dow\":4,\"key\":\"test-joinEvents-2\",\"timestamp\":1695887006657}", false); init(TestUtils.getConfigEvents(2)); + Assert.assertEquals(0, TestUtils.getCurrentRequestQueue().length); validateQueueSize(2); Countly.instance().events().recordEvent("recordEvent_queueSizeOver", 1, 45.9, null, 32.0); validateQueueSize(0); + Assert.assertEquals(1, TestUtils.getCurrentRequestQueue().length); + + Map request = TestUtils.getCurrentRequestQueue()[0]; + Assert.assertTrue(request.get("events").contains("recordEvent_queueSizeOver") && request.get("events").contains("test-joinEvents-1") && request.get("events").contains("test-joinEvents-2")); } /** diff --git a/sdk-java/src/test/java/ly/count/sdk/java/internal/TestUtils.java b/sdk-java/src/test/java/ly/count/sdk/java/internal/TestUtils.java index 07e04ae52..75826c5c5 100644 --- a/sdk-java/src/test/java/ly/count/sdk/java/internal/TestUtils.java +++ b/sdk-java/src/test/java/ly/count/sdk/java/internal/TestUtils.java @@ -16,12 +16,13 @@ import static ly.count.sdk.java.internal.SDKStorage.EVENT_QUEUE_FILE_NAME; import static ly.count.sdk.java.internal.SDKStorage.FILE_NAME_PREFIX; import static ly.count.sdk.java.internal.SDKStorage.FILE_NAME_SEPARATOR; +import static org.mockito.Mockito.mock; public class TestUtils { static String SERVER_URL = "https://test.count.ly"; static String SERVER_APP_KEY = "COUNTLY_APP_KEY"; static String DEVICE_ID = "some_random_test_device_id"; - + public static final String[] eKeys = new String[] { "eventKey1", "eventKey2", "eventKey3", "eventKey4", "eventKey5", "eventKey6", "eventKey7" }; private TestUtils() { @@ -65,6 +66,10 @@ static void checkSdkStorageRootDirectoryExist(File directory) { } } + protected static Map[] getCurrentRequestQueue() { + return getCurrentRequestQueue(getTestSDirectory(), mock(Log.class)); + } + /** * Get current request queue from target folder *