1

I have a directory structure like:

./gen/items/items.go
./gen/items/items_test.go
./gen/items/data/items.xml
./engine/action/actions_test.go

In items.go I have a method which is supposed to run once, and populate some data structure with the items loaded from items.xml. I do this with an absolute path, which works when the code is running in items_test.go, as I assume the current working directory in go test is the package root of the current tested package.

itemsXMLPath := "./data/items.xml"
absPath, err := path.Abs(itemsXMLPath)
if err != nil {
    ...
}
bytes, err := ioutil.ReadFile(absPath)

You can see here why the absolute path ./data/items.xml works fine from the gen/items dir.

If I run tests in actions_tests.go, it tries to use the relative path from action and fails because there isn't a data dir in action.

When I call my loadFromXML method in items.go, I want it to always use the relative path from the items dir, which will search the data dir below.

I assumed since the calling code is in items.go, the path will go from there, but it seems it uses the working directory of the test runner or the go runner (if I run main.go).

Is there a way to ensure whether used in a test run, or from a main run, the xml load function uses a relative path from a consistent directory in the repo (e.g., how would I consistently find package root dir when ran from MAIN or a test).

I'm sure there is a way to achieve this without duplicating the test data, or without passing in a relative path to the loadFromXML function, which must be adjusted based on where the method is being called!

3
  • 1
    No, there is no way to do this. Pass the filename as an argument. You can also use an actual absolute path (which starts with a slash, not with a dot), but that's obviously not very practical if you ever want to run this code on another machine. Commented Oct 19, 2019 at 10:31
  • 2
    To explain: there are no packages at runtime, so there is no such thing as "package root" at runtime. Packages are only a thing for building the program, not when running it. Commented Oct 19, 2019 at 10:37
  • 1
    Are those XML files static? If yes, use something like github.com/GeertJohan/go.rice or github.com/gobuffalo/packr Commented Oct 19, 2019 at 12:34

1 Answer 1

1

I've ended up just writing a function which can find the root directory from any running folder in the project, and then I can apply the relative path from there. Seems to work running from main, or the test runner.

func getRootPath() string {
    dir, err := os.Getwd()
    if err != nil {
        log.Fatal(err)
    }

    // Check if already in root directory (e.g. main.go)
    lastSlashIndex := strings.LastIndex(dir, "/")
    cName := dir[lastSlashIndex+1:]
    if cName == "#root-folder-name#" {
        return dir
    }

    // Get parent directory
    parent := filepath.Dir(dir)
    lastSlashIndex = strings.LastIndex(parent, "/")
    pName := parent[lastSlashIndex+1:]

    // If not at root, continue getting parent
    for pName != "#root-folder-name#" {
        parent = filepath.Dir(parent)
        lastSlashIndex = strings.LastIndex(parent, "/")
        pName = parent[lastSlashIndex+1:]
    }
    return parent
}

Then I can concatenate this directory with the relative path and use it in filepath.Abs

itemsXMLPath := getRootPath() + "/folder/path/data/items.xml"
absPath, err := filepath.Abs(itemsXMLPath)
Sign up to request clarification or add additional context in comments.

1 Comment

the working directory has nothing to do with the source location. This only works when you actually execute the code from within the source tree

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.