add Path.contains (#640)

This commit is contained in:
Jens Geiregat
2020-10-27 15:39:06 +01:00
committed by GitHub
parent 71991e03f1
commit 4891a36183
6 changed files with 141 additions and 9 deletions

View File

@@ -7,6 +7,7 @@ package ch.tutteli.atrium.api.fluent.en_GB
import ch.tutteli.atrium.creating.Expect
import ch.tutteli.atrium.logic.*
import ch.tutteli.kbox.forElementAndForEachIn
import java.nio.charset.Charset
import java.nio.file.Path
@@ -310,6 +311,32 @@ fun <T : Path> Expect<T>.isAbsolute(): Expect<T> =
fun <T : Path> Expect<T>.isRelative(): Expect<T> =
_logicAppend { isRelative() }
/**
* Expects that the subject of the assertion (a [Path]) is a directory;
* meaning that there is a file system entry at the location the [Path] points to and that is a directory.
*
* Every argument string is expected to exist as a child file or child directory for the subject of the assertion.
*
* This assertion _resolves_ symbolic links.
* Therefore, if a symbolic link exists at the location the subject points to, search will continue
* at the location the link points at.
*
* This assertion is not atomic with respect to concurrent file system operations on the paths the assertions works on.
* Its result, in particular its extended explanations, may be wrong if such concurrent file system operations
* take place.
*
* @return An [Expect] for the current subject of the assertion.
* @throws AssertionError Might throw an [AssertionError] if the assertion made is not correct.
*
* @since 0.14.0
*/
fun <T : Path> Expect<T>.contains(path: String, vararg otherPaths: String): Expect<T> =
isDirectory() and {
forElementAndForEachIn(path, otherPaths) { p ->
resolve(p) { exists() }
}
}
/**
* Creates an [Expect] for the property [Path.extension][ch.tutteli.niok.extension]
* (provided via [niok](https://github.com/robstoll/niok)) of the subject of the assertion,

View File

@@ -1,7 +1,7 @@
module ch.tutteli.atrium.api.fluent.en_GB {
requires ch.tutteli.atrium.logic;
requires kotlin.stdlib;
requires ch.tutteli.kbox;
exports ch.tutteli.atrium.api.fluent.en_GB;
exports ch.tutteli.atrium.api.fluent.en_GB.creating.charsequence.contains.builders;

View File

@@ -1,10 +1,7 @@
package ch.tutteli.atrium.api.fluent.en_GB
import ch.tutteli.atrium.creating.Expect
import ch.tutteli.atrium.specs.fun0
import ch.tutteli.atrium.specs.fun1
import ch.tutteli.atrium.specs.fun3
import ch.tutteli.atrium.specs.notImplemented
import ch.tutteli.atrium.specs.*
import java.nio.file.Path
import java.nio.file.Paths
@@ -22,6 +19,7 @@ class PathAssertionsSpec : ch.tutteli.atrium.specs.integration.PathAssertionsSpe
fun0(Expect<Path>::isDirectory),
fun0(Expect<Path>::isAbsolute),
fun0(Expect<Path>::isRelative),
fun2<Path, String, Array<out String>>(Expect<Path>::contains),
fun1(Expect<Path>::hasSameBinaryContentAs),
fun3(Expect<Path>::hasSameTextualContentAs),
fun1(Companion::hasSameTextualContentAsDefaultArgs)

View File

@@ -5,6 +5,7 @@ import ch.tutteli.atrium.specs.fun1
import ch.tutteli.atrium.specs.fun3
import ch.tutteli.atrium.specs.notImplemented
import ch.tutteli.atrium.specs.testutils.WithAsciiReporter
import ch.tutteli.kbox.forElementAndForEachIn
import java.nio.charset.Charset
import java.nio.file.Path
import java.nio.file.Paths
@@ -23,6 +24,7 @@ class PathAssertionsSpec : ch.tutteli.atrium.specs.integration.PathAssertionsSpe
"toBe ${aDirectory::class.simpleName}" to Companion::isDirectory,
"toBe ${relative::class.simpleName}" to Companion::isAbsolute,
"toBe ${relative::class.simpleName}" to Companion::isRelative,
"contains not yet implemented in this API" to Companion::contains,
fun1(Expect<Path>::hasSameBinaryContentAs),
fun3(Companion::hasSameTextualContentAs),
fun1(Companion::hasSameTextualContentAsDefaultArgs)
@@ -38,6 +40,11 @@ class PathAssertionsSpec : ch.tutteli.atrium.specs.integration.PathAssertionsSpe
private fun isDirectory(expect: Expect<Path>) = expect toBe aDirectory
private fun isAbsolute(expect: Expect<Path>) = expect toBe absolute
private fun isRelative(expect: Expect<Path>) = expect toBe relative
private fun contains(expect: Expect<Path>, path: String, vararg otherPaths: String) = isDirectory(expect) and {
forElementAndForEachIn(path, otherPaths) { p ->
it resolve path(p) { it toBe existing }
}
}
private fun hasSameTextualContentAs(
expect: Expect<Path>,

View File

@@ -3,14 +3,12 @@
package ch.tutteli.atrium.api.fluent.en_GB.jdk8
import ch.tutteli.atrium.api.fluent.en_GB.contains
import ch.tutteli.atrium.api.fluent.en_GB.isAbsolute
import ch.tutteli.atrium.api.fluent.en_GB.isExecutable
import ch.tutteli.atrium.api.fluent.en_GB.isRelative
import ch.tutteli.atrium.creating.Expect
import ch.tutteli.atrium.specs.fun0
import ch.tutteli.atrium.specs.fun1
import ch.tutteli.atrium.specs.fun3
import ch.tutteli.atrium.specs.notImplemented
import ch.tutteli.atrium.specs.*
import java.nio.file.Path
import java.nio.file.Paths
@@ -28,6 +26,7 @@ class PathAssertionsSpec : ch.tutteli.atrium.specs.integration.PathAssertionsSpe
fun0(Expect<Path>::isDirectory),
fun0(Expect<Path>::isAbsolute), // checks the new function from fluent-jvm because it is not implemented in fluent-jkd8
fun0(Expect<Path>::isRelative), // checks the new function from fluent-jvm because it is not implemented in fluent-jkd8
fun2<Path, String, Array<out String>>(Expect<Path>::contains), // checks the new function from fluent-jvm because it is not implemented in fluent-jkd8
fun1(Expect<Path>::hasSameBinaryContentAs),
fun3(Expect<Path>::hasSameTextualContentAs),
fun1(Companion::hasSameTextualContentAsDefaultArgs)

View File

@@ -45,6 +45,7 @@ abstract class PathAssertionsSpec(
isDirectory: Fun0<Path>,
isAbsolute: Fun0<Path>,
isRelative: Fun0<Path>,
contains: Fun2<Path, String, Array<out String>>,
hasSameBinaryContentAs: Fun1<Path, Path>,
hasSameTextualContentAs: Fun3<Path, Path, Charset, Charset>,
hasSameTextualContentAsDefaultArgs: Fun1<Path, Path>,
@@ -66,6 +67,7 @@ abstract class PathAssertionsSpec(
isDirectory.forSubjectLess(),
isAbsolute.forSubjectLess(),
isRelative.forSubjectLess(),
contains.forSubjectLess("a", arrayOf("b", "c")),
hasSameBinaryContentAs.forSubjectLess(Paths.get("a")),
hasSameTextualContentAs.forSubjectLess(Paths.get("a"), Charsets.ISO_8859_1, Charsets.ISO_8859_1),
hasSameTextualContentAsDefaultArgs.forSubjectLess(Paths.get("a"))
@@ -868,6 +870,103 @@ abstract class PathAssertionsSpec(
}
}
describeFun(contains) {
val containsFun = contains.lambda
listOf(
ContainsTestData( "directory", "directories") { parent, name -> parent.newDirectory(name) },
ContainsTestData("file", "files") { parent, name -> parent.newDirectory(name) }
) .forEach {td ->
it("does not throw if the single parameter is a child ${td.singleName}") withAndWithoutSymlink { maybeLink ->
val folder = maybeLink.create(tempFolder.newDirectory("startDir"))
maybeLink.create(td.factory.invoke(folder, "a"))
expect(folder).containsFun("a", emptyArray())
}
it("does not throw if both parameters are child ${td.multipleName}") withAndWithoutSymlink { maybeLink ->
val folder = maybeLink.create(tempFolder.newDirectory("startDir"))
maybeLink.create(td.factory.invoke(folder, "a"))
maybeLink.create(td.factory.invoke(folder, "b"))
expect(folder).containsFun("a", arrayOf("b"))
}
it("does not throw if three parameters are child ${td.multipleName}") withAndWithoutSymlink { maybeLink ->
val folder = maybeLink.create(tempFolder.newDirectory("startDir"))
maybeLink.create(td.factory.invoke(folder, "a"))
maybeLink.create(td.factory.invoke(folder, "b"))
maybeLink.create(td.factory.invoke(folder, "c"))
expect(folder).containsFun("a", arrayOf("b", "c"))
}
it("it throws if the first ${td.singleName} does not exist") withAndWithoutSymlink { maybeLink ->
val folder = maybeLink.create(tempFolder.newDirectory("startDir"))
maybeLink.create(td.factory.invoke(folder, "file2"))
maybeLink.create(td.factory.invoke(folder, "file3"))
expect {
expect(folder).containsFun("file1", arrayOf("file2", "file3"))
}.toThrow<AssertionError>().message {
contains("${TO.getDefault()}: ${EXIST.getDefault()}")
contains("file1")
containsNot("file2")
containsNot("file3")
}
}
it("it throws if the second ${td.singleName} does not exist") withAndWithoutSymlink { maybeLink ->
val folder = maybeLink.create(tempFolder.newDirectory("startDir"))
maybeLink.create(td.factory.invoke(folder, "file1"))
maybeLink.create(td.factory.invoke(folder, "file3"))
expect {
expect(folder).containsFun("file1", arrayOf("file2", "file3"))
}.toThrow<AssertionError>().message {
contains("${TO.getDefault()}: ${EXIST.getDefault()}")
contains("file2")
containsNot("file1")
containsNot("file3")
}
}
it("it throws if third ${td.singleName} does not exist") withAndWithoutSymlink { maybeLink ->
val folder = maybeLink.create(tempFolder.newDirectory("startDir"))
maybeLink.create(td.factory.invoke(folder, "file1"))
maybeLink.create(td.factory.invoke(folder, "file2"))
expect {
expect(folder).containsFun("file1", arrayOf("file2", "file3"))
}.toThrow<AssertionError>().message {
contains("${TO.getDefault()}: ${EXIST.getDefault()}")
containsNot("file2")
containsNot("file1")
contains("file3")
}
}
it("it throws if the first and third ${td.singleName} do not exist") withAndWithoutSymlink { maybeLink ->
val folder = maybeLink.create(tempFolder.newDirectory("startDir"))
maybeLink.create(td.factory.invoke(folder, "file2"))
expect {
expect(folder).containsFun("file1", arrayOf("file2", "file3"))
}.toThrow<AssertionError>().message {
contains("${TO.getDefault()}: ${EXIST.getDefault()}")
containsNot("file2")
contains("file1")
contains("file3")
}
}
it("it throws if the directory does not exist") withAndWithoutSymlink { maybeLink ->
val folder = maybeLink.create(tempFolder.tmpDir.resolve("nonExistent"))
val expectedMessage = "$isDescr: ${A_DIRECTORY.getDefault()}"
expect {
expect(folder).containsFun("file1", arrayOf("file2", "file3"))
}.toThrow<AssertionError>().message {
contains(expectedMessage, FAILURE_DUE_TO_NO_SUCH_FILE.getDefault())
containsExplanationFor(maybeLink)
}
}
}
}
describeFun(hasSameBinaryContentAs, hasSameTextualContentAs, hasSameTextualContentAsDefaultArgs) {
val hasSameBinaryContentAsFun = hasSameBinaryContentAs.lambda
val hasSameTextualContentAsFun = hasSameTextualContentAs.lambda
@@ -1239,3 +1338,5 @@ private fun expectedPermissionTypeHintFor(type: Translatable, being: Translatabl
type.getDefault(),
being.getDefault()
)
internal data class ContainsTestData(val singleName: String, val multipleName: String, val factory: (f: Path, name: String) -> Path)