hackpadfs icon indicating copy to clipboard operation
hackpadfs copied to clipboard

Methods for getting & setting the "working directory"

Open ghostsquad opened this issue 2 years ago • 9 comments

I need the ability to mock out os.Getwd. probably also Chdir.

ghostsquad avatar Jun 02 '22 01:06 ghostsquad

@ghostsquad I take it you're using os.FS? In Go's io/fs documentation the file system doesn't include working directories in the spec, so we don't have one per-se.

However, you could use something like os.FS.Sub(wd) to Chroot your file system to the current working directory. Check out this part of the README for more: https://github.com/hack-pad/hackpadfs#relative-file-paths

JohnStarich avatar Jun 12 '22 05:06 JohnStarich

Maybe the use of io/fs is the wrong choice, as I want to simulate real world behavior and usage, which allows for relative paths, including just using a single . and nothing else.

I could be, and hopefully am, missing something important here. How do other major projects like kubectl or others handle fs mocking for testing?

ghostsquad avatar Jun 12 '22 17:06 ghostsquad

You can certainly use relative paths with io/fs, they just can't go "above ." if that makes sense.

Sounds like you have a familiar use case though: swap out a default os.FS with an in-memory one for testing. In this case, the typical patterns are to either:

  • Use an os.FS and call Sub() with the current working directory, so all paths are sub-paths of this dir.
  • OR use a raw os.FS and use the current working directory as the relative path starting point.

I've used both strategies since they each have their advantages and disadvantages. The first is really great for simple swappable FS's – the downside is you can't reach above the working directory. The second has the greatest flexibility, but it requires input path conversions.

For covet, I preferred to use the latter since my main() could set up the os.FS while the mem.FS could be used for tests. As long as all of my input arguments are converted to valid io/fs paths, it works really well.

JohnStarich avatar Jun 13 '22 03:06 JohnStarich

Would it help if we made a new helper func to convert from os paths to io/fs paths? It may also help resolve https://github.com/hack-pad/hackpadfs/issues/15

I imagine you could run os.Getwd() and run it through the convert func, then use it on the os.FS directly.

JohnStarich avatar Jun 13 '22 03:06 JohnStarich

A helper function would be nice, but I also would like a way to mock out Getwd()

ghostsquad avatar Jun 13 '22 16:06 ghostsquad

Since the Go docs say:

Path names passed to open are UTF-8-encoded, unrooted, slash-separated sequences of path elements, like “x/y/z”. Path names must not contain an element that is “.” or “..” or the empty string, except for the special case that the root directory is named “.”. Paths must not start or end with a slash: “/x” and “x/” are invalid.

– io/fs.ValidPath() docs

I'm of the opinion that Getwd() wouldn't make sense for an io/fs-like file system. I can be convinced otherwise if we find strong precedent from the Go maintainers or community though.

For your use case, I could see some "non-standard" FS's that would be quite effective. For example, since an FS has no knowledge of relative paths, you could wrap any generic FS with your own working directory method Getwd() and then wrap the other methods to handle relative paths too. Maybe it might look like this:

package wdfs

import "github.com/hack-pad/hackpadfs"

type FS struct {
    fs               hackpadfs.FS
    workingDirectory string
}

func New(underlyingFS hackpadfs.FS, workingDirectory string) *FS {
    return &FS{fs: underlyingFS, workingDirectory: workingDirectory}
}

func (f *FS) Open(name string) (hackpadfs.File, error) {
    if isRelativePath(name) {
        name = path.Join(f.workingDirectory, name)
    }
    return f.fs.Open(name)
}

func (f *FS) Getwd() (string, error) {
    return f.workingDirectory, nil
}

JohnStarich avatar Jul 09 '22 23:07 JohnStarich

Interesting idea 🤔

ghostsquad avatar Jul 10 '22 04:07 ghostsquad

@ghostsquad Helpers added in #18! Was the above Go code snippet and the new helpers enough to unblock you?

JohnStarich avatar Jul 17 '22 05:07 JohnStarich

I'll go check it out tomorrow. Thanks! 😄

ghostsquad avatar Jul 18 '22 06:07 ghostsquad

Closing for now, feel free to open a new issue if you have a follow-up 👍

JohnStarich avatar Nov 14 '22 03:11 JohnStarich