From 585f0998f4dd169f9468ba2930f5725f920f42c3 Mon Sep 17 00:00:00 2001 From: Jae Kim <45045038+jaeopt@users.noreply.github.com> Date: Thu, 2 Feb 2023 16:00:50 -0800 Subject: [PATCH] fix(EventRescheduler): catch exception for event service restart (#446) --- .../event_handler/EventReschedulerTest.java | 59 +++++++++------ .../event_handler/EventRescheduler.java | 15 +++- .../DefaultEventHandlerTest.java | 74 ------------------- 3 files changed, 48 insertions(+), 100 deletions(-) rename event-handler/src/{test => androidTest}/java/com/optimizely/ab/android/event_handler/EventReschedulerTest.java (58%) delete mode 100644 event-handler/src/test/java/com/optimizely/ab/android/event_handler/DefaultEventHandlerTest.java diff --git a/event-handler/src/test/java/com/optimizely/ab/android/event_handler/EventReschedulerTest.java b/event-handler/src/androidTest/java/com/optimizely/ab/android/event_handler/EventReschedulerTest.java similarity index 58% rename from event-handler/src/test/java/com/optimizely/ab/android/event_handler/EventReschedulerTest.java rename to event-handler/src/androidTest/java/com/optimizely/ab/android/event_handler/EventReschedulerTest.java index 28f5c904..18236dfa 100644 --- a/event-handler/src/test/java/com/optimizely/ab/android/event_handler/EventReschedulerTest.java +++ b/event-handler/src/androidTest/java/com/optimizely/ab/android/event_handler/EventReschedulerTest.java @@ -1,23 +1,22 @@ -/**************************************************************************** - * Copyright 2016,2021, Optimizely, Inc. and contributors * - * * - * Licensed under the Apache License, Version 2.0 (the "License"); * - * you may not use this file except in compliance with the License. * - * You may obtain a copy of the License at * - * * - * http://www.apache.org/licenses/LICENSE-2.0 * - * * - * Unless required by applicable law or agreed to in writing, software * - * distributed under the License is distributed on an "AS IS" BASIS, * - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * - * See the License for the specific language governing permissions and * - * limitations under the License. * - ***************************************************************************/ +// Copyright 2016,2021,2023, Optimizely, Inc. and contributors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. package com.optimizely.ab.android.event_handler; import android.content.Context; import android.content.Intent; +import android.net.NetworkInfo; import android.net.wifi.WifiManager; import org.junit.Before; @@ -26,15 +25,23 @@ import org.junit.runner.RunWith; import org.mockito.runners.MockitoJUnitRunner; import org.slf4j.Logger; + +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.matches; +import static org.mockito.Mockito.doNothing; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import androidx.test.ext.junit.runners.AndroidJUnit4; + /** * Unit tests for {@link EventRescheduler} */ -@RunWith(MockitoJUnitRunner.class) -@Ignore +@RunWith(AndroidJUnit4.class) public class EventReschedulerTest { private Context context; @@ -47,8 +54,7 @@ public void setupEventRescheduler() { context = mock(Context.class); intent = mock(Intent.class); logger = mock(Logger.class); - rescheduler = mock(EventRescheduler.class); - rescheduler = new EventRescheduler(); + rescheduler = spy(new EventRescheduler()); rescheduler.logger = logger; } @@ -71,6 +77,13 @@ public void onReceiveInvalidAction() { verify(logger).warn("Received unsupported broadcast action to event rescheduler"); } + @Test + public void onReceiveWhenRescheduleWithException() { + when(intent.getAction()).thenThrow(new IllegalStateException()); + rescheduler.onReceive(context, intent); + verify(logger).warn(matches("WorkScheduler failed to reschedule an event service.*")); + } + @Test public void onReceiveValidBootComplete() { when(intent.getAction()).thenReturn(Intent.ACTION_BOOT_COMPLETED); @@ -88,10 +101,12 @@ public void onReceiveValidPackageReplaced() { @Test public void flushOnWifiConnectionIfScheduled() { final Intent eventServiceIntent = mock(Intent.class); - when(intent.getAction()).thenReturn(WifiManager.SUPPLICANT_CONNECTION_CHANGE_ACTION); - when(intent.getBooleanExtra(WifiManager.EXTRA_SUPPLICANT_CONNECTED, false)).thenReturn(true); + when(intent.getAction()).thenReturn(WifiManager.WIFI_STATE_CHANGED_ACTION); + NetworkInfo info = mock(NetworkInfo.class); + when(info.isConnected()).thenReturn(true); + when(intent.getParcelableExtra(WifiManager.EXTRA_NETWORK_INFO)).thenReturn(info); + rescheduler.reschedule(context, intent); - verify(context).startService(eventServiceIntent); verify(logger).info("Preemptively flushing events since wifi became available"); } } diff --git a/event-handler/src/main/java/com/optimizely/ab/android/event_handler/EventRescheduler.java b/event-handler/src/main/java/com/optimizely/ab/android/event_handler/EventRescheduler.java index 65bad758..9393718e 100644 --- a/event-handler/src/main/java/com/optimizely/ab/android/event_handler/EventRescheduler.java +++ b/event-handler/src/main/java/com/optimizely/ab/android/event_handler/EventRescheduler.java @@ -1,5 +1,5 @@ /**************************************************************************** - * Copyright 2016-2021, Optimizely, Inc. and contributors * + * Copyright 2016-2021, 2023 Optimizely, Inc. and contributors * * * * Licensed under the Apache License, Version 2.0 (the "License"); * * you may not use this file except in compliance with the License. * @@ -62,10 +62,17 @@ public class EventRescheduler extends BroadcastReceiver { */ @Override public void onReceive(Context context, Intent intent) { - if (context != null && intent != null) { - reschedule(context, intent); - } else { + if (context == null || intent == null) { logger.warn("Received invalid broadcast to event rescheduler"); + return; + } + + try { + reschedule(context, intent); + } catch (Exception e) { + // Rare exceptions (IllegalStateException: "WorkManager is not initialized properly...") with WorkerScheduler.startService(), probably related to a WorkManager start timing issue. + // Gracefully handled here, and it's safe for those rare cases since event-dispatch service will be scheduled again on next events. + logger.warn("WorkScheduler failed to reschedule an event service: " + e.getMessage()); } } diff --git a/event-handler/src/test/java/com/optimizely/ab/android/event_handler/DefaultEventHandlerTest.java b/event-handler/src/test/java/com/optimizely/ab/android/event_handler/DefaultEventHandlerTest.java deleted file mode 100644 index b3a68c73..00000000 --- a/event-handler/src/test/java/com/optimizely/ab/android/event_handler/DefaultEventHandlerTest.java +++ /dev/null @@ -1,74 +0,0 @@ -/**************************************************************************** - * Copyright 2016, Optimizely, Inc. and contributors * - * * - * Licensed under the Apache License, Version 2.0 (the "License"); * - * you may not use this file except in compliance with the License. * - * You may obtain a copy of the License at * - * * - * http://www.apache.org/licenses/LICENSE-2.0 * - * * - * Unless required by applicable law or agreed to in writing, software * - * distributed under the License is distributed on an "AS IS" BASIS, * - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * - * See the License for the specific language governing permissions and * - * limitations under the License. * - ***************************************************************************/ - -package com.optimizely.ab.android.event_handler; - -import android.content.Context; - -import com.optimizely.ab.event.LogEvent; -import com.optimizely.ab.event.internal.payload.EventBatch; - -import org.junit.Before; -import org.junit.Ignore; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.runners.MockitoJUnitRunner; -import org.slf4j.Logger; - -import java.net.MalformedURLException; -import java.util.HashMap; - -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; - -/** - * Tests for {@link DefaultEventHandler} - */ -@RunWith(MockitoJUnitRunner.class) -@Ignore -public class DefaultEventHandlerTest { - - private Context context; - private Logger logger; - private DefaultEventHandler eventHandler; - private String url = "http://www.foo.com"; - - @Before - public void setupEventHandler() { - context = mock(Context.class); - logger = mock(Logger.class); - eventHandler = DefaultEventHandler.getInstance(context); - eventHandler.logger = logger; - } - - @Test - public void dispatchEventSuccess() throws MalformedURLException { - eventHandler.dispatchEvent(new LogEvent(LogEvent.RequestMethod.POST, url, new HashMap(), new EventBatch())); - verify(logger).info("Sent url {} to the event handler service", "http://www.foo.com"); - } - - @Test - public void dispatchEmptyUrlString() { - eventHandler.dispatchEvent(new LogEvent(LogEvent.RequestMethod.POST, "", new HashMap(), new EventBatch())); - verify(logger).error("Event dispatcher received an empty url"); - } - - @Test - public void dispatchEmptyParams() { - eventHandler.dispatchEvent(new LogEvent(LogEvent.RequestMethod.POST, url, new HashMap(), new EventBatch())); - verify(logger).info("Sent url {} to the event handler service", "http://www.foo.com"); - } -}