square / okio

A modern I/O library for Android, Java, and Kotlin Multiplatform.

Home Page:https://square.github.io/okio/

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

FakeFileSystem incorrect file size on conversion to File

arturdryomov opened this issue · comments

When using FakeFileSystem, the Path#toFile() works fine but calling length() on the resulting File always results in 0. For example, the following test fails:

@Test
fun checkFileSize() {
    FakeFileSystem().let { fs ->
        val rootPath = "root".toPath().apply {
            fs.createDirectory(this)
        }

        val textPath = rootPath.resolve("file.txt")

        fs.write(textPath) {
            writeUtf8("fake")
            flush()
        }

        assertEquals(fs.metadata(textPath).size!!, textPath.toFile().length())
    }
}
Expected :4
Actual   :0

I’m not sure if a workaround is possible though since it’s not possible to pass a file size into the File constructor or set it. As far as I can see, under the hood File uses DefaultFileSystem.getFileSystem() on recent JDKs so a FS swap is not possible as well.

Overall, is it a known behavior / caveat when working with FakeFileSystem?

In Okio, the path is a value which represents just that: the file path. There is no intrinsic information that ties it to a particular file system. This is in contrast to java.io.File which is tied to the system file system, or java.nio.file.Path which is tied to a particular java.nio.file.FileSystem.

If you have a path which represents /tmp/hello.txt and I have the same path, reading the file will (probably) produce different results because we have different machines. The path is the same value, the file system is the system file system on both machines but the machines are different. The path doesn't know that, though, and is likely valid on both (or any) machine.

If you have the path META-INT/MANIFEST.MF and you read it from two different .jar files (using zip filesystem) you'll get different results. The path is the same, but the file system is different. The path doesn't know that, it's valid anywhere since it's just a value.

In your case, you have a path /root/file.txt and you are writing to that on the fake file system instance and then attempting to read it from the system file system (by virtue of first converting it to a File). You are writing in one place and reading from another.

The only time toFile() will return a File that points to the same reference is if you were using FileSystem.SYSTEM.

Another way to think about it is to create two instances of FakeFileSystem. If you write to one and then read from the other you will not see the correct size.

By calling toFile() you are taking the path and passing it to a different system that only knows how to read from the system file system.

Thanks for the detailed and awesome explanation!