ForwardingFileSystem

abstract class ForwardingFileSystem(val delegate: FileSystem) : FileSystem

A FileSystem that forwards calls to another, intended for subclassing.

Fault Injection

You can use this to deterministically trigger file system failures in tests. This is useful to confirm that your program behaves correctly even if its file system operations fail. For example, this subclass fails every access of files named unlucky.txt:

val faultyFileSystem = object : ForwardingFileSystem(FileSystem.SYSTEM) {
override fun onPathParameter(path: Path, functionName: String, parameterName: String): Path {
if (path.name == "unlucky.txt") throw IOException("synthetic failure!")
return path
}
}

You can fail specific operations by overriding them directly:

val faultyFileSystem = object : ForwardingFileSystem(FileSystem.SYSTEM) {
override fun delete(path: Path) {
throw IOException("synthetic failure!")
}
}

Observability

You can extend this to verify which files your program accesses. This is a testing file system that records accesses as they happen:

class LoggingFileSystem : ForwardingFileSystem(FileSystem.SYSTEM) {
val log = mutableListOf<String>()

override fun onPathParameter(path: Path, functionName: String, parameterName: String): Path {
log += "$functionName($parameterName=$path)"
return path
}
}

This makes it easy for tests to assert exactly which files were accessed.

@Test
fun testMergeJsonReports() {
createSampleJsonReports()
loggingFileSystem.log.clear()

mergeJsonReports()

assertThat(loggingFileSystem.log).containsExactly(
"list(dir=json_reports)",
"source(file=json_reports/2020-10.json)",
"source(file=json_reports/2020-12.json)",
"source(file=json_reports/2020-11.json)",
"sink(file=json_reports/2020-all.json)"
)
}

Transformations

Subclasses can transform file names and content.

For example, your program may be written to operate on a well-known directory like /etc/ or /System. You can rewrite paths to make such operations safer to test.

You may also transform file content to apply application-layer encryption or compression. This is particularly useful in situations where it's difficult or impossible to enable those features in the underlying file system.

Abstract Functions Only

Some file system functions like copy are implemented by using other features. These are the non-abstract functions in the FileSystem interface.

This class forwards only the abstract functions; non-abstract functions delegate to the other functions of this class. If desired, subclasses may override non-abstract functions to forward them.

Constructors

Link copied to clipboard
constructor(delegate: FileSystem)

Properties

Link copied to clipboard
@get:JvmName(name = "delegate")
val delegate: FileSystem

FileSystem to which this instance is delegating.

Functions

Link copied to clipboard
open override fun appendingSink(file: Path, mustExist: Boolean): Sink

Returns a sink that appends bytes to the end of file, creating it if it doesn't already exist.

Link copied to clipboard
open override fun atomicMove(source: Path, target: Path)

Moves source to target in-place if the underlying file system supports it. If target exists, it is first removed. If source == target, this operation does nothing. This may be used to move a file or a directory.

Link copied to clipboard
open override fun canonicalize(path: Path): Path

Resolves path against the current working directory and symlinks in this file system. The returned path identifies the same file as path, but with an absolute path that does not include any symbolic links.

Link copied to clipboard
expect open fun copy(source: Path, target: Path)

Copies all the bytes from the file at source to the file at target. This does not copy file metadata like last modified time, permissions, or extended attributes.

Link copied to clipboard
expect fun createDirectories(dir: Path, mustCreate: Boolean = false)

Creates a directory at the path identified by dir, and any enclosing parent path directories, recursively.

Link copied to clipboard
open override fun createDirectory(dir: Path, mustCreate: Boolean)

Creates a directory at the path identified by dir.

Link copied to clipboard
open override fun createSymlink(source: Path, target: Path)

Creates a symbolic link at source that resolves to target. If target is a relative path, it is relative to source.parent.

Link copied to clipboard
open override fun delete(path: Path, mustExist: Boolean)

Deletes the file or directory at path.

Link copied to clipboard
expect open fun deleteRecursively(fileOrDirectory: Path, mustExist: Boolean = false)

Recursively deletes all children of fileOrDirectory if it is a directory, then deletes fileOrDirectory itself.

Link copied to clipboard
expect fun exists(path: Path): Boolean

Returns true if path identifies an object on this file system.

Link copied to clipboard
open override fun list(dir: Path): List<Path>

Returns the children of dir. The returned list is sorted using natural ordering. If dir is a relative path, the returned elements will also be relative paths. If it is an absolute path, the returned elements will also be absolute paths.

Link copied to clipboard
open override fun listOrNull(dir: Path): List<Path>?

Returns the children of the directory identified by dir. The returned list is sorted using natural ordering. If dir is a relative path, the returned elements will also be relative paths. If it is an absolute path, the returned elements will also be absolute paths.

Link copied to clipboard
open override fun listRecursively(dir: Path, followSymlinks: Boolean): Sequence<Path>

Returns a sequence that lazily traverses the children of dir using repeated calls to list. If none of dir's children are directories this returns the same elements as list.

Link copied to clipboard
expect fun metadata(path: Path): FileMetadata

Returns metadata of the file, directory, or object identified by path.

Link copied to clipboard
open override fun metadataOrNull(path: Path): FileMetadata?

Returns metadata of the file, directory, or object identified by path. This returns null if there is no file at path.

Link copied to clipboard
open fun onPathParameter(path: Path, functionName: String, parameterName: String): Path

Invoked each time a path is passed as a parameter to this file system. This returns the path to pass to delegate, which should be path itself or a path on delegate that corresponds to it.

Link copied to clipboard
open fun onPathResult(path: Path, functionName: String): Path

Invoked each time a path is returned by delegate. This returns the path to return to the caller, which should be path itself or a path on this that corresponds to it.

Link copied to clipboard
open override fun openReadOnly(file: Path): FileHandle

Returns a handle to read file. This will fail if the file doesn't already exist.

Link copied to clipboard
open override fun openReadWrite(file: Path, mustCreate: Boolean, mustExist: Boolean): FileHandle

Returns a handle to read and write file. This will create the file if it doesn't already exist.

Link copied to clipboard
fun FileSystem.openZip(zipPath: Path): FileSystem
Link copied to clipboard
expect inline fun <T> read(file: Path, readerAction: BufferedSource.() -> T): T

Creates a source to read file, executes readerAction to read it, and then closes the source. This is a compact way to read the contents of a file.

Link copied to clipboard
open override fun sink(file: Path, mustCreate: Boolean): Sink

Returns a sink that writes bytes to file from beginning to end. If file already exists it will be replaced with the new data.

Link copied to clipboard
open override fun source(file: Path): Source

Returns a source that reads the bytes of file from beginning to end.

Link copied to clipboard
open override fun toString(): String
Link copied to clipboard
expect inline fun <T> write(file: Path, mustCreate: Boolean = false, writerAction: BufferedSink.() -> T): T

Creates a sink to write file, executes writerAction to write it, and then closes the sink. This is a compact way to write a file.