Skip to content

Commit 712ca83

Browse files
committed
add file deletion tests
Signed-off-by: alperozturk96 <alper_ozturk@proton.me>
1 parent b01e0fe commit 712ca83

File tree

1 file changed

+295
-0
lines changed

1 file changed

+295
-0
lines changed
Lines changed: 295 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,295 @@
1+
/*
2+
* Nextcloud - Android Client
3+
*
4+
* SPDX-FileCopyrightText: 2026 Alper Ozturk <alper.ozturk@nextcloud.com>
5+
* SPDX-License-Identifier: AGPL-3.0-or-later
6+
*/
7+
8+
package com.nextcloud.test
9+
10+
import com.owncloud.android.AbstractIT
11+
import com.owncloud.android.datamodel.OCFile
12+
import com.owncloud.android.utils.FileStorageUtils
13+
import com.owncloud.android.utils.MimeType
14+
import junit.framework.TestCase.assertFalse
15+
import junit.framework.TestCase.assertNotNull
16+
import junit.framework.TestCase.assertNull
17+
import junit.framework.TestCase.assertTrue
18+
import junit.framework.TestCase.fail
19+
import org.junit.After
20+
import org.junit.Before
21+
import org.junit.Test
22+
import java.io.File
23+
import kotlin.random.Random
24+
25+
class FileDeletionTests : AbstractIT() {
26+
27+
private lateinit var tempDir: File
28+
29+
@Before
30+
fun setup() {
31+
val parent = System.getProperty("java.io.tmpdir")
32+
val childPath = "file_deletion_test_${System.currentTimeMillis()}"
33+
tempDir = File(parent, childPath)
34+
tempDir.mkdirs()
35+
}
36+
37+
@After
38+
fun cleanup() {
39+
tempDir.deleteRecursively()
40+
}
41+
42+
private fun getRandomRemoteId(): String = Random
43+
.nextLong(10_000_000L, 99_999_999L)
44+
.toString()
45+
.padEnd(32, '0')
46+
47+
private fun createAndSaveSingleFileWithLocalCopy(): OCFile {
48+
val now = System.currentTimeMillis()
49+
50+
val file = OCFile("/TestFile.txt").apply {
51+
fileId = Random.nextLong(1, 10_000)
52+
parentId = 0
53+
remoteId = getRandomRemoteId()
54+
fileLength = 1024
55+
mimeType = MimeType.TEXT_PLAIN
56+
creationTimestamp = now
57+
modificationTimestamp = now
58+
permissions = "RWDNV"
59+
}
60+
61+
val localFile = File(tempDir, "TestFile_${file.fileId}.txt").apply {
62+
parentFile?.mkdirs()
63+
createNewFile()
64+
writeText("Temporary test content")
65+
}
66+
file.storagePath = localFile.absolutePath
67+
68+
storageManager.saveFile(file)
69+
70+
return file
71+
}
72+
73+
private fun createAndSaveFolderTree(): OCFile {
74+
val now = System.currentTimeMillis()
75+
val rootFolder = OCFile("/TestFolder").apply {
76+
fileId = Random.nextLong(1, 10_000)
77+
parentId = 0
78+
remoteId = getRandomRemoteId()
79+
mimeType = MimeType.DIRECTORY
80+
creationTimestamp = now
81+
modificationTimestamp = now
82+
permissions = "RWDNVCK"
83+
}
84+
85+
val subFolder = OCFile("/TestFolder/Sub").apply {
86+
fileId = rootFolder.fileId + 1
87+
parentId = rootFolder.fileId
88+
remoteId = getRandomRemoteId()
89+
mimeType = MimeType.DIRECTORY
90+
creationTimestamp = now
91+
modificationTimestamp = now
92+
permissions = "RWDNVCK"
93+
}
94+
95+
val file1 = OCFile("/TestFolder/file1.txt").apply {
96+
fileId = rootFolder.fileId + 2
97+
parentId = rootFolder.fileId
98+
remoteId = getRandomRemoteId()
99+
fileLength = 512
100+
mimeType = MimeType.TEXT_PLAIN
101+
creationTimestamp = now
102+
modificationTimestamp = now
103+
permissions = "RWDNV"
104+
}
105+
106+
val file2 = OCFile("/TestFolder/Sub/file2.txt").apply {
107+
fileId = rootFolder.fileId + 3
108+
parentId = subFolder.fileId
109+
remoteId = getRandomRemoteId()
110+
fileLength = 256
111+
mimeType = MimeType.TEXT_PLAIN
112+
creationTimestamp = now
113+
modificationTimestamp = now
114+
permissions = "RWDNV"
115+
}
116+
117+
listOf(rootFolder, subFolder, file1, file2).forEach { storageManager.saveFile(it) }
118+
119+
val file1Path = File(tempDir, "file1_${file1.fileId}.txt").apply { createNewFile() }
120+
val file2Path = File(tempDir, "file2_${file2.fileId}.txt").apply { createNewFile() }
121+
122+
file1.storagePath = file1Path.absolutePath
123+
file2.storagePath = file2Path.absolutePath
124+
125+
storageManager.saveFile(file1)
126+
storageManager.saveFile(file2)
127+
128+
return rootFolder
129+
}
130+
131+
private fun getMixedOcFiles(): List<OCFile> {
132+
val now = System.currentTimeMillis()
133+
134+
fun createFolder(id: Long, parentId: Long, path: String): OCFile = OCFile(path).apply {
135+
fileId = id
136+
this.parentId = parentId
137+
remoteId = getRandomRemoteId()
138+
mimeType = MimeType.DIRECTORY
139+
creationTimestamp = now
140+
modificationTimestamp = now
141+
permissions = "RWDNVCK"
142+
}
143+
144+
fun createFile(id: Long, parentId: Long, path: String, size: Long, mime: String): OCFile = OCFile(path).apply {
145+
fileId = id
146+
this.parentId = parentId
147+
remoteId = getRandomRemoteId()
148+
fileLength = size
149+
creationTimestamp = now
150+
mimeType = mime
151+
modificationTimestamp = now
152+
permissions = "RWDNV"
153+
}
154+
155+
val list = mutableListOf<OCFile>()
156+
157+
list.add(createFolder(1, 0, "/"))
158+
159+
list.add(createFolder(5, 2, "/Documents/Projects"))
160+
list.add(createFile(9, 5, "/Documents/Projects/spec.txt", 12000, MimeType.TEXT_PLAIN))
161+
list.add(createFolder(2, 1, "/Documents"))
162+
list.add(createFile(11, 7, "/Photos/Vacation/img2.jpg", 300000, MimeType.JPEG))
163+
list.add(createFolder(7, 3, "/Photos/Vacation"))
164+
list.add(createFile(4, 2, "/Documents/resume.pdf", 50000, MimeType.PDF))
165+
list.add(createFolder(3, 1, "/Photos"))
166+
list.add(createFile(12, 3, "/Photos/cover.png", 80000, MimeType.PNG))
167+
list.add(createFile(6, 5, "/Documents/Projects/readme.txt", 2000, MimeType.TEXT_PLAIN))
168+
list.add(createFolder(8, 5, "/Documents/Projects/Archive"))
169+
list.add(createFile(13, 8, "/Documents/Projects/Archive/old.bmp", 900000, MimeType.BMP))
170+
list.add(createFile(10, 7, "/Photos/Vacation/img1.jpg", 250000, MimeType.JPEG))
171+
list.add(createFolder(14, 1, "/Temp"))
172+
list.add(createFile(15, 14, "/Temp/tmp1.txt", 400, MimeType.TEXT_PLAIN))
173+
list.add(createFile(16, 14, "/Temp/tmp2.txt", 800, MimeType.TEXT_PLAIN))
174+
list.add(createFolder(17, 14, "/Temp/Nested"))
175+
list.add(createFile(18, 17, "/Temp/Nested/deep.txt", 100, MimeType.TEXT_PLAIN))
176+
list.add(createFile(19, 2, "/Documents/notes.txt", 1500, MimeType.TEXT_PLAIN))
177+
list.add(createFolder(20, 3, "/Photos/EmptyFolder"))
178+
179+
list.forEach { ocFile ->
180+
if (!ocFile.isFolder) {
181+
val localFile = File(tempDir, ocFile.remoteId).apply {
182+
parentFile?.mkdirs()
183+
createNewFile()
184+
writeText("test content")
185+
}
186+
ocFile.storagePath = localFile.absolutePath
187+
storageManager.saveFile(ocFile)
188+
} else {
189+
// For folders, create the folder in tempDir
190+
val localFolder = File(tempDir, ocFile.remoteId).apply { mkdirs() }
191+
ocFile.storagePath = localFolder.absolutePath
192+
storageManager.saveFile(ocFile)
193+
}
194+
}
195+
196+
return list
197+
}
198+
199+
@Test
200+
fun deleteMixedFiles() {
201+
var result = false
202+
val files = getMixedOcFiles()
203+
204+
files.forEach {
205+
result = storageManager.removeFile(it, true, true)
206+
if (!result) {
207+
fail("remove operation is failed")
208+
}
209+
}
210+
211+
assert(result)
212+
}
213+
214+
@Test
215+
fun removeNullFileShouldReturnsFalse() {
216+
val result = storageManager.removeFile(null, true, true)
217+
assertFalse(result)
218+
}
219+
220+
@Test
221+
fun deleteFileOnlyFromDb() {
222+
val file = createAndSaveSingleFileWithLocalCopy()
223+
224+
val result = storageManager.removeFile(file, true, false)
225+
226+
assertTrue(result)
227+
228+
// verify DB no longer contains file
229+
val fromDb = storageManager.getFileById(file.fileId)
230+
assertNull(fromDb)
231+
232+
// verify local file still exists
233+
assertTrue(File(file.storagePath).exists())
234+
}
235+
236+
@Test
237+
fun deleteFileOnlyLocalCopy() {
238+
val file = createAndSaveSingleFileWithLocalCopy()
239+
240+
val result = storageManager.removeFile(file, false, true)
241+
242+
assertTrue(result)
243+
244+
// DB should still contain file
245+
val fromDb = storageManager.getFileById(file.fileId)
246+
assertNotNull(fromDb)
247+
248+
// Storage path should be null
249+
assertNull(fromDb?.storagePath)
250+
}
251+
252+
@Test
253+
fun deleteFileDBAndLocal() {
254+
val file = createAndSaveSingleFileWithLocalCopy()
255+
256+
val result = storageManager.removeFile(file, true, true)
257+
258+
assertTrue(result)
259+
260+
assertNull(storageManager.getFileById(file.fileId))
261+
assertFalse(File(file.storagePath).exists())
262+
}
263+
264+
@Test
265+
fun deleteFolderRecursive() {
266+
val folder = createAndSaveFolderTree()
267+
268+
val result = storageManager.removeFile(folder, true, true)
269+
270+
assertTrue(result)
271+
272+
// Folder removed from DB
273+
assertNull(storageManager.getFileById(folder.fileId))
274+
275+
// Children removed
276+
val children = storageManager.getFolderContent(folder, false)
277+
assertTrue(children.isEmpty())
278+
279+
// Local folder gone
280+
val localPath = FileStorageUtils.getDefaultSavePathFor(user.accountName, folder)
281+
assertFalse(File(localPath).exists())
282+
}
283+
284+
@Test
285+
fun removeFolderFileIdMinusOneSkipsDBDeletion() {
286+
val folder = OCFile("/Test").apply {
287+
fileId = -1
288+
mimeType = MimeType.DIRECTORY
289+
}
290+
291+
val result = storageManager.removeFile(folder, true, false)
292+
293+
assertTrue(result)
294+
}
295+
}

0 commit comments

Comments
 (0)