From 8ef62d126f8af36dc0bad6a395ac30237296cb73 Mon Sep 17 00:00:00 2001 From: Abdelilah El Aissaoui Date: Thu, 28 Mar 2024 18:39:28 +0100 Subject: [PATCH] Fixed tree when entries have similar name + added tests --- .../jetpackduba/gitnuro/ui/tree_files/Tree.kt | 42 ++++++++-- .../gitnuro/ui/tree_files/TreeTest.kt | 80 +++++++++++++++++++ 2 files changed, 117 insertions(+), 5 deletions(-) create mode 100644 src/test/kotlin/com/jetpackduba/gitnuro/ui/tree_files/TreeTest.kt diff --git a/src/main/kotlin/com/jetpackduba/gitnuro/ui/tree_files/Tree.kt b/src/main/kotlin/com/jetpackduba/gitnuro/ui/tree_files/Tree.kt index 4c3256c9..48652c95 100644 --- a/src/main/kotlin/com/jetpackduba/gitnuro/ui/tree_files/Tree.kt +++ b/src/main/kotlin/com/jetpackduba/gitnuro/ui/tree_files/Tree.kt @@ -1,6 +1,8 @@ package com.jetpackduba.gitnuro.ui.tree_files +import arrow.core.compareTo import com.jetpackduba.gitnuro.system.systemSeparator +import kotlin.math.max fun entriesToTreeEntry( entries: List, @@ -9,20 +11,26 @@ fun entriesToTreeEntry( ): List> { return entries .asSequence() + .sortedWith { entry1, entry2 -> + val path1 = onGetEntryPath(entry1) + val path2 = onGetEntryPath(entry2) + + PathsComparator().compare(path1, path2) + } .map { entry -> val filePath = onGetEntryPath(entry) val parts = filePath.split(systemSeparator) parts.mapIndexed { index, partName -> if (index == parts.lastIndex) { - val isParentContracted = treeContractedDirs.none { contractedDir -> + val isParentDirectoryContracted = treeContractedDirs.any { contractedDir -> filePath.startsWith(contractedDir + systemSeparator) } - if (isParentContracted) { - TreeItem.File(entry, partName, filePath, index) - } else { + if (isParentDirectoryContracted) { null + } else { + TreeItem.File(entry, partName, filePath, index) } } else { val dirPath = parts.slice(0..index).joinToString(systemSeparator) @@ -45,10 +53,34 @@ fun entriesToTreeEntry( .flatten() .filterNotNull() .distinct() - .sortedBy { it.fullPath } .toList() } +private class PathsComparator : Comparator { + override fun compare(path1: String, path2: String): Int { + val path1Parts = path1.split(systemSeparator) + val path2Parts = path2.split(systemSeparator) + + path1Parts.compareTo(path2Parts) + + val maxIndex = max(path1Parts.count(), path2Parts.count()) + + for (i in 0 until maxIndex) { + val part1 = path1Parts.getOrNull(i) ?: return -1 + val part2 = path2Parts.getOrNull(i) ?: return 1 + + val comparison = part1.compareTo(part2) + + if (comparison != 0) { + return comparison + } + } + + return 0 + } + +} + sealed interface TreeItem { val fullPath: String val displayName: String diff --git a/src/test/kotlin/com/jetpackduba/gitnuro/ui/tree_files/TreeTest.kt b/src/test/kotlin/com/jetpackduba/gitnuro/ui/tree_files/TreeTest.kt new file mode 100644 index 00000000..5b6d737d --- /dev/null +++ b/src/test/kotlin/com/jetpackduba/gitnuro/ui/tree_files/TreeTest.kt @@ -0,0 +1,80 @@ +package com.jetpackduba.gitnuro.ui.tree_files + +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Assertions.assertTrue +import org.junit.jupiter.api.Test + +class TreeTest { + + @Test + fun `test entriesToTreeEntry with empty entries`() { + val entries = emptyList() + val treeContractedDirs = emptyList() + val result = entriesToTreeEntry(entries, treeContractedDirs) { it } + assertTrue(result.isEmpty()) + } + + @Test + fun `test entriesToTreeEntry with single file entry`() { + val entries = listOf("file.txt") + val treeContractedDirs = emptyList() + val result = entriesToTreeEntry(entries, treeContractedDirs) { it } + + assertEquals(listOf(TreeItem.File("file.txt", "file.txt", "file.txt", 0)), result) + } + + @Test + fun `test entriesToTreeEntry with multiple file entries`() { + val entries = listOf("dir1/file1.txt", "dir2/file2.txt", "dir3/file3.txt") + val treeContractedDirs = emptyList() + val result = entriesToTreeEntry(entries, treeContractedDirs) { it } + val expected = listOf( + TreeItem.Dir(true, "dir1", "dir1", 0), + TreeItem.File("dir1/file1.txt", "file1.txt", "dir1/file1.txt", 1), + TreeItem.Dir(true, "dir2", "dir2", 0), + TreeItem.File("dir2/file2.txt", "file2.txt", "dir2/file2.txt", 1), + TreeItem.Dir(true, "dir3", "dir3", 0), + TreeItem.File("dir3/file3.txt", "file3.txt", "dir3/file3.txt", 1) + ) + assertEquals(expected, result) + } + + @Test + fun `test entriesToTreeEntry with similar names`() { + val entries = listOf( + "webpack/webpack.config2.ts", + "webpack/webpack.config.ts", + "webpack-plugin.ts", + "dir1/file3.txt" + ) + val treeContractedDirs = emptyList() + val result = entriesToTreeEntry(entries, treeContractedDirs) { it } + val expected = listOf( + TreeItem.Dir(true, "dir1", "dir1", 0), + TreeItem.File("dir1/file3.txt", "file3.txt", "dir1/file3.txt", 1), + TreeItem.Dir(true, "webpack", "webpack", 0), + TreeItem.File("webpack/webpack.config.ts", "webpack.config.ts", "webpack/webpack.config.ts", 1), + TreeItem.File("webpack/webpack.config2.ts", "webpack.config2.ts", "webpack/webpack.config2.ts", 1), + TreeItem.File("webpack-plugin.ts", "webpack-plugin.ts", "webpack-plugin.ts", 0) + ) + assertEquals(expected, result) + } + + @Test + fun `test entriesToTreeEntry with contracted directories`() { + val entries = listOf( + "webpack/webpack.config2.ts", + "webpack/webpack.config.ts", + "webpack-plugin.ts", + "dir1/file3.txt" + ) + val treeContractedDirs = listOf("webpack", "dir1") + val result = entriesToTreeEntry(entries, treeContractedDirs) { it } + val expected = listOf( + TreeItem.Dir(false, "dir1", "dir1", 0), + TreeItem.Dir(false, "webpack", "webpack", 0), + TreeItem.File("webpack-plugin.ts", "webpack-plugin.ts", "webpack-plugin.ts", 0) + ) + assertEquals(expected, result) + } +} \ No newline at end of file