Skip to content

Commit cd98f99

Browse files
committed
test cases
1 parent af09a9e commit cd98f99

File tree

3 files changed

+418
-0
lines changed

3 files changed

+418
-0
lines changed
Lines changed: 219 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,219 @@
1+
package com.pluto
2+
3+
import android.app.Application
4+
import android.content.Context
5+
import android.content.Intent
6+
import android.os.Bundle
7+
import com.pluto.core.notch.Notch
8+
import com.pluto.plugin.Plugin
9+
import com.pluto.plugin.PluginGroup
10+
import com.pluto.plugin.PluginManager
11+
import com.pluto.plugin.libinterface.NotificationInterface.Companion.BUNDLE_LABEL
12+
import com.pluto.plugin.libinterface.NotificationInterface.Companion.ID_LABEL
13+
import com.pluto.ui.container.PlutoActivity
14+
import com.pluto.ui.selector.SelectorActivity
15+
import org.junit.Before
16+
import org.junit.Test
17+
import org.junit.runner.RunWith
18+
import org.mockito.ArgumentCaptor
19+
import org.mockito.ArgumentMatchers.any
20+
import org.mockito.Mock
21+
import org.mockito.Mockito.`when`
22+
import org.mockito.Mockito.mock
23+
import org.mockito.Mockito.never
24+
import org.mockito.Mockito.times
25+
import org.mockito.Mockito.verify
26+
import org.mockito.MockitoAnnotations
27+
import org.robolectric.RobolectricTestRunner
28+
import org.robolectric.annotation.Config
29+
30+
@RunWith(RobolectricTestRunner::class)
31+
@Config(manifest = Config.NONE)
32+
class PlutoTest {
33+
34+
@Mock
35+
private lateinit var mockApplication: Application
36+
37+
@Mock
38+
private lateinit var mockContext: Context
39+
40+
@Mock
41+
private lateinit var mockPluginManager: PluginManager
42+
43+
@Mock
44+
private lateinit var mockPlugin: Plugin
45+
46+
@Before
47+
fun setup() {
48+
MockitoAnnotations.openMocks(this)
49+
`when`(mockApplication.applicationContext).thenReturn(mockContext)
50+
51+
// Initialize Pluto with required fields for testing
52+
// Since Pluto is an object (singleton), we need to use reflection to set its state
53+
54+
// First initialize the callbacks
55+
val initCallbacksMethod = Pluto::class.java.getDeclaredMethod("initialiseCallbacks")
56+
initCallbacksMethod.isAccessible = true
57+
initCallbacksMethod.invoke(Pluto)
58+
59+
// Set the application field
60+
val applicationField = Pluto::class.java.getDeclaredField("application")
61+
applicationField.isAccessible = true
62+
applicationField.set(Pluto, mockApplication)
63+
64+
// Set the pluginManager field
65+
val pluginManagerField = Pluto::class.java.getDeclaredField("pluginManager")
66+
pluginManagerField.isAccessible = true
67+
pluginManagerField.set(Pluto, mockPluginManager)
68+
}
69+
70+
@Test
71+
fun `installer should add plugins and install Pluto`() {
72+
// Given
73+
val installer = Pluto.Installer(mockApplication)
74+
val mockPlugin = mock(Plugin::class.java)
75+
val mockPluginGroup = mock(PluginGroup::class.java)
76+
77+
// We need to access the init method since it's called by the installer
78+
val initMethod = Pluto::class.java.getDeclaredMethod("init", Application::class.java, LinkedHashSet::class.java)
79+
initMethod.isAccessible = true
80+
81+
// When
82+
installer.addPlugin(mockPlugin)
83+
.addPluginGroup(mockPluginGroup)
84+
85+
// Instead of calling install() which would call the real init method,
86+
// we'll verify that the plugins were added correctly
87+
val pluginsField = installer.javaClass.getDeclaredField("plugins")
88+
pluginsField.isAccessible = true
89+
val plugins = pluginsField.get(installer) as LinkedHashSet<*>
90+
91+
// Then
92+
assert(plugins.size == 2)
93+
assert(plugins.contains(mockPlugin))
94+
assert(plugins.contains(mockPluginGroup))
95+
}
96+
97+
@Test
98+
fun `open should start SelectorActivity when identifier is null`() {
99+
// Given
100+
val intentCaptor = ArgumentCaptor.forClass(Intent::class.java)
101+
102+
// When
103+
Pluto.open()
104+
105+
// Then
106+
verify(mockContext).startActivity(intentCaptor.capture())
107+
val capturedIntent = intentCaptor.value
108+
assert(capturedIntent.component?.className == SelectorActivity::class.java.name)
109+
assert(capturedIntent.flags and Intent.FLAG_ACTIVITY_NEW_TASK != 0)
110+
}
111+
112+
@Test
113+
fun `open should start PlutoActivity when valid identifier is provided`() {
114+
// Given
115+
val intentCaptor = ArgumentCaptor.forClass(Intent::class.java)
116+
val testIdentifier = "test_plugin"
117+
val testBundle = Bundle()
118+
`when`(mockPluginManager.get(testIdentifier)).thenReturn(mockPlugin)
119+
120+
// When
121+
Pluto.open(testIdentifier, testBundle)
122+
123+
// Then
124+
verify(mockContext).startActivity(intentCaptor.capture())
125+
val capturedIntent = intentCaptor.value
126+
assert(capturedIntent.component?.className == PlutoActivity::class.java.name)
127+
assert(capturedIntent.getStringExtra(ID_LABEL) == testIdentifier)
128+
assert(capturedIntent.getBundleExtra(BUNDLE_LABEL) == testBundle)
129+
assert(capturedIntent.flags and Intent.FLAG_ACTIVITY_NEW_TASK != 0)
130+
assert(capturedIntent.flags and Intent.FLAG_ACTIVITY_CLEAR_TOP != 0)
131+
assert(capturedIntent.flags and Intent.FLAG_ACTIVITY_MULTIPLE_TASK != 0)
132+
}
133+
134+
@Test
135+
fun `open should show toast when invalid identifier is provided`() {
136+
// Given
137+
val testIdentifier = "invalid_plugin"
138+
`when`(mockPluginManager.get(testIdentifier)).thenReturn(null)
139+
140+
// When
141+
Pluto.open(testIdentifier)
142+
143+
// Then
144+
// We can't easily verify toast messages in Robolectric tests,
145+
// but we can verify that startActivity was not called
146+
verify(mockContext, never()).startActivity(any())
147+
}
148+
149+
@Test
150+
fun `clearLogs should call pluginManager clearLogs with null when no identifier is provided`() {
151+
// When
152+
Pluto.clearLogs()
153+
154+
// Then
155+
verify(mockPluginManager).clearLogs(null)
156+
}
157+
158+
@Test
159+
fun `clearLogs should call pluginManager clearLogs with identifier when provided`() {
160+
// Given
161+
val testIdentifier = "test_plugin"
162+
163+
// When
164+
Pluto.clearLogs(testIdentifier)
165+
166+
// Then
167+
verify(mockPluginManager).clearLogs(testIdentifier)
168+
}
169+
170+
@Test
171+
fun `showNotch should enable or disable notch based on state parameter`() {
172+
// Given
173+
val mockNotch = mock(Notch::class.java)
174+
val field = Pluto::class.java.getDeclaredField("notch")
175+
field.isAccessible = true
176+
field.set(Pluto, mockNotch)
177+
178+
// When - enable notch
179+
Pluto.showNotch(true)
180+
181+
// Then
182+
verify(mockNotch).enable(true)
183+
184+
// When - disable notch
185+
Pluto.showNotch(false)
186+
187+
// Then
188+
verify(mockNotch).enable(false)
189+
}
190+
191+
@Test
192+
fun `initialiseCallbacks should initialize all required callbacks`() {
193+
// Given
194+
// Use reflection to access the private method
195+
val method = Pluto::class.java.getDeclaredMethod("initialiseCallbacks")
196+
method.isAccessible = true
197+
198+
// When
199+
method.invoke(Pluto)
200+
201+
// Then
202+
// Verify that all callbacks are initialized by checking they're not null
203+
val resetDataCallbackField = Pluto::class.java.getDeclaredField("resetDataCallback")
204+
resetDataCallbackField.isAccessible = true
205+
assert(resetDataCallbackField.get(Pluto) != null)
206+
207+
val appStateCallbackField = Pluto::class.java.getDeclaredField("appStateCallback")
208+
appStateCallbackField.isAccessible = true
209+
assert(appStateCallbackField.get(Pluto) != null)
210+
211+
val selectorStateCallbackField = Pluto::class.java.getDeclaredField("selectorStateCallback")
212+
selectorStateCallbackField.isAccessible = true
213+
assert(selectorStateCallbackField.get(Pluto) != null)
214+
215+
val notchStateCallbackField = Pluto::class.java.getDeclaredField("notchStateCallback")
216+
notchStateCallbackField.isAccessible = true
217+
assert(notchStateCallbackField.get(Pluto) != null)
218+
}
219+
}
Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
package com.pluto.core.applifecycle
2+
3+
import android.app.Activity
4+
import android.os.Bundle
5+
import androidx.arch.core.executor.testing.InstantTaskExecutorRule
6+
import androidx.lifecycle.Observer
7+
import org.junit.Before
8+
import org.junit.Rule
9+
import org.junit.Test
10+
import org.junit.runner.RunWith
11+
import org.mockito.Mock
12+
import org.mockito.Mockito.mock
13+
import org.mockito.Mockito.never
14+
import org.mockito.Mockito.times
15+
import org.mockito.Mockito.verify
16+
import org.mockito.MockitoAnnotations
17+
import org.robolectric.RobolectricTestRunner
18+
import org.robolectric.annotation.Config
19+
20+
@RunWith(RobolectricTestRunner::class)
21+
@Config(manifest = Config.NONE)
22+
class AppLifecycleTest {
23+
24+
@get:Rule
25+
val instantTaskExecutorRule = InstantTaskExecutorRule()
26+
27+
private lateinit var appLifecycle: AppLifecycle
28+
private lateinit var appStateCallback: AppStateCallback
29+
30+
@Mock
31+
private lateinit var stateObserver: Observer<AppStateCallback.State>
32+
33+
@Mock
34+
private lateinit var mockActivity: Activity
35+
36+
@Before
37+
fun setup() {
38+
MockitoAnnotations.openMocks(this)
39+
appStateCallback = AppStateCallback()
40+
appStateCallback.state.observeForever(stateObserver)
41+
appLifecycle = AppLifecycle(appStateCallback)
42+
}
43+
44+
@Test
45+
fun `when first activity starts, app state should change to Foreground`() {
46+
// When
47+
appLifecycle.onActivityStarted(mockActivity)
48+
49+
// Then
50+
verify(stateObserver).onChanged(AppStateCallback.State.Foreground)
51+
}
52+
53+
@Test
54+
fun `when second activity starts, app state should remain Foreground without additional updates`() {
55+
// Given
56+
appLifecycle.onActivityStarted(mockActivity) // First activity starts
57+
verify(stateObserver).onChanged(AppStateCallback.State.Foreground)
58+
59+
// When
60+
val secondActivity = mock(Activity::class.java)
61+
appLifecycle.onActivityStarted(secondActivity) // Second activity starts
62+
63+
// Then - verify observer was only called once with Foreground (from the first activity)
64+
verify(stateObserver).onChanged(AppStateCallback.State.Foreground)
65+
}
66+
67+
@Test
68+
fun `when one activity stops but another is still running, app state should remain Foreground`() {
69+
// Given
70+
val secondActivity = mock(Activity::class.java)
71+
appLifecycle.onActivityStarted(mockActivity) // First activity starts
72+
appLifecycle.onActivityStarted(secondActivity) // Second activity starts
73+
74+
// When
75+
appLifecycle.onActivityStopped(mockActivity) // First activity stops
76+
77+
// Then
78+
verify(stateObserver, never()).onChanged(AppStateCallback.State.Background)
79+
}
80+
81+
@Test
82+
fun `when all activities stop, app state should change to Background`() {
83+
// Given
84+
appLifecycle.onActivityStarted(mockActivity) // Activity starts
85+
86+
// When
87+
appLifecycle.onActivityStopped(mockActivity) // Activity stops
88+
89+
// Then
90+
verify(stateObserver).onChanged(AppStateCallback.State.Background)
91+
}
92+
93+
@Test
94+
fun `when app goes to background and then foreground, both state changes should be observed`() {
95+
// Given - app starts in foreground
96+
appLifecycle.onActivityStarted(mockActivity)
97+
verify(stateObserver).onChanged(AppStateCallback.State.Foreground)
98+
99+
// When - app goes to background
100+
appLifecycle.onActivityStopped(mockActivity)
101+
verify(stateObserver).onChanged(AppStateCallback.State.Background)
102+
103+
// When - app comes back to foreground
104+
appLifecycle.onActivityStarted(mockActivity)
105+
106+
// Then - verify Foreground state was observed again
107+
verify(stateObserver, times(2)).onChanged(AppStateCallback.State.Foreground)
108+
}
109+
110+
@Test
111+
fun `other lifecycle methods should not affect app state`() {
112+
// Given
113+
val bundle = mock(Bundle::class.java)
114+
115+
// When
116+
appLifecycle.onActivityCreated(mockActivity, bundle)
117+
appLifecycle.onActivityResumed(mockActivity)
118+
appLifecycle.onActivityPaused(mockActivity)
119+
appLifecycle.onActivitySaveInstanceState(mockActivity, bundle)
120+
appLifecycle.onActivityDestroyed(mockActivity)
121+
122+
// Then
123+
verify(stateObserver, never()).onChanged(AppStateCallback.State.Foreground)
124+
verify(stateObserver, never()).onChanged(AppStateCallback.State.Background)
125+
}
126+
}

0 commit comments

Comments
 (0)