mirror of
https://github.com/jlengrand/atrium.git
synced 2026-03-10 08:01:19 +00:00
add isEmptyDirectory (#831)
* add isEmptyDirectory to PathAssertions.kt and implemented it. * add infix samples. * add fluent samples.
This commit is contained in:
@@ -280,6 +280,9 @@ fun <T : Path> Expect<T>.isDirectory(): Expect<T> =
|
||||
* @return an [Expect] for the subject of `this` expectation.
|
||||
*
|
||||
* @since 0.16.0
|
||||
*
|
||||
* @sample ch.tutteli.atrium.api.fluent.en_GB.samples.PathAssertionSamples.isASymbolicLink
|
||||
* @sample ch.tutteli.atrium.api.fluent.en_GB.samples.PathAssertionSamples.isNotASymbolicLink
|
||||
*/
|
||||
fun <T : Path> Expect<T>.toBeASymbolicLink(): Expect<T> =
|
||||
_logicAppend { toBeASymbolicLink() }
|
||||
@@ -379,3 +382,17 @@ fun <T : Path> Expect<T>.hasSameTextualContentAs(
|
||||
*/
|
||||
fun <T : Path> Expect<T>.hasSameBinaryContentAs(targetPath: Path): Expect<T> =
|
||||
_logicAppend { hasSameBinaryContentAs(targetPath) }
|
||||
|
||||
/**
|
||||
* Expects that the subject of `this` expectation (a [Path]) is an empty directory;
|
||||
* meaning that there is a file system entry at the location the [Path] points to and that is an empty directory.
|
||||
*
|
||||
* @return an [Expect] for the subject of `this` expectation.
|
||||
*
|
||||
* @since 0.16.0
|
||||
*
|
||||
* @sample ch.tutteli.atrium.api.fluent.en_GB.samples.PathAssertionSamples.isEmptyDirectory
|
||||
* @sample ch.tutteli.atrium.api.fluent.en_GB.samples.PathAssertionSamples.isNotEmptyDirectory
|
||||
*/
|
||||
fun <T : Path> Expect<T>.isEmptyDirectory(): Expect<T> =
|
||||
_logicAppend { isEmptyDirectory() }
|
||||
|
||||
@@ -20,6 +20,7 @@ class PathExpectationsSpec : ch.tutteli.atrium.specs.integration.PathExpectation
|
||||
fun0(Expect<Path>::toBeASymbolicLink),
|
||||
fun0(Expect<Path>::isAbsolute),
|
||||
fun0(Expect<Path>::isRelative),
|
||||
fun0(Expect<Path>::isEmptyDirectory),
|
||||
Expect<Path>::hasDirectoryEntry.name to Companion::hasDirectoryEntrySingle,
|
||||
fun2<Path, String, Array<out String>>(Expect<Path>::hasDirectoryEntry),
|
||||
fun1(Expect<Path>::hasSameBinaryContentAs),
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
package ch.tutteli.atrium.api.fluent.en_GB.samples
|
||||
|
||||
import ch.tutteli.atrium.api.fluent.en_GB.isEmptyDirectory
|
||||
import ch.tutteli.atrium.api.fluent.en_GB.toBeASymbolicLink
|
||||
import ch.tutteli.atrium.api.verbs.internal.expect
|
||||
import ch.tutteli.niok.newDirectory
|
||||
import ch.tutteli.niok.newFile
|
||||
import java.nio.file.Files
|
||||
import kotlin.test.Test
|
||||
@@ -28,4 +30,19 @@ class PathAssertionSamples {
|
||||
expect(path).toBeASymbolicLink()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun isEmptyDirectory() {
|
||||
val path = tempDir.newDirectory("dir")
|
||||
expect(path).isEmptyDirectory()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun isNotEmptyDirectory() {
|
||||
tempDir.newFile("a")
|
||||
|
||||
fails {
|
||||
expect(tempDir).isEmptyDirectory()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,6 +22,12 @@ object aRegularFile : Keyword
|
||||
*/
|
||||
object aDirectory : Keyword
|
||||
|
||||
/**
|
||||
* A helper construct to allow expressing assertions about a path being an empty directory.
|
||||
* It can be used for a parameterless function so that it has one parameter and thus can be used as infix function.
|
||||
*/
|
||||
object anEmptyDirectory : Keyword
|
||||
|
||||
/**
|
||||
* A helper construct to allow expressing assertions about a path being a symbolic link.
|
||||
* It can be used for a parameterless function so that it has one parameter and thus can be used as infix function.
|
||||
|
||||
@@ -10,7 +10,6 @@ import ch.tutteli.atrium.api.infix.en_GB.creating.path.PathWithCreator
|
||||
import ch.tutteli.atrium.api.infix.en_GB.creating.path.PathWithEncoding
|
||||
import ch.tutteli.atrium.creating.Expect
|
||||
import ch.tutteli.atrium.logic.*
|
||||
import ch.tutteli.kbox.glue
|
||||
import java.nio.charset.Charset
|
||||
import java.nio.file.Path
|
||||
|
||||
@@ -343,6 +342,9 @@ infix fun <T : Path> Expect<T>.toBe(@Suppress("UNUSED_PARAMETER") aDirectory: aD
|
||||
* @return an [Expect] for the subject of `this` expectation.
|
||||
*
|
||||
* @since 0.16.0
|
||||
*
|
||||
* @sample ch.tutteli.atrium.api.infix.en_GB.samples.PathAssertionSamples.isASymbolicLink
|
||||
* @sample ch.tutteli.atrium.api.infix.en_GB.samples.PathAssertionSamples.isNotASymbolicLink
|
||||
*/
|
||||
infix fun <T : Path> Expect<T>.toBe(@Suppress("UNUSED_PARAMETER") aSymbolicLink: aSymbolicLink): Expect<T> =
|
||||
_logicAppend { toBeASymbolicLink() }
|
||||
@@ -447,3 +449,17 @@ infix fun <T : Path> Expect<T>.hasSameTextualContentAs(pathWithEncoding: PathWit
|
||||
*/
|
||||
infix fun <T : Path> Expect<T>.hasSameBinaryContentAs(targetPath: Path): Expect<T> =
|
||||
_logicAppend { hasSameBinaryContentAs(targetPath) }
|
||||
|
||||
/**
|
||||
* Expects that the subject of `this` expectation (a [Path]) is an empty directory;
|
||||
* meaning that there is a file system entry at the location the [Path] points to and that is an empty directory.
|
||||
*
|
||||
* @return an [Expect] for the subject of `this` expectation.
|
||||
*
|
||||
* @since 0.16.0
|
||||
*
|
||||
* @sample ch.tutteli.atrium.api.infix.en_GB.samples.PathAssertionSamples.isEmptyDirectory
|
||||
* @sample ch.tutteli.atrium.api.infix.en_GB.samples.PathAssertionSamples.isNotEmptyDirectory
|
||||
*/
|
||||
infix fun <T : Path> Expect<T>.toBe(@Suppress("UNUSED_PARAMETER") anEmptyDirectory: anEmptyDirectory): Expect<T> =
|
||||
_logicAppend { isEmptyDirectory() }
|
||||
|
||||
@@ -21,6 +21,7 @@ class PathExpectationsSpec : ch.tutteli.atrium.specs.integration.PathExpectation
|
||||
"toBe ${aSymbolicLink::class.simpleName}" to Companion::toBeASymbolicLink,
|
||||
"toBe ${relative::class.simpleName}" to Companion::isAbsolute,
|
||||
"toBe ${relative::class.simpleName}" to Companion::isRelative,
|
||||
"toBe ${relative::class.simpleName}" to Companion::isEmptyDirectory,
|
||||
fun1(Expect<Path>::hasDirectoryEntry),
|
||||
"has ${::directoryEntries.name}" to Companion::hasDirectoryEntryMultiple,
|
||||
fun1(Expect<Path>::hasSameBinaryContentAs),
|
||||
@@ -50,6 +51,7 @@ class PathExpectationsSpec : ch.tutteli.atrium.specs.integration.PathExpectation
|
||||
private fun toBeASymbolicLink(expect: Expect<Path>) = expect toBe aSymbolicLink
|
||||
private fun isAbsolute(expect: Expect<Path>) = expect toBe absolute
|
||||
private fun isRelative(expect: Expect<Path>) = expect toBe relative
|
||||
private fun isEmptyDirectory(expect: Expect<Path>) = expect toBe anEmptyDirectory
|
||||
private fun hasDirectoryEntryMultiple(expect: Expect<Path>, entry: String, vararg otherEntries: String) =
|
||||
expect has directoryEntries(entry, *otherEntries)
|
||||
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
package ch.tutteli.atrium.api.infix.en_GB.samples
|
||||
|
||||
import ch.tutteli.atrium.api.infix.en_GB.aSymbolicLink
|
||||
import ch.tutteli.atrium.api.infix.en_GB.anEmptyDirectory
|
||||
import ch.tutteli.atrium.api.infix.en_GB.toBe
|
||||
import ch.tutteli.atrium.api.verbs.internal.expect
|
||||
import ch.tutteli.niok.newDirectory
|
||||
import ch.tutteli.niok.newFile
|
||||
import java.nio.file.Files
|
||||
import kotlin.test.Test
|
||||
@@ -29,4 +31,19 @@ class PathAssertionSamples {
|
||||
expect(path) toBe aSymbolicLink
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun isEmptyDirectory() {
|
||||
val path = tempDir.newDirectory("dir")
|
||||
expect(path) toBe anEmptyDirectory
|
||||
}
|
||||
|
||||
@Test
|
||||
fun isNotEmptyDirectory() {
|
||||
tempDir.newFile("a")
|
||||
|
||||
fails {
|
||||
expect(tempDir) toBe anEmptyDirectory
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -50,6 +50,7 @@ fun <T : Path> AssertionContainer<T>.parent(): FeatureExtractorBuilder.Execution
|
||||
fun <T : Path> AssertionContainer<T>.resolve(other: String): FeatureExtractorBuilder.ExecutionStep<T, Path> = impl.resolve(this, other)
|
||||
|
||||
fun <T : Path> AssertionContainer<T>.hasDirectoryEntry(entries: List<String>): Assertion = impl.hasDirectoryEntry(this, entries)
|
||||
fun <T : Path> AssertionContainer<T>.isEmptyDirectory(): Assertion = impl.isEmptyDirectory(this)
|
||||
|
||||
@Suppress("DEPRECATION" /* OptIn is only available since 1.3.70 which we cannot use if we want to support 1.2 */)
|
||||
@UseExperimental(ExperimentalNewExpectTypes::class)
|
||||
|
||||
@@ -52,4 +52,5 @@ interface PathAssertions {
|
||||
): FeatureExtractorBuilder.ExecutionStep<T, Path>
|
||||
|
||||
fun <T : Path> hasDirectoryEntry(container: AssertionContainer<T>, entries: List<String>): Assertion
|
||||
fun <T : Path> isEmptyDirectory(container: AssertionContainer<T>): Assertion
|
||||
}
|
||||
|
||||
@@ -8,14 +8,13 @@ package ch.tutteli.atrium.logic.impl
|
||||
import ch.tutteli.atrium.assertions.Assertion
|
||||
import ch.tutteli.atrium.assertions.builders.assertionBuilder
|
||||
import ch.tutteli.atrium.assertions.builders.invisibleGroup
|
||||
import ch.tutteli.atrium.assertions.builders.withFailureHintBasedOnDefinedSubject
|
||||
import ch.tutteli.atrium.core.None
|
||||
import ch.tutteli.atrium.core.Some
|
||||
import ch.tutteli.atrium.creating.AssertionContainer
|
||||
import ch.tutteli.atrium.creating.Expect
|
||||
import ch.tutteli.atrium.logic.*
|
||||
import ch.tutteli.atrium.logic.creating.filesystem.Failure
|
||||
import ch.tutteli.atrium.logic.creating.filesystem.IoResult
|
||||
import ch.tutteli.atrium.logic.creating.filesystem.Success
|
||||
import ch.tutteli.atrium.logic.creating.filesystem.*
|
||||
import ch.tutteli.atrium.logic.creating.filesystem.hints.*
|
||||
import ch.tutteli.atrium.logic.creating.filesystem.runCatchingIo
|
||||
import ch.tutteli.atrium.logic.creating.transformers.FeatureExtractorBuilder
|
||||
@@ -194,4 +193,31 @@ class DefaultPathAssertions : PathAssertions {
|
||||
container.resolve(entry).collect { _logicAppend { exists(NOFOLLOW_LINKS) } }
|
||||
}
|
||||
).build()
|
||||
|
||||
override fun <T : Path> isEmptyDirectory(container: AssertionContainer<T>): Assertion {
|
||||
val isDirectory = container.isDirectory()
|
||||
if (isDirectory.holds()) {
|
||||
return container.changeSubject.unreported {
|
||||
it.runCatchingIo { Files.newDirectoryStream(it).use { stream -> stream.firstOrNull() } }
|
||||
}.let { expectResult ->
|
||||
assertionBuilder.descriptive.withTest(expectResult) { it is Success && it.value == null }
|
||||
.withFailureHintBasedOnDefinedSubject(expectResult) {
|
||||
explainForResolvedLink(it.path) { realPath ->
|
||||
when (it) {
|
||||
is Success ->
|
||||
assertionBuilder.descriptive.failing
|
||||
.withDescriptionAndRepresentation(
|
||||
DIRECTORY_CONTAINS,
|
||||
it.path.relativize(it.value!!)
|
||||
)
|
||||
.build()
|
||||
is Failure -> hintForIoException(realPath, it.exception)
|
||||
}
|
||||
}
|
||||
}
|
||||
.withDescriptionAndRepresentation(DescriptionBasic.IS, AN_EMPTY_DIRECTORY)
|
||||
.build()
|
||||
}
|
||||
} else return isDirectory
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,6 +42,7 @@ abstract class PathExpectationsSpec(
|
||||
isSymbolicLink: Fun0<Path>,
|
||||
isAbsolute: Fun0<Path>,
|
||||
isRelative: Fun0<Path>,
|
||||
isEmptyDirectory: Fun0<Path>,
|
||||
hasDirectoryEntrySingle: Fun1<Path, String>,
|
||||
hasDirectoryEntryMulti: Fun2<Path, String, Array<out String>>,
|
||||
hasSameBinaryContentAs: Fun1<Path, Path>,
|
||||
@@ -76,6 +77,7 @@ abstract class PathExpectationsSpec(
|
||||
isSymbolicLink.forSubjectLess(),
|
||||
isAbsolute.forSubjectLess(),
|
||||
isRelative.forSubjectLess(),
|
||||
isEmptyDirectory.forSubjectLess(),
|
||||
hasDirectoryEntrySingle.forSubjectLess("a"),
|
||||
hasDirectoryEntryMulti.forSubjectLess("a", arrayOf("b", "c")),
|
||||
hasSameBinaryContentAs.forSubjectLess(Paths.get("a")),
|
||||
@@ -948,6 +950,70 @@ abstract class PathExpectationsSpec(
|
||||
}
|
||||
}
|
||||
|
||||
describeFun(isEmptyDirectory) {
|
||||
val isEmptyDirectoryFun = isEmptyDirectory.lambda
|
||||
val expectedMessage = "$isDescr: ${A_DIRECTORY.getDefault()}"
|
||||
val expectedEmptyMessage = "$isDescr: ${AN_EMPTY_DIRECTORY.getDefault()}"
|
||||
|
||||
context("not accessible") {
|
||||
it("throws an AssertionError for a non-existent path") withAndWithoutSymlink { maybeLink ->
|
||||
val file = maybeLink.create(tempFolder.tmpDir.resolve("nonExistent"))
|
||||
expect {
|
||||
expect(file).isEmptyDirectoryFun()
|
||||
}.toThrow<AssertionError>().message {
|
||||
contains(expectedMessage, FAILURE_DUE_TO_NO_SUCH_FILE.getDefault())
|
||||
containsExplanationFor(maybeLink)
|
||||
}
|
||||
}
|
||||
|
||||
itPrintsFileAccessProblemDetails { testFile ->
|
||||
expect(testFile).isEmptyDirectoryFun()
|
||||
}
|
||||
}
|
||||
|
||||
it("throws an AssertionError for a file") withAndWithoutSymlink { maybeLink ->
|
||||
val file = maybeLink.create(tempFolder.newFile("test"))
|
||||
expect {
|
||||
expect(file).isEmptyDirectoryFun()
|
||||
}.toThrow<AssertionError>().message {
|
||||
contains(expectedMessage, "${WAS.getDefault()}: ${A_FILE.getDefault()}")
|
||||
containsExplanationFor(maybeLink)
|
||||
}
|
||||
}
|
||||
|
||||
it("throws an AssertionError for a non-empty directory") withAndWithoutSymlink { maybeLink ->
|
||||
val dir = tempFolder.newDirectory("notEmpty")
|
||||
dir.newFile("a")
|
||||
val folder = maybeLink.create(dir)
|
||||
expect {
|
||||
expect(folder).isEmptyDirectoryFun()
|
||||
}.toThrow<AssertionError>().message {
|
||||
contains(expectedEmptyMessage)
|
||||
containsExplanationFor(maybeLink)
|
||||
contains("a")
|
||||
}
|
||||
}
|
||||
|
||||
it("throws an AssertionError for a directory that contains an empty directory") withAndWithoutSymlink
|
||||
{ maybeLink ->
|
||||
val dir = tempFolder.newDirectory("notEmpty")
|
||||
dir.newDirectory("a")
|
||||
val folder = maybeLink.create(dir)
|
||||
expect {
|
||||
expect(folder).isEmptyDirectoryFun()
|
||||
}.toThrow<AssertionError>().message {
|
||||
contains(expectedEmptyMessage)
|
||||
containsExplanationFor(maybeLink)
|
||||
contains("a")
|
||||
}
|
||||
}
|
||||
|
||||
it("does not throw for an empty directory") withAndWithoutSymlink { maybeLink ->
|
||||
val folder = maybeLink.create(tempFolder.newDirectory("test"))
|
||||
expect(folder).isEmptyDirectoryFun()
|
||||
}
|
||||
}
|
||||
|
||||
val hasDirectoryEntryVariations = listOf(
|
||||
DirectoryEntryVariation("directory", "directories") { entry -> newDirectory(entry) },
|
||||
DirectoryEntryVariation("file", "files") { entry -> newFile(entry) },
|
||||
|
||||
@@ -29,6 +29,8 @@ enum class DescriptionPathAssertion(override val value: String) : StringBasedTra
|
||||
EXECUTABLE("ausführbar"),
|
||||
A_FILE("eine Datei"),
|
||||
A_DIRECTORY("ein Verzeichnis"),
|
||||
AN_EMPTY_DIRECTORY("ein leeres Verzeichnis"),
|
||||
DIRECTORY_CONTAINS("Verzeichnis enthält"),
|
||||
A_SYMBOLIC_LINK("eine symbolische Verknüpfung"),
|
||||
A_UNKNOWN_FILE_TYPE("ein unbekannter Dateityp"),
|
||||
FAILURE_DUE_TO_NO_SUCH_FILE("es exisitiert kein Dateisystemeintrag an diesem Ort"),
|
||||
|
||||
@@ -24,6 +24,8 @@ enum class DescriptionPathAssertion(override val value: String) : StringBasedTra
|
||||
EXECUTABLE("executable"),
|
||||
A_FILE("a file"),
|
||||
A_DIRECTORY("a directory"),
|
||||
AN_EMPTY_DIRECTORY("an empty directory"),
|
||||
DIRECTORY_CONTAINS("directory contains"),
|
||||
A_SYMBOLIC_LINK("a symbolic link"),
|
||||
A_UNKNOWN_FILE_TYPE("a unknown file type"),
|
||||
FAILURE_DUE_TO_NO_SUCH_FILE("no file system entry exists at this location"),
|
||||
|
||||
Reference in New Issue
Block a user