Compare commits

..

2 Commits

Author SHA1 Message Date
e7dd3fa073 Verify FSEntry name
All checks were successful
Test Workflow / Lint and test library (pull_request) Successful in 1m42s
2024-01-07 15:04:13 +00:00
726980a669 Test create file & cleanup 2024-01-07 15:03:00 +00:00
4 changed files with 7 additions and 160 deletions

View File

@@ -1,7 +1,7 @@
package filesystem package filesystem
import java.nio.file.FileAlreadyExistsException import java.nio.file.FileAlreadyExistsException
import kotlin.io.path.Path import java.nio.file.Path
import kotlin.io.path.createDirectory import kotlin.io.path.createDirectory
import kotlin.io.path.createFile import kotlin.io.path.createFile
import kotlin.io.path.writeText import kotlin.io.path.writeText
@@ -10,27 +10,21 @@ class FSCreator {
/** /**
* Create entry, leaving existing folders' contents, but overwriting existing files. * Create entry, leaving existing folders' contents, but overwriting existing files.
* @throws CyclicFolderException Cyclic folders cannot be created. * @throws CyclicFolderException Cyclic folders cannot be created.
* @throws DuplicateEntryNameException A folder or sub-folder contains entries with duplicate names.
*/ */
@Throws(CyclicFolderException::class, DuplicateEntryNameException::class) @Throws(CyclicFolderException::class)
fun create( fun create(
entryToCreate: FSEntry, entryToCreate: FSEntry,
destination: String, destination: String,
) { ) {
// No point in running anything if we know the input is invalid. // No point in running anything if we know the input is invalid.
if (entryToCreate is FSFolder) { if (entryToCreate is FSFolder && entryToCreate.isCyclic()) {
if (entryToCreate.isCyclic()) { throw CyclicFolderException()
throw CyclicFolderException()
}
if (entryToCreate.deepHasDuplicateNames()) {
throw DuplicateEntryNameException()
}
} }
val queue = val queue =
ArrayDeque( ArrayDeque(
listOf( listOf(
entryToCreate to Path(destination), entryToCreate to Path.of(destination),
), ),
) )
@@ -52,6 +46,4 @@ class FSCreator {
} }
} }
class CyclicFolderException : Exception("Cyclic FSFolders are not supported") class CyclicFolderException : Exception("Cyclic FSFolders are not supported")
class DuplicateEntryNameException : Exception("Folder contains entries with duplicate names")

View File

@@ -34,26 +34,6 @@ class FSFolder(name: String, val entries: List<FSEntry>) : FSEntry(name) {
} }
return false return false
} }
/**
* Check whether a folder contains multiple entries with the same name.
*/
fun hasDuplicateNames(): Boolean {
val seen = HashSet<String>()
return entries.any { !seen.add(it.name) }
}
internal fun deepHasDuplicateNames(): Boolean {
val queue = ArrayDeque(listOf(this))
while (queue.isNotEmpty()) {
val entry = queue.removeFirst()
if (entry.hasDuplicateNames()) {
return true
}
queue.addAll(entry.entries.mapNotNull { it as? FSFolder })
}
return false
}
} }
class InvalidEntryNameException(name: String) : Exception("Invalid FSEntry name: '$name'") class InvalidEntryNameException(name: String) : Exception("Invalid FSEntry name: '$name'")

View File

@@ -104,7 +104,6 @@ class FSCreatorTest {
"folder", "folder",
listOf( listOf(
FSFolder( FSFolder(
// Repeated name should be fine here (not throw)
"folder", "folder",
listOf( listOf(
FSFolder( FSFolder(
@@ -153,34 +152,4 @@ class FSCreatorTest {
creator.create(folder4, "_tmp") creator.create(folder4, "_tmp")
} }
} }
@Test
fun `create throws on folder with duplicate names`() {
val folder =
FSFolder(
"folder",
listOf(
FSFile("README.md", "# Test File"),
FSFile("hello-world.txt", "Hello World!"),
FSFolder(
"src",
listOf(
FSFile("README.md", "# Source files"),
FSFolder(
"solution",
listOf(
FSFile("solution.py", "print('1 + 1 = 1')"),
FSFile("tmp", "A temporary file"),
FSFolder("tmp", listOf()),
),
),
),
),
FSFolder("tmp", listOf()),
),
)
assertThrows<DuplicateEntryNameException> {
creator.create(folder, "_tmp")
}
}
} }

View File

@@ -1,34 +1,10 @@
package filesystem package filesystem
import org.junit.jupiter.api.* import org.junit.jupiter.api.Test
import kotlin.test.assertFalse import kotlin.test.assertFalse
import kotlin.test.assertTrue import kotlin.test.assertTrue
class FSEntryTest { class FSEntryTest {
@Test
fun `valid name entries`() {
assertDoesNotThrow("should construct FSFile and FSFolder without throwing") {
FSFile("A file with a name.tar.xz", "Contents")
FSFolder(".a folder with a name", listOf())
}
}
@Test
fun `invalid name entries`() {
assertThrows<InvalidEntryNameException> {
FSFile("File/here", "Contents")
}
assertThrows<InvalidEntryNameException> {
FSFolder("Folder/here", listOf())
}
assertThrows<InvalidEntryNameException> {
FSFolder(".", listOf())
}
assertThrows<InvalidEntryNameException> {
FSFolder("/", listOf())
}
}
@Test @Test
fun `non-cyclic folder`() { fun `non-cyclic folder`() {
val folder = val folder =
@@ -63,74 +39,4 @@ class FSEntryTest {
assertTrue(folder3.isCyclic()) assertTrue(folder3.isCyclic())
assertTrue(folder4.isCyclic()) assertTrue(folder4.isCyclic())
} }
@Test
fun `no duplicate names folder`() {
val folder =
FSFolder(
"folder",
listOf(
FSFile("README.md", "# Test File"),
FSFile("hello-world.txt", "Hello World!"),
FSFolder(
"src",
listOf(
FSFile("README.md", "# Source files"),
FSFile("solution-1.py", "print('1 + 1 = 1')"),
FSFile("solution-2.py", "print('1 + 1 = 1')"),
),
),
FSFolder("tmp", listOf()),
),
)
assertFalse(folder.hasDuplicateNames())
assertFalse(folder.deepHasDuplicateNames())
}
@Test
fun `shallow duplicate names folder`() {
val folder =
FSFolder(
"folder",
listOf(
FSFile("README.md", "# Test File"),
FSFile("hello-world.txt", "Hello World!"),
FSFolder(
"src",
listOf(
FSFile("README.md", "# Source files"),
FSFile("solution-1.py", "print('1 + 1 = 1')"),
FSFile("solution-2.py", "print('1 + 1 = 1')"),
),
),
FSFolder("tmp", listOf()),
FSFile("tmp", "A temporary file"),
),
)
assertTrue(folder.hasDuplicateNames())
assertTrue(folder.deepHasDuplicateNames())
}
@Test
fun `deep duplicate names folder`() {
val folder =
FSFolder(
"folder",
listOf(
FSFile("README.md", "# Test File"),
FSFile("hello-world.txt", "Hello World!"),
FSFolder(
"src",
listOf(
FSFile("README.md", "# Source files"),
FSFile("solution-1.py", "print('1 + 1 = 1')"),
FSFile("solution-1.py", "print('1 + 1 = 1')"),
),
),
FSFolder("tmp", listOf()),
),
)
assertFalse(folder.hasDuplicateNames())
assertTrue(folder.deepHasDuplicateNames())
}
} }