|
1 | 1 | package com.maxmind.device.collector |
2 | 2 |
|
3 | | -import android.app.ActivityManager |
4 | 3 | import android.content.Context |
5 | | -import android.content.pm.PackageInfo |
6 | | -import android.content.pm.PackageManager |
7 | | -import android.os.Environment |
8 | | -import android.os.StatFs |
9 | | -import android.provider.Settings |
10 | | -import android.util.DisplayMetrics |
11 | | -import android.view.Display |
12 | | -import android.view.WindowManager |
13 | | -import io.mockk.every |
14 | 4 | import io.mockk.mockk |
15 | | -import io.mockk.mockkStatic |
16 | | -import io.mockk.slot |
17 | | -import io.mockk.unmockkStatic |
18 | | -import org.junit.jupiter.api.AfterEach |
19 | 5 | import org.junit.jupiter.api.Assertions.assertNotNull |
20 | | -import org.junit.jupiter.api.Assertions.assertTrue |
21 | | -import org.junit.jupiter.api.BeforeEach |
22 | | -import org.junit.jupiter.api.Disabled |
23 | 6 | import org.junit.jupiter.api.Test |
24 | | -import java.io.File |
25 | 7 |
|
26 | 8 | /** |
27 | 9 | * Tests for DeviceDataCollector. |
28 | 10 | * |
29 | | - * Note: Most tests require Android framework classes (Build.*, Typeface, etc.) |
30 | | - * that cannot be mocked in pure unit tests. These tests are disabled and should |
31 | | - * be run in instrumented tests on real devices. |
32 | | - * |
33 | | - * Uses MockK to mock Android framework classes and services. |
| 11 | + * Full integration testing of collect() requires instrumented tests |
| 12 | + * due to Android framework static fields (Build.*, etc.). |
34 | 13 | */ |
35 | 14 | internal class DeviceDataCollectorTest { |
36 | | - private lateinit var mockContext: Context |
37 | | - private lateinit var mockWindowManager: WindowManager |
38 | | - private lateinit var mockActivityManager: ActivityManager |
39 | | - private lateinit var mockPackageManager: PackageManager |
40 | | - private lateinit var mockDisplay: Display |
41 | | - |
42 | | - @BeforeEach |
43 | | - internal fun setUp() { |
44 | | - mockContext = mockk(relaxed = true) |
45 | | - mockWindowManager = mockk(relaxed = true) |
46 | | - mockActivityManager = mockk(relaxed = true) |
47 | | - mockPackageManager = mockk(relaxed = true) |
48 | | - mockDisplay = mockk(relaxed = true) |
49 | | - |
50 | | - every { mockContext.getSystemService(Context.WINDOW_SERVICE) } returns mockWindowManager |
51 | | - every { mockContext.getSystemService(Context.ACTIVITY_SERVICE) } returns mockActivityManager |
52 | | - every { mockContext.packageManager } returns mockPackageManager |
53 | | - every { mockContext.packageName } returns "com.test.app" |
54 | | - |
55 | | - // Mock display metrics |
56 | | - val metricsSlot = slot<DisplayMetrics>() |
57 | | - @Suppress("DEPRECATION") |
58 | | - every { mockWindowManager.defaultDisplay } returns mockDisplay |
59 | | - @Suppress("DEPRECATION") |
60 | | - every { mockDisplay.getMetrics(capture(metricsSlot)) } answers { |
61 | | - metricsSlot.captured.widthPixels = 1080 |
62 | | - metricsSlot.captured.heightPixels = 1920 |
63 | | - metricsSlot.captured.densityDpi = 480 |
64 | | - metricsSlot.captured.density = 3.0f |
65 | | - } |
66 | | - every { mockDisplay.refreshRate } returns 60.0f |
67 | | - |
68 | | - // Mock memory info |
69 | | - every { mockActivityManager.getMemoryInfo(any()) } answers { |
70 | | - val info = firstArg<ActivityManager.MemoryInfo>() |
71 | | - info.totalMem = 4_000_000_000L |
72 | | - } |
73 | | - |
74 | | - // Mock package info |
75 | | - val packageInfo = PackageInfo() |
76 | | - packageInfo.firstInstallTime = 1700000000000L |
77 | | - packageInfo.lastUpdateTime = 1700100000000L |
78 | | - packageInfo.versionName = "1.0.0" |
79 | | - @Suppress("DEPRECATION") |
80 | | - packageInfo.versionCode = 1 |
81 | | - every { mockPackageManager.getPackageInfo("com.test.app", 0) } returns packageInfo |
82 | | - |
83 | | - // Mock content resolver for DeviceIDsCollector |
84 | | - val mockContentResolver = mockk<android.content.ContentResolver>(relaxed = true) |
85 | | - every { mockContext.contentResolver } returns mockContentResolver |
86 | | - |
87 | | - mockkStatic(Settings.Secure::class) |
88 | | - every { |
89 | | - Settings.Secure.getString(mockContentResolver, Settings.Secure.ANDROID_ID) |
90 | | - } returns "test-android-id" |
91 | | - |
92 | | - // Mock Environment and StatFs for storage |
93 | | - mockkStatic(Environment::class) |
94 | | - every { Environment.getDataDirectory() } returns File("/data") |
95 | | - |
96 | | - mockkStatic(StatFs::class) |
97 | | - } |
98 | | - |
99 | | - @AfterEach |
100 | | - internal fun tearDown() { |
101 | | - unmockkStatic(Settings.Secure::class) |
102 | | - unmockkStatic(Environment::class) |
103 | | - unmockkStatic(StatFs::class) |
104 | | - } |
105 | | - |
106 | | - // Note: The following tests are disabled because DeviceDataCollector.collect() |
107 | | - // accesses Android framework static fields (Build.*, Typeface, etc.) that |
108 | | - // cannot be mocked in pure unit tests without Robolectric. |
109 | | - // |
110 | | - // These should be tested in instrumented tests (androidTest). |
111 | | - |
112 | | - @Test |
113 | | - @Disabled("Requires Android framework - use instrumented tests") |
114 | | - internal fun `collect returns DeviceData with build info`() { |
115 | | - // This test requires Build.FINGERPRINT, etc. which are null in unit tests |
116 | | - } |
117 | | - |
118 | | - @Test |
119 | | - @Disabled("Requires Android framework - use instrumented tests") |
120 | | - internal fun `collect returns DeviceData with display info`() { |
121 | | - // This test requires WindowManager which is mocked but inner calls fail |
122 | | - } |
123 | | - |
124 | | - @Test |
125 | | - @Disabled("Requires Android framework - use instrumented tests") |
126 | | - internal fun `collect returns DeviceData with hardware info`() { |
127 | | - // Requires Android framework |
128 | | - } |
129 | | - |
130 | | - @Test |
131 | | - @Disabled("Requires Android framework - use instrumented tests") |
132 | | - internal fun `collect returns DeviceData with installation info`() { |
133 | | - // Requires Android framework |
134 | | - } |
135 | 15 |
|
136 | 16 | @Test |
137 | | - @Disabled("Requires Android framework - use instrumented tests") |
138 | | - internal fun `collect returns DeviceData with locale info`() { |
139 | | - // Requires Android framework |
140 | | - } |
141 | | - |
142 | | - @Test |
143 | | - @Disabled("Requires Android framework - use instrumented tests") |
144 | | - internal fun `collect returns DeviceData with timezone offset`() { |
145 | | - // Requires Android framework |
146 | | - } |
147 | | - |
148 | | - @Test |
149 | | - @Disabled("Requires Android framework - use instrumented tests") |
150 | | - internal fun `collect returns DeviceData with device time`() { |
151 | | - // Requires Android framework |
152 | | - } |
153 | | - |
154 | | - @Test |
155 | | - @Disabled("Requires Android framework - use instrumented tests") |
156 | | - internal fun `collect returns DeviceData with device IDs`() { |
157 | | - // Requires Android framework |
158 | | - } |
159 | | - |
160 | | - @Test |
161 | | - @Disabled("Requires Android framework - use instrumented tests") |
162 | | - internal fun `build info supportedAbis is not empty`() { |
163 | | - // Requires Build.SUPPORTED_ABIS which is null in unit tests |
164 | | - } |
165 | | - |
166 | | - // Tests that can run without full Android framework |
167 | | - |
168 | | - @Test |
169 | | - internal fun `collector can be instantiated with mock context`() { |
170 | | - // Just verify the collector can be created |
| 17 | + internal fun `collector can be instantiated with context`() { |
| 18 | + val mockContext = mockk<Context>(relaxed = true) |
171 | 19 | val collector = DeviceDataCollector(mockContext) |
172 | 20 | assertNotNull(collector) |
173 | 21 | } |
174 | | - |
175 | | - @Test |
176 | | - internal fun `locale info uses system defaults`() { |
177 | | - // Verify Locale.getDefault() works in unit tests |
178 | | - val locale = java.util.Locale.getDefault() |
179 | | - assertNotNull(locale.language) |
180 | | - } |
181 | | - |
182 | | - @Test |
183 | | - internal fun `timezone offset is within valid range`() { |
184 | | - // Verify TimeZone.getDefault() works in unit tests |
185 | | - val offset = java.util.TimeZone.getDefault().rawOffset / 60000 |
186 | | - assertTrue(offset >= -720) |
187 | | - assertTrue(offset <= 840) |
188 | | - } |
189 | | - |
190 | | - @Test |
191 | | - internal fun `device time is current`() { |
192 | | - // Verify System.currentTimeMillis() works in unit tests |
193 | | - val time = System.currentTimeMillis() |
194 | | - assertTrue(time > 0) |
195 | | - } |
196 | 22 | } |
0 commit comments