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
import java.nio.file.FileAlreadyExistsException
import kotlin.io.path.Path
import java.nio.file.Path
import kotlin.io.path.createDirectory
import kotlin.io.path.createFile
import kotlin.io.path.writeText
@@ -10,27 +10,21 @@ class FSCreator {
/**
* Create entry, leaving existing folders' contents, but overwriting existing files.
* @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(
entryToCreate: FSEntry,
destination: String,
) {
// No point in running anything if we know the input is invalid.
if (entryToCreate is FSFolder) {
if (entryToCreate.isCyclic()) {
throw CyclicFolderException()
}
if (entryToCreate.deepHasDuplicateNames()) {
throw DuplicateEntryNameException()
}
if (entryToCreate is FSFolder && entryToCreate.isCyclic()) {
throw CyclicFolderException()
}
val queue =
ArrayDeque(
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 DuplicateEntryNameException : Exception("Folder contains entries with duplicate names")
class CyclicFolderException : Exception("Cyclic FSFolders are not supported")

View File

@@ -34,26 +34,6 @@ class FSFolder(name: String, val entries: List<FSEntry>) : FSEntry(name) {
}
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'")

View File

@@ -104,7 +104,6 @@ class FSCreatorTest {
"folder",
listOf(
FSFolder(
// Repeated name should be fine here (not throw)
"folder",
listOf(
FSFolder(
@@ -153,34 +152,4 @@ class FSCreatorTest {
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
import org.junit.jupiter.api.*
import org.junit.jupiter.api.Test
import kotlin.test.assertFalse
import kotlin.test.assertTrue
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
fun `non-cyclic folder`() {
val folder =
@@ -63,74 +39,4 @@ class FSEntryTest {
assertTrue(folder3.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())
}
}