Add test for LauncherWidgetHolder

Number of new tested methods: 8

Test: atest NexusLauncherRoboTests:LauncherWidgetHolderTest
Test: SysUI studio
Flag: TEST_ONLY
Bug: 353303621
Change-Id: I8751c8de8d719e567775368d988794b2d7abc37e
diff --git a/src/com/android/launcher3/widget/LauncherWidgetHolder.java b/src/com/android/launcher3/widget/LauncherWidgetHolder.java
index 1fb8c83..3016c9a 100644
--- a/src/com/android/launcher3/widget/LauncherWidgetHolder.java
+++ b/src/com/android/launcher3/widget/LauncherWidgetHolder.java
@@ -37,6 +37,7 @@
 
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
+import androidx.annotation.VisibleForTesting;
 import androidx.annotation.WorkerThread;
 
 import com.android.launcher3.BaseActivity;
@@ -484,7 +485,8 @@
      * Sets or unsets a flag the can change whether the widget host should be in the listening
      * state.
      */
-    private void setShouldListenFlag(int flag, boolean on) {
+    @VisibleForTesting
+    void setShouldListenFlag(int flag, boolean on) {
         if (on) {
             mFlags.updateAndGet(old -> old | flag);
         } else {
diff --git a/tests/multivalentTests/src/com/android/launcher3/widget/LauncherWidgetHolderTest.kt b/tests/multivalentTests/src/com/android/launcher3/widget/LauncherWidgetHolderTest.kt
new file mode 100644
index 0000000..1a659e2
--- /dev/null
+++ b/tests/multivalentTests/src/com/android/launcher3/widget/LauncherWidgetHolderTest.kt
@@ -0,0 +1,149 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * 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.android.launcher3.widget
+
+import androidx.test.annotation.UiThreadTest
+import androidx.test.filters.SmallTest
+import androidx.test.platform.app.InstrumentationRegistry.getInstrumentation
+import com.android.launcher3.BuildConfig.WIDGETS_ENABLED
+import com.android.launcher3.util.ActivityContextWrapper
+import com.android.launcher3.util.LauncherMultivalentJUnit
+import com.android.launcher3.widget.LauncherWidgetHolder.FLAG_ACTIVITY_RESUMED
+import com.android.launcher3.widget.LauncherWidgetHolder.FLAG_ACTIVITY_STARTED
+import com.android.launcher3.widget.LauncherWidgetHolder.FLAG_STATE_IS_NORMAL
+import org.junit.After
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertFalse
+import org.junit.Assert.assertSame
+import org.junit.Assert.assertTrue
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mockito.mock
+import org.mockito.Mockito.times
+import org.mockito.Mockito.verify
+import org.mockito.kotlin.doReturn
+import org.mockito.kotlin.whenever
+
+@SmallTest
+@RunWith(LauncherMultivalentJUnit::class)
+class LauncherWidgetHolderTest {
+    private lateinit var widgetHolder: LauncherWidgetHolder
+
+    @Before
+    fun setUp() {
+        assertTrue(WIDGETS_ENABLED)
+        widgetHolder =
+            LauncherWidgetHolder(ActivityContextWrapper(getInstrumentation().targetContext)) {}
+    }
+
+    @After
+    fun tearDown() {
+        widgetHolder.destroy()
+    }
+
+    @Test
+    fun widget_holder_start_listening() {
+        val testView = mock(PendingAppWidgetHostView::class.java)
+        widgetHolder.mViews[0] = testView
+        widgetHolder.setListeningFlag(false)
+        assertFalse(widgetHolder.isListening)
+        widgetHolder.startListening()
+        widgetHolder.widgetHolderExecutor.submit {}.get()
+        getInstrumentation().waitForIdleSync()
+        assertTrue(widgetHolder.isListening)
+        verify(testView, times(1)).reInflate()
+        widgetHolder.clearWidgetViews()
+    }
+
+    @Test
+    fun holder_start_listening_after_activity_start() {
+        widgetHolder.setShouldListenFlag(FLAG_STATE_IS_NORMAL or FLAG_ACTIVITY_RESUMED, true)
+        widgetHolder.setActivityStarted(false)
+        widgetHolder.widgetHolderExecutor.submit {}.get()
+        assertFalse(widgetHolder.shouldListen(widgetHolder.mFlags.get()))
+        widgetHolder.setActivityStarted(true)
+        widgetHolder.widgetHolderExecutor.submit {}.get()
+        assertTrue(widgetHolder.shouldListen(widgetHolder.mFlags.get()))
+    }
+
+    @Test
+    fun holder_start_listening_after_activity_resume() {
+        widgetHolder.setShouldListenFlag(FLAG_STATE_IS_NORMAL or FLAG_ACTIVITY_STARTED, true)
+        widgetHolder.setActivityResumed(false)
+        widgetHolder.widgetHolderExecutor.submit {}.get()
+        assertFalse(widgetHolder.shouldListen(widgetHolder.mFlags.get()))
+        widgetHolder.setActivityResumed(true)
+        widgetHolder.widgetHolderExecutor.submit {}.get()
+        assertTrue(widgetHolder.shouldListen(widgetHolder.mFlags.get()))
+    }
+
+    @Test
+    fun holder_start_listening_after_state_normal() {
+        widgetHolder.setShouldListenFlag(FLAG_ACTIVITY_RESUMED or FLAG_ACTIVITY_STARTED, true)
+        widgetHolder.setStateIsNormal(false)
+        widgetHolder.widgetHolderExecutor.submit {}.get()
+        assertFalse(widgetHolder.shouldListen(widgetHolder.mFlags.get()))
+        widgetHolder.setStateIsNormal(true)
+        widgetHolder.widgetHolderExecutor.submit {}.get()
+        assertTrue(widgetHolder.shouldListen(widgetHolder.mFlags.get()))
+    }
+
+    @Test
+    @UiThreadTest
+    fun widget_holder_create_view() {
+        val mockProviderInfo = mock(LauncherAppWidgetProviderInfo::class.java)
+        doReturn(false).whenever(mockProviderInfo).isCustomWidget
+        assertEquals(0, widgetHolder.mViews.size())
+        widgetHolder.createView(APP_WIDGET_ID, mockProviderInfo)
+        assertEquals(1, widgetHolder.mViews.size())
+        assertEquals(APP_WIDGET_ID, widgetHolder.mViews.get(0).appWidgetId)
+        widgetHolder.deleteAppWidgetId(APP_WIDGET_ID)
+        assertEquals(0, widgetHolder.mViews.size())
+    }
+
+    @Test
+    fun holder_add_provider_change_listener() {
+        val listener = LauncherWidgetHolder.ProviderChangedListener {}
+        widgetHolder.addProviderChangeListener(listener)
+        getInstrumentation().waitForIdleSync()
+        assertEquals(1, widgetHolder.mProviderChangedListeners.size)
+        assertSame(widgetHolder.mProviderChangedListeners.first(), listener)
+        widgetHolder.removeProviderChangeListener(listener)
+    }
+
+    @Test
+    fun holder_remove_provider_change_listener() {
+        val listener = LauncherWidgetHolder.ProviderChangedListener {}
+        widgetHolder.addProviderChangeListener(listener)
+        widgetHolder.removeProviderChangeListener(listener)
+        getInstrumentation().waitForIdleSync()
+        assertEquals(0, widgetHolder.mProviderChangedListeners.size)
+    }
+
+    @Test
+    fun widget_holder_stop_listening() {
+        widgetHolder.setListeningFlag(true)
+        assertTrue(widgetHolder.isListening)
+        widgetHolder.stopListening()
+        widgetHolder.widgetHolderExecutor.submit {}.get()
+        assertFalse(widgetHolder.isListening)
+    }
+
+    companion object {
+        private const val APP_WIDGET_ID = 0
+    }
+}