Skip to content

Commit fbc248c

Browse files
committed
test: unit test for profile context wrapper
1 parent 4a44f2a commit fbc248c

File tree

1 file changed

+169
-0
lines changed

1 file changed

+169
-0
lines changed
Lines changed: 169 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,169 @@
1+
/*
2+
* Copyright (c) 2025 Ashish Yadav <[email protected]>
3+
*
4+
* This program is free software; you can redistribute it and/or modify it under
5+
* the terms of the GNU General Public License as published by the Free Software
6+
* Foundation; either version 3 of the License, or (at your option) any later
7+
* version.
8+
*
9+
* This program is distributed in the hope that it will be useful, but WITHOUT ANY
10+
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
11+
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
12+
* details.
13+
*
14+
* You should have received a copy of the GNU General Public License along with
15+
* this program. If not, see <http://www.gnu.org/licenses/>.
16+
*/
17+
18+
package com.ichi2.anki.multiprofile
19+
20+
import android.content.Context
21+
import android.content.SharedPreferences
22+
import androidx.test.core.app.ApplicationProvider
23+
import org.junit.Assert.assertEquals
24+
import org.junit.Assert.assertTrue
25+
import org.junit.Before
26+
import org.junit.Rule
27+
import org.junit.Test
28+
import org.junit.rules.TemporaryFolder
29+
import org.junit.runner.RunWith
30+
import org.mockito.ArgumentMatchers.anyInt
31+
import org.mockito.kotlin.any
32+
import org.mockito.kotlin.doReturn
33+
import org.mockito.kotlin.eq
34+
import org.mockito.kotlin.mock
35+
import org.mockito.kotlin.spy
36+
import org.mockito.kotlin.verify
37+
import org.robolectric.RobolectricTestRunner
38+
import java.io.File
39+
40+
@RunWith(RobolectricTestRunner::class)
41+
class ProfileContextWrapperTest {
42+
@get:Rule
43+
val tempFolder = TemporaryFolder()
44+
45+
private lateinit var baseContext: Context
46+
private lateinit var profileBaseDir: File
47+
private val profileId = "p_test123"
48+
49+
@Before
50+
fun setUp() {
51+
// spy to verify calls passed to super.getSharedPreferences
52+
baseContext = spy(ApplicationProvider.getApplicationContext<Context>())
53+
profileBaseDir = tempFolder.newFolder("p_test123")
54+
}
55+
56+
@Test
57+
fun `getFilesDir returns correct path and creates directory`() {
58+
val wrapper = ProfileContextWrapper(baseContext, profileId, profileBaseDir)
59+
val result = wrapper.filesDir
60+
val expected = File(profileBaseDir, "files")
61+
assertEquals(expected.absolutePath, result.absolutePath)
62+
assertTrue("Directory should be created", result.exists())
63+
}
64+
65+
@Test
66+
fun `getCacheDir returns correct path and creates directory`() {
67+
val wrapper = ProfileContextWrapper(baseContext, profileId, profileBaseDir)
68+
val result = wrapper.cacheDir
69+
val expected = File(profileBaseDir, "cache")
70+
assertEquals(expected.absolutePath, result.absolutePath)
71+
assertTrue(result.exists())
72+
}
73+
74+
@Test
75+
fun `getCodeCacheDir returns correct path and creates directory`() {
76+
val wrapper = ProfileContextWrapper(baseContext, profileId, profileBaseDir)
77+
val result = wrapper.codeCacheDir
78+
val expected = File(profileBaseDir, "code_cache")
79+
assertEquals(expected.absolutePath, result.absolutePath)
80+
assertTrue(result.exists())
81+
}
82+
83+
@Test
84+
fun `getNoBackupFilesDir returns correct path and creates directory`() {
85+
val wrapper = ProfileContextWrapper(baseContext, profileId, profileBaseDir)
86+
val result = wrapper.noBackupFilesDir
87+
val expected = File(profileBaseDir, "no_backup")
88+
assertEquals(expected.absolutePath, result.absolutePath)
89+
assertTrue(result.exists())
90+
}
91+
92+
@Test
93+
fun `getDatabasePath returns correct path inside databases folder`() {
94+
val wrapper = ProfileContextWrapper(baseContext, profileId, profileBaseDir)
95+
val dbName = "my_collection.db"
96+
val result = wrapper.getDatabasePath(dbName)
97+
val expectedDir = File(profileBaseDir, "databases")
98+
val expectedFile = File(expectedDir, dbName)
99+
assertEquals(expectedFile.absolutePath, result.absolutePath)
100+
assertTrue("Parent databases folder should be created", expectedDir.exists())
101+
}
102+
103+
@Test
104+
fun `getDir returns correct custom path`() {
105+
val wrapper = ProfileContextWrapper(baseContext, profileId, profileBaseDir)
106+
val dirName = "app_textures"
107+
val result = wrapper.getDir(dirName, Context.MODE_PRIVATE)
108+
val expected = File(profileBaseDir, dirName)
109+
assertEquals(expected.absolutePath, result.absolutePath)
110+
assertTrue(result.exists())
111+
}
112+
113+
@Test
114+
fun `getSharedPreferences prefixes name for custom profile`() {
115+
val wrapper = ProfileContextWrapper(baseContext, profileId, profileBaseDir)
116+
val originalName = "deck_options"
117+
118+
doReturn(mock<SharedPreferences>())
119+
.`when`(baseContext)
120+
.getSharedPreferences(any(), anyInt())
121+
122+
wrapper.getSharedPreferences(originalName, Context.MODE_PRIVATE)
123+
124+
val expectedName = "profile_${profileId}_$originalName"
125+
verify(baseContext).getSharedPreferences(eq(expectedName), eq(Context.MODE_PRIVATE))
126+
}
127+
128+
@Test
129+
fun `getSharedPreferences does NOT prefix name for default profile`() {
130+
val defaultId = "default"
131+
val wrapper = ProfileContextWrapper(baseContext, defaultId, profileBaseDir)
132+
val originalName = "deck_options"
133+
134+
doReturn(mock<SharedPreferences>())
135+
.`when`(baseContext)
136+
.getSharedPreferences(any(), anyInt())
137+
138+
wrapper.getSharedPreferences(originalName, Context.MODE_PRIVATE)
139+
140+
verify(baseContext).getSharedPreferences(eq(originalName), eq(Context.MODE_PRIVATE))
141+
}
142+
143+
@Test
144+
fun `getSharedPreferences does NOT double-prefix if name is already prefixed`() {
145+
val wrapper = ProfileContextWrapper(baseContext, profileId, profileBaseDir)
146+
val alreadyPrefixedName = "profile_${profileId}_deck_options"
147+
148+
doReturn(mock<SharedPreferences>())
149+
.`when`(baseContext)
150+
.getSharedPreferences(any(), anyInt())
151+
152+
wrapper.getSharedPreferences(alreadyPrefixedName, Context.MODE_PRIVATE)
153+
154+
verify(baseContext).getSharedPreferences(eq(alreadyPrefixedName), eq(Context.MODE_PRIVATE))
155+
}
156+
157+
@Test
158+
fun `init block creates directories immediately`() {
159+
profileBaseDir.deleteRecursively()
160+
161+
ProfileContextWrapper(baseContext, profileId, profileBaseDir)
162+
163+
val filesDir = File(profileBaseDir, "files")
164+
val databasesDir = File(profileBaseDir, "databases")
165+
166+
assertTrue("Files dir should be created in init", filesDir.exists())
167+
assertTrue("Databases dir should be created in init", databasesDir.exists())
168+
}
169+
}

0 commit comments

Comments
 (0)