Skip to content

Commit

Permalink
Implement SystemCleaner EmptyDirectoryFilter and improve IO logic (pa…
Browse files Browse the repository at this point in the history
…th matching)
  • Loading branch information
d4rken committed Dec 25, 2022
1 parent 3653c0d commit 257a595
Show file tree
Hide file tree
Showing 24 changed files with 484 additions and 93 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,12 @@ import eu.darken.sdmse.common.debug.logging.asLog
import eu.darken.sdmse.common.debug.logging.log
import eu.darken.sdmse.common.files.core.local.LocalPath
import eu.darken.sdmse.common.files.core.local.crumbsTo
import eu.darken.sdmse.common.files.core.local.isAncestorOf
import eu.darken.sdmse.common.files.core.local.isParentOf
import eu.darken.sdmse.common.files.core.saf.SAFPath
import eu.darken.sdmse.common.files.core.saf.crumbsTo
import eu.darken.sdmse.common.files.core.saf.isAncestorOf
import eu.darken.sdmse.common.files.core.saf.isParentOf
import okio.Sink
import okio.Source
import java.io.File
Expand Down Expand Up @@ -227,13 +231,35 @@ suspend fun <T : APath> T.tryMkDirs(gateway: APathGateway<T, out APathLookup<T>>
// return filteredChildren
// }

fun APath.isAncestorOf(descendant: APath): Boolean {
if (this.pathType != descendant.pathType) {
throw IllegalArgumentException("Can't compare different types ($this and $descendant)")
}
return when (pathType) {
APath.PathType.LOCAL -> (this.downCast() as LocalPath).isAncestorOf(descendant.downCast() as LocalPath)
APath.PathType.SAF -> (this.downCast() as SAFPath).isAncestorOf(descendant.downCast() as SAFPath)
APath.PathType.RAW -> descendant.downCast().path.startsWith(this.downCast().path + "/")
}
}

fun APath.isParentOf(child: APath): Boolean {
if (this.pathType != child.pathType) {
throw IllegalArgumentException("Can't compare different types ($this and $child)")
}
return when (pathType) {
APath.PathType.LOCAL -> (this as LocalPath).isParentOf(child as LocalPath)
APath.PathType.SAF -> (this as SAFPath).isParentOf(child as SAFPath)
APath.PathType.RAW -> child.path.startsWith(this.path + "/")
APath.PathType.LOCAL -> (this.downCast() as LocalPath).isParentOf(child.downCast() as LocalPath)
APath.PathType.SAF -> (this.downCast() as SAFPath).isParentOf(child.downCast() as SAFPath)
APath.PathType.RAW -> this.downCast().child(child.name) == child
}
}

fun APath.matches(other: APath): Boolean {
if (this.pathType != other.pathType) {
throw IllegalArgumentException("Can't compare different types ($this and $other)")
}
return when (pathType) {
APath.PathType.LOCAL -> (this.downCast() as LocalPath).path == (other.downCast() as LocalPath).path
APath.PathType.SAF -> (this.downCast() as SAFPath).path == (other.downCast() as SAFPath).path
APath.PathType.RAW -> other.downCast().path == this.downCast().path
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package eu.darken.sdmse.common.files.core

import java.io.File


fun RawPath.crumbsTo(child: RawPath): Array<String> {
val childPath = child.path
Expand All @@ -8,4 +10,12 @@ fun RawPath.crumbsTo(child: RawPath): Array<String> {
return pure.split(java.io.File.separatorChar)
.filter { it.isNotEmpty() }
.toTypedArray()
}


fun RawPath.isAncestorOf(child: RawPath): Boolean {
val parentPath = this.asFile().absolutePath
val childPath = child.asFile().absolutePath

return childPath.startsWith(parentPath + File.separator)
}
Original file line number Diff line number Diff line change
Expand Up @@ -56,16 +56,22 @@ data class LocalPath(
}

fun build(vararg crumbs: String): LocalPath {
var compacter = File(crumbs[0])
var compacter = File(
if (crumbs[0].startsWith(File.separatorChar)) {
crumbs[0]
} else {
File.separator + crumbs[0]
}
)

for (i in 1 until crumbs.size) {
compacter = File(compacter, crumbs[i])
}

return build(compacter)
}

fun build(file: File): LocalPath {
return LocalPath(file)
}
fun build(file: File): LocalPath = LocalPath(file)
}


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -87,9 +87,13 @@ fun LocalPath.performLookup(
)
}

fun LocalPath.isParentOf(child: LocalPath): Boolean {
fun LocalPath.isAncestorOf(child: LocalPath): Boolean {
val parentPath = this.asFile().absolutePath
val childPath = child.asFile().absolutePath

return childPath.startsWith(parentPath + File.separator)
}

fun LocalPath.isParentOf(child: LocalPath): Boolean {
return child(child.name) == child
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,19 +27,4 @@ data class LocalPathLookup(
@IgnoredOnParcel override val pathType: APath.PathType
get() = lookedUp.pathType

override fun equals(other: Any?): Boolean {
if (this === other) return true
if (other !is LocalPathLookup) return false

if (lookedUp != other.lookedUp) return false
if (fileType != other.fileType) return false

return true
}

override fun hashCode(): Int {
var result = lookedUp.hashCode()
result = 31 * result + fileType.hashCode()
return result
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -53,9 +53,16 @@ fun SAFPath.matchPermission(permissions: Collection<UriPermission>): PermissionM
return null
}

fun SAFPath.isParentOf(child: SAFPath): Boolean {
fun SAFPath.isAncestorOf(child: SAFPath): Boolean {
if (this.treeRoot != child.treeRoot) return false
if (this.segments.size > child.segments.size) return false
if (this == child) return false
return child.segments.take(this.segments.size) == this.segments
}

fun SAFPath.isParentOf(child: SAFPath): Boolean {
if (this.treeRoot != child.treeRoot) return false
if (this.segments.size + 1 == child.segments.size) return false
if (this == child) return false
return child.segments.dropLast(1) == this.segments
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,19 +30,4 @@ data class SAFPathLookup(
@IgnoredOnParcel override val pathType: APath.PathType
get() = lookedUp.pathType

override fun equals(other: Any?): Boolean {
if (this === other) return true
if (other !is SAFPathLookup) return false

if (lookedUp != other.lookedUp) return false
if (fileType != other.fileType) return false

return true
}

override fun hashCode(): Int {
var result = lookedUp.hashCode()
result = 31 * result + fileType.hashCode()
return result
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package eu.darken.sdmse.common.files.core

import eu.darken.sdmse.common.files.core.local.LocalPath
import eu.darken.sdmse.common.files.core.local.LocalPathLookup
import io.kotest.matchers.shouldBe
import org.junit.jupiter.api.Test
import testhelpers.BaseTest
import java.time.Instant

class APathExtensionTest : BaseTest() {

@Test
fun `match operator`() {
val file1: APath = LocalPath.build("test", "file1")
val file2: APath = LocalPath.build("test", "file2")

val lookup1: APathLookup<*> = LocalPathLookup(
lookedUp = LocalPath.build("test", "file1"),
fileType = FileType.FILE,
size = 16,
modifiedAt = Instant.EPOCH,
ownership = null,
permissions = null,
target = null,
)
val lookup2: APathLookup<*> = LocalPathLookup(
lookedUp = LocalPath.build("test", "file2"),
fileType = FileType.FILE,
size = 16,
modifiedAt = Instant.EPOCH,
ownership = null,
permissions = null,
target = null,
)
file1.matches(file1) shouldBe true
file1.matches(file2) shouldBe false
file1.matches(lookup1) shouldBe true
file1.matches(lookup2) shouldBe false
lookup1.matches(file1) shouldBe true
lookup1.matches(file2) shouldBe false
lookup1.matches(lookup1) shouldBe true
lookup1.matches(lookup2) shouldBe false
file2.matches(lookup2) shouldBe true
}
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
package eu.darken.sdmse.common.files.core.local

import eu.darken.sdmse.common.files.core.FileType
import eu.darken.sdmse.common.files.core.matches
import io.kotest.matchers.shouldBe
import org.junit.jupiter.api.Test
import testhelpers.BaseTest
import java.time.Instant

class LocalPathExtensionsTest {
class LocalPathExtensionsTest : BaseTest() {


@Test
Expand All @@ -19,12 +23,45 @@ class LocalPathExtensionsTest {
@Test
fun `parent child relationship`() {
val parent = LocalPath.build("base", "the", "parent")
parent.isParentOf(LocalPath.build("base", "the")) shouldBe false
parent.isParentOf(LocalPath.build("base", "the", "parent")) shouldBe false
parent.isParentOf(LocalPath.build("base", "the", "parent2")) shouldBe false
parent.isParentOf(LocalPath.build("base", "the", "parent", "child")) shouldBe true
parent.isParentOf(LocalPath.build("base", "the", "parent", "child", "child")) shouldBe true
parent.isParentOf(LocalPath.build("base", "the", "parent", "child1", "child2")) shouldBe true
parent.isAncestorOf(LocalPath.build("base", "the")) shouldBe false
parent.isAncestorOf(LocalPath.build("base", "the", "parent")) shouldBe false
parent.isAncestorOf(LocalPath.build("base", "the", "parent2")) shouldBe false
parent.isAncestorOf(LocalPath.build("base", "the", "parent", "child")) shouldBe true
parent.isAncestorOf(LocalPath.build("base", "the", "parent", "child", "child")) shouldBe true
parent.isAncestorOf(LocalPath.build("base", "the", "parent", "child1", "child2")) shouldBe true
}

@Test
fun `match operator`() {
val file1 = LocalPath.build("test", "file1")
val file2 = LocalPath.build("test", "file2")

val lookup1 = LocalPathLookup(
lookedUp = LocalPath.build("test", "file1"),
fileType = FileType.FILE,
size = 16,
modifiedAt = Instant.EPOCH,
ownership = null,
permissions = null,
target = null,
)
val lookup2 = LocalPathLookup(
lookedUp = LocalPath.build("test", "file2"),
fileType = FileType.FILE,
size = 16,
modifiedAt = Instant.EPOCH,
ownership = null,
permissions = null,
target = null,
)
file1.matches(file1) shouldBe true
file1.matches(file2) shouldBe false
file1.matches(lookup1) shouldBe true
file1.matches(lookup2) shouldBe false
lookup1.matches(file1) shouldBe true
lookup1.matches(file2) shouldBe false
lookup1.matches(lookup1) shouldBe true
lookup1.matches(lookup2) shouldBe false
file2.matches(lookup2) shouldBe true
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,12 @@ import io.kotest.matchers.shouldBe
import io.kotest.matchers.shouldNotBe
import org.junit.jupiter.api.AfterEach
import org.junit.jupiter.api.Test
import testhelpers.BaseTest
import testhelpers.json.toComparableJson
import java.io.File
import java.time.Instant

class LocalPathTest {
class LocalPathTest : BaseTest() {
private val testFile = File("./testfile")

@AfterEach
Expand Down Expand Up @@ -79,6 +80,12 @@ class LocalPathTest {
}
}

@Test
fun `path are always absolute`() {
LocalPath.build("test", "file1").path shouldBe "/test/file1"
LocalPath.build("").path shouldBe "/"
}

@Test
fun `path comparison`() {
val file1a = LocalPath.build("test", "file1")
Expand Down Expand Up @@ -126,7 +133,7 @@ class LocalPathTest {
permissions = null,
target = null,
)
lookup1a shouldBe lookup1b
lookup1a shouldNotBe lookup1b
lookup1a shouldNotBe lookup1c
lookup1a shouldNotBe lookup2
}
Expand Down
Loading

0 comments on commit 257a595

Please sign in to comment.