-
Notifications
You must be signed in to change notification settings - Fork 0
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
test(hydra): export virtual tree (#224) #245
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -83,6 +83,7 @@ | |
"nicksnyder", | ||
"nolint", | ||
"nolintlint", | ||
"Nuxx", | ||
"onsi", | ||
"outdir", | ||
"Persistable", | ||
|
Original file line number | Diff line number | Diff line change | ||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
@@ -1,10 +1,11 @@ | ||||||||||||||||||||
package lab | ||||||||||||||||||||
package hydra | ||||||||||||||||||||
|
||||||||||||||||||||
import ( | ||||||||||||||||||||
"bytes" | ||||||||||||||||||||
"encoding/xml" | ||||||||||||||||||||
"fmt" | ||||||||||||||||||||
"os" | ||||||||||||||||||||
"os/exec" | ||||||||||||||||||||
"path/filepath" | ||||||||||||||||||||
"strings" | ||||||||||||||||||||
"testing/fstest" | ||||||||||||||||||||
|
@@ -14,47 +15,69 @@ import ( | |||||||||||||||||||
"github.com/snivilised/traverse/internal/third/lo" | ||||||||||||||||||||
) | ||||||||||||||||||||
|
||||||||||||||||||||
const ( | ||||||||||||||||||||
offset = 2 | ||||||||||||||||||||
tabSize = 2 | ||||||||||||||||||||
doWrite = true | ||||||||||||||||||||
) | ||||||||||||||||||||
// Nuxx is a luna.MemFS factory hardcoded to Musico | ||||||||||||||||||||
func Nuxx(verbose bool, portions ...string) (fS *luna.MemFS) { | ||||||||||||||||||||
fS = luna.NewMemFS() | ||||||||||||||||||||
|
||||||||||||||||||||
musico( | ||||||||||||||||||||
newMemWriteProvider(fS, os.ReadFile, verbose, portions...), | ||||||||||||||||||||
verbose, | ||||||||||||||||||||
) | ||||||||||||||||||||
|
||||||||||||||||||||
return fS | ||||||||||||||||||||
} | ||||||||||||||||||||
|
||||||||||||||||||||
func Musico(verbose bool, portions ...string) (fS *luna.MemFS, root string) { | ||||||||||||||||||||
// CustomTree is a luna.MemFS factory, equivalent to Nuxx that is populated by an | ||||||||||||||||||||
// alternative xml file. index is the full path the xml index file and | ||||||||||||||||||||
// tree is the name of the root element in the file (for Nuxx, this would be | ||||||||||||||||||||
// "MUSICO"). The tree can be filtered by specifying 'portions'. | ||||||||||||||||||||
func CustomTree(index, element string, verbose bool, | ||||||||||||||||||||
portions ...string, | ||||||||||||||||||||
) (fS *luna.MemFS, err error) { | ||||||||||||||||||||
fS = luna.NewMemFS() | ||||||||||||||||||||
|
||||||||||||||||||||
root = Provision( | ||||||||||||||||||||
NewMemWriteProvider(fS, os.ReadFile, portions...), | ||||||||||||||||||||
err = custom( | ||||||||||||||||||||
index, | ||||||||||||||||||||
element, | ||||||||||||||||||||
newMemWriteProvider(fS, os.ReadFile, verbose, portions...), | ||||||||||||||||||||
verbose, | ||||||||||||||||||||
portions..., | ||||||||||||||||||||
) | ||||||||||||||||||||
|
||||||||||||||||||||
return fS, root | ||||||||||||||||||||
return fS, err | ||||||||||||||||||||
} | ||||||||||||||||||||
|
||||||||||||||||||||
func Provision(provider *IOProvider, verbose bool, portions ...string) (root string) { | ||||||||||||||||||||
repo := Repo("") | ||||||||||||||||||||
root = filepath.Join(repo, "test", "data", "MUSICO") | ||||||||||||||||||||
index := Join(repo, "test/data/musico-index.xml") | ||||||||||||||||||||
// index is the full path to the xml index to load, including the xml file name | ||||||||||||||||||||
// tree is the name of the root element of the tree | ||||||||||||||||||||
func custom(index, tree string, provider *IOProvider, verbose bool) error { | ||||||||||||||||||||
if _, err := os.Stat(index); err != nil { | ||||||||||||||||||||
return err | ||||||||||||||||||||
} | ||||||||||||||||||||
|
||||||||||||||||||||
if err := ensure(index, provider, verbose); err != nil { | ||||||||||||||||||||
if err := ensure(index, tree, provider, verbose); err != nil { | ||||||||||||||||||||
fmt.Printf("provision failed %v\n", err.Error()) | ||||||||||||||||||||
return "" | ||||||||||||||||||||
} | ||||||||||||||||||||
|
||||||||||||||||||||
if verbose { | ||||||||||||||||||||
fmt.Printf("\n🤖 re-generated tree at '%v' (filters: '%v')\n\n", | ||||||||||||||||||||
root, strings.Join(portions, ", "), | ||||||||||||||||||||
) | ||||||||||||||||||||
} | ||||||||||||||||||||
return nil | ||||||||||||||||||||
} | ||||||||||||||||||||
|
||||||||||||||||||||
const ( | ||||||||||||||||||||
offset = 2 | ||||||||||||||||||||
tabSize = 2 | ||||||||||||||||||||
doWrite = true | ||||||||||||||||||||
) | ||||||||||||||||||||
|
||||||||||||||||||||
return root | ||||||||||||||||||||
func musico(provider *IOProvider, verbose bool) { | ||||||||||||||||||||
repo := Repo("") | ||||||||||||||||||||
index := Combine(repo, "test/data/musico-index.xml") | ||||||||||||||||||||
|
||||||||||||||||||||
if err := ensure(index, "MUSICO", provider, verbose); err != nil { | ||||||||||||||||||||
fmt.Printf("provision failed %v\n", err.Error()) | ||||||||||||||||||||
} | ||||||||||||||||||||
} | ||||||||||||||||||||
|
||||||||||||||||||||
// ensure | ||||||||||||||||||||
func ensure(index string, provider *IOProvider, verbose bool) error { | ||||||||||||||||||||
builder := directoryTreeBuilder{ | ||||||||||||||||||||
tree: "MUSICO", | ||||||||||||||||||||
func ensure(index, tree string, provider *IOProvider, verbose bool) error { | ||||||||||||||||||||
builder := virtualTree{ | ||||||||||||||||||||
tree: tree, | ||||||||||||||||||||
stack: collections.NewStack[string](), | ||||||||||||||||||||
index: index, | ||||||||||||||||||||
doWrite: doWrite, | ||||||||||||||||||||
|
@@ -74,8 +97,9 @@ func ensure(index string, provider *IOProvider, verbose bool) error { | |||||||||||||||||||
return builder.walk() | ||||||||||||||||||||
} | ||||||||||||||||||||
|
||||||||||||||||||||
func NewMemWriteProvider(fS *luna.MemFS, | ||||||||||||||||||||
func newMemWriteProvider(fS *luna.MemFS, | ||||||||||||||||||||
indexReader readFile, | ||||||||||||||||||||
verbose bool, | ||||||||||||||||||||
portions ...string, | ||||||||||||||||||||
) *IOProvider { | ||||||||||||||||||||
filter := lo.Ternary(len(portions) > 0, | ||||||||||||||||||||
|
@@ -93,6 +117,12 @@ func NewMemWriteProvider(fS *luna.MemFS, | |||||||||||||||||||
}), | ||||||||||||||||||||
) | ||||||||||||||||||||
|
||||||||||||||||||||
if verbose { | ||||||||||||||||||||
fmt.Printf("\n🤖 re-generating tree (filters: '%v')\n\n", | ||||||||||||||||||||
strings.Join(portions, ", "), | ||||||||||||||||||||
) | ||||||||||||||||||||
} | ||||||||||||||||||||
|
||||||||||||||||||||
// PS: to check the existence of a path in an fs in production | ||||||||||||||||||||
// code, use fs.Stat(fsys, path) instead of os.Stat/os.Lstat | ||||||||||||||||||||
|
||||||||||||||||||||
|
@@ -230,8 +260,8 @@ func (fn matcher) match(portion string) bool { | |||||||||||||||||||
return fn(portion) | ||||||||||||||||||||
} | ||||||||||||||||||||
|
||||||||||||||||||||
// directoryTreeBuilder | ||||||||||||||||||||
type directoryTreeBuilder struct { | ||||||||||||||||||||
// virtualTree | ||||||||||||||||||||
type virtualTree struct { | ||||||||||||||||||||
tree string | ||||||||||||||||||||
full string | ||||||||||||||||||||
stack *collections.Stack[string] | ||||||||||||||||||||
|
@@ -244,7 +274,7 @@ type directoryTreeBuilder struct { | |||||||||||||||||||
show display | ||||||||||||||||||||
} | ||||||||||||||||||||
|
||||||||||||||||||||
func (r *directoryTreeBuilder) read() (*Directory, error) { | ||||||||||||||||||||
func (r *virtualTree) read() (*Directory, error) { | ||||||||||||||||||||
data, err := r.provider.file.in.read(r.index) | ||||||||||||||||||||
|
||||||||||||||||||||
if err != nil { | ||||||||||||||||||||
|
@@ -260,32 +290,32 @@ func (r *directoryTreeBuilder) read() (*Directory, error) { | |||||||||||||||||||
return &tree.Root, nil | ||||||||||||||||||||
} | ||||||||||||||||||||
|
||||||||||||||||||||
func (r *directoryTreeBuilder) pad() string { | ||||||||||||||||||||
func (r *virtualTree) pad() string { | ||||||||||||||||||||
return string(bytes.Repeat([]byte{' '}, (r.depth+offset)*tabSize)) | ||||||||||||||||||||
} | ||||||||||||||||||||
|
||||||||||||||||||||
func (r *directoryTreeBuilder) refill() string { | ||||||||||||||||||||
func (r *virtualTree) refill() string { | ||||||||||||||||||||
segments := r.stack.Content() | ||||||||||||||||||||
return filepath.Join(segments...) | ||||||||||||||||||||
} | ||||||||||||||||||||
|
||||||||||||||||||||
func (r *directoryTreeBuilder) inc(name string) { | ||||||||||||||||||||
func (r *virtualTree) inc(name string) { | ||||||||||||||||||||
r.stack.Push(name) | ||||||||||||||||||||
r.full = r.refill() | ||||||||||||||||||||
|
||||||||||||||||||||
r.depth++ | ||||||||||||||||||||
r.padding = r.pad() | ||||||||||||||||||||
} | ||||||||||||||||||||
|
||||||||||||||||||||
func (r *directoryTreeBuilder) dec() { | ||||||||||||||||||||
func (r *virtualTree) dec() { | ||||||||||||||||||||
_, _ = r.stack.Pop() | ||||||||||||||||||||
r.full = r.refill() | ||||||||||||||||||||
|
||||||||||||||||||||
r.depth-- | ||||||||||||||||||||
r.padding = r.pad() | ||||||||||||||||||||
} | ||||||||||||||||||||
|
||||||||||||||||||||
func (r *directoryTreeBuilder) walk() error { | ||||||||||||||||||||
func (r *virtualTree) walk() error { | ||||||||||||||||||||
top, err := r.read() | ||||||||||||||||||||
|
||||||||||||||||||||
if err != nil { | ||||||||||||||||||||
|
@@ -297,7 +327,7 @@ func (r *directoryTreeBuilder) walk() error { | |||||||||||||||||||
return r.dir(*top, true) | ||||||||||||||||||||
} | ||||||||||||||||||||
|
||||||||||||||||||||
func (r *directoryTreeBuilder) dir(dir Directory, isRoot bool) error { //nolint:gocritic // performance is not a concern | ||||||||||||||||||||
func (r *virtualTree) dir(dir Directory, isRoot bool) error { //nolint:gocritic // performance is not a concern | ||||||||||||||||||||
if !isRoot { | ||||||||||||||||||||
// We dont to add the root because only the descendents of the root | ||||||||||||||||||||
// should be added | ||||||||||||||||||||
|
@@ -322,7 +352,7 @@ func (r *directoryTreeBuilder) dir(dir Directory, isRoot bool) error { //nolint: | |||||||||||||||||||
} | ||||||||||||||||||||
|
||||||||||||||||||||
for _, file := range dir.Files { | ||||||||||||||||||||
full := Join(r.full, file.Name) | ||||||||||||||||||||
full := Combine(r.full, file.Name) | ||||||||||||||||||||
|
||||||||||||||||||||
if r.doWrite { | ||||||||||||||||||||
if err := r.provider.file.out.write( | ||||||||||||||||||||
|
@@ -340,3 +370,26 @@ func (r *directoryTreeBuilder) dir(dir Directory, isRoot bool) error { //nolint: | |||||||||||||||||||
|
||||||||||||||||||||
return nil | ||||||||||||||||||||
} | ||||||||||||||||||||
|
||||||||||||||||||||
// remember to remove Join and Repo from lab | ||||||||||||||||||||
|
||||||||||||||||||||
// Combine creates a path from the parent combined with the relative path. The relative | ||||||||||||||||||||
// path is a file system path so should only contain forward slashes, not the standard | ||||||||||||||||||||
// file path separator as denoted by filepath.Separator, typically used when interacting | ||||||||||||||||||||
// with the local file system. Do not use trailing "/". | ||||||||||||||||||||
func Combine(parent, relative string) string { | ||||||||||||||||||||
if relative == "" { | ||||||||||||||||||||
return parent | ||||||||||||||||||||
} | ||||||||||||||||||||
|
||||||||||||||||||||
return parent + "/" + relative | ||||||||||||||||||||
} | ||||||||||||||||||||
|
||||||||||||||||||||
// Repo gets the path of the repo with relative joined on | ||||||||||||||||||||
func Repo(relative string) string { | ||||||||||||||||||||
cmd := exec.Command("git", "rev-parse", "--show-toplevel") | ||||||||||||||||||||
output, _ := cmd.Output() | ||||||||||||||||||||
repo := strings.TrimSpace(string(output)) | ||||||||||||||||||||
|
||||||||||||||||||||
return Combine(repo, relative) | ||||||||||||||||||||
Comment on lines
+391
to
+393
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Handle the error returned by The Apply this diff to handle the error: func Repo(relative string) string {
cmd := exec.Command("git", "rev-parse", "--show-toplevel")
- output, _ := cmd.Output()
+ output, err := cmd.Output()
+ if err != nil {
+ fmt.Printf("Error obtaining repository path: %v\n", err)
+ return Combine("", relative)
+ }
repo := strings.TrimSpace(string(output))
return Combine(repo, relative)
} 📝 Committable suggestion
Suggested change
|
||||||||||||||||||||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Return the error from the
ensure
function call incustom
The
custom
function callsensure
, and ifensure
returns an error, it currently prints the error message but does not return the error. This can lead to silent failures for callers ofcustom
who might assume that no error occurred. It's important to propagate the error to allow proper handling upstream.Apply this diff to fix the error handling:
func custom(index, tree string, provider *IOProvider, verbose bool) error { if _, err := os.Stat(index); err != nil { return err } if err := ensure(index, tree, provider, verbose); err != nil { fmt.Printf("provision failed %v\n", err.Error()) + return err } return nil }
📝 Committable suggestion