cue/load: add Dependencies, rename Root and improve test
This sanatizes the repo in preparation of two changes
- fixing path bugs (these are exposed by the new tests)
- implementing the package additions of the spec
These two will be handled in separate CLs.
Change-Id: I5a42aa133b4fe9cf34e2f48b73fdfbf03a910242
Reviewed-on: https://cue-review.googlesource.com/c/cue/+/2947
Reviewed-by: Marcel van Lohuizen <mpvl@golang.org>
diff --git a/cue/build/instance.go b/cue/build/instance.go
index dcde72b..c0197a6 100644
--- a/cue/build/instance.go
+++ b/cue/build/instance.go
@@ -80,7 +80,16 @@
// from ancestor directories, up to the module file.
Dir string
- Root string // module root directory ("" if unknown)
+ // Module defines the module name of a package. It must be defined if
+ // the packages within the directory structure of the module are to be
+ // imported by other packages, including those within the module.
+ Module string
+
+ // Root is the root of the directory hierarchy, it may be "" if this an
+ // instance has no imports.
+ // If Module != "", this corresponds to the module root.
+ // Root/pkg is the directory that holds third-party packages.
+ Root string // root directory of hierarchy ("" if unknown)
// AllTags are the build tags that can influence file selection in this
// directory.
@@ -106,6 +115,25 @@
Match []string
}
+// Dependencies reports all Instances on which this instance depends.
+func (inst *Instance) Dependencies() []*Instance {
+ // TODO: as cyclic dependencies are not allowed, we could just not check.
+ // Do for safety now and remove later if needed.
+ return appendDependencies(nil, inst, map[*Instance]bool{})
+}
+
+func appendDependencies(a []*Instance, inst *Instance, done map[*Instance]bool) []*Instance {
+ for _, d := range inst.Imports {
+ if done[d] {
+ continue
+ }
+ a = append(a, d)
+ done[d] = true
+ a = appendDependencies(a, d, done)
+ }
+ return a
+}
+
// Abs converts relative path used in the one of the file fields to an
// absolute one.
func (inst *Instance) Abs(path string) string {
diff --git a/cue/load/config.go b/cue/load/config.go
index 2852270..3433680 100644
--- a/cue/load/config.go
+++ b/cue/load/config.go
@@ -148,6 +148,13 @@
return i
}
+// Complete updates the configuration information. After calling complete,
+// the following invariants hold:
+// - c.ModuleRoot != ""
+// - c.Module is set to the module import prefix if there is a cue.mod file
+// with the module property.
+// - c.loader != nil
+// - c.cache != ""
func (c Config) complete() (cfg *Config, err error) {
// Each major CUE release should add a tag here.
// Old tags should not be removed. That is, the cue1.x tag is present
@@ -201,7 +208,7 @@
break
}
var r cue.Runtime
- inst, err := r.Parse(mod, f)
+ inst, err := r.Compile(mod, f)
if err != nil {
return nil, errors.Wrapf(err, token.NoPos, "invalid cue.mod file")
}
diff --git a/cue/load/import.go b/cue/load/import.go
index 46c44f6..de14a70 100644
--- a/cue/load/import.go
+++ b/cue/load/import.go
@@ -199,6 +199,8 @@
func updateDirs(c *Config, p *build.Instance, path, srcDir string, mode importMode) errors.Error {
p.DisplayPath = path
+ p.Root = c.ModuleRoot
+ p.Module = c.Module
isLocal := isLocalImport(path)
p.Local = isLocal
diff --git a/cue/load/loader_test.go b/cue/load/loader_test.go
index 949c3e9..26625f6 100644
--- a/cue/load/loader_test.go
+++ b/cue/load/loader_test.go
@@ -16,18 +16,18 @@
import (
"bytes"
- "fmt"
"os"
"path/filepath"
"strconv"
"strings"
"testing"
+ "text/template"
"unicode"
"cuelang.org/go/cue"
- build "cuelang.org/go/cue/build"
"cuelang.org/go/cue/format"
"cuelang.org/go/internal/str"
+ "github.com/kylelemons/godebug/diff"
)
// TestLoad is an end-to-end test.
@@ -36,102 +36,207 @@
if err != nil {
t.Fatal(err)
}
+ testdataDir := filepath.Join(cwd, testdata)
+ dirCfg := &Config{Dir: testdataDir}
+
args := str.StringList
testCases := []struct {
+ cfg *Config
args []string
want string
- err string
}{{
+ // Even though the directory is called testdata, the last path in
+ // the module is test. So "package test" is correctly the default
+ // package of this directory.
+ cfg: dirCfg,
args: nil,
want: `
-test: test.cue (1 files)
- sub: sub/sub.cue (1 files)`,
+path: example.org/test
+module: example.org/test
+root: $CWD/testdata
+dir: $CWD/testdata
+files:
+ $CWD/testdata/test.cue
+imports:
+ example.org/test/sub: $CWD/testdata/sub/sub.cue`,
}, {
+ // Even though the directory is called testdata, the last path in
+ // the module is test. So "package test" is correctly the default
+ // package of this directory.
+ cfg: dirCfg,
args: args("."),
want: `
-test: test.cue (1 files)
- sub: sub/sub.cue (1 files)`,
+path: example.org/test
+module: example.org/test
+root: $CWD/testdata
+dir: $CWD/testdata
+files:
+ $CWD/testdata/test.cue
+imports:
+ example.org/test/sub: $CWD/testdata/sub/sub.cue`,
}, {
+ // TODO:
+ // - paths are incorrect, should be example.org/test/other:main and
+ // example.org/test/other/file, respectively.
+ // - referenced import path of files is wrong.
+ cfg: dirCfg,
args: args("./other/..."),
want: `
-main: other/main.cue (1 files)
- file: other/file/file.cue (1 files);main: other/main.cue (1 files)
- file: other/file/file.cue (1 files)`,
+path: example.org/test
+module: example.org/test
+root: $CWD/testdata/
+dir: $CWD/testdata/other
+files:
+ $CWD/testdata/other/main.cue
+imports:
+ ./file: $CWD/testdata/other/file/file.cue
+
+path: example.org/test/file
+module: example.org/test
+root: $CWD/testdata/
+dir: $CWD/testdata/other/file
+files:
+ $CWD/testdata/other/file/file.cue`,
}, {
+ cfg: dirCfg,
args: args("./anon"),
- want: ": (0 files)",
- err: "build constraints exclude all CUE files",
+ want: `
+err: build constraints exclude all CUE files in ./anon
+path: example.org/test/anon
+module: example.org/test
+root: $CWD/testdata
+dir: $CWD/testdata/anon`,
}, {
+ // TODO:
+ // - paths are incorrect, should be example.org/test/other:main and
+ // example.org/test/other/file, respectively.
+ cfg: dirCfg,
args: args("./other"),
want: `
-main: other/main.cue (1 files)
- file: other/file/file.cue (1 files)`,
+path: example.org/test/other
+module: example.org/test
+root: $CWD/testdata
+dir: $CWD/testdata/other
+files:
+ $CWD/testdata/other/main.cue
+imports:
+ ./file: $CWD/testdata/other/file/file.cue`,
}, {
+ // TODO:
+ // - incorrect path, should be example.org/test/hello:test
+ cfg: dirCfg,
args: args("./hello"),
want: `
-test: test.cue hello/test.cue (2 files)
- sub: sub/sub.cue (1 files)`,
+path: example.org/test/hello
+module: example.org/test
+root: $CWD/testdata
+dir: $CWD/testdata/hello
+files:
+ $CWD/testdata/test.cue
+ $CWD/testdata/hello/test.cue
+imports:
+ example.org/test/sub: $CWD/testdata/sub/sub.cue`,
}, {
+ cfg: dirCfg,
args: args("./anon.cue", "./other/anon.cue"),
- want: ": ./anon.cue ./other/anon.cue (2 files)",
+ want: `
+path: ""
+module: ""
+root: $CWD/testdata
+dir: $CWD/testdata
+files:
+ $CWD/testdata/anon.cue
+ $CWD/testdata/other/anon.cue`,
}, {
+ cfg: dirCfg,
// Absolute file is normalized.
args: args(filepath.Join(cwd, "testdata", "anon.cue")),
- want: ": ./anon.cue (1 files)",
+ want: `
+path: ""
+module: ""
+root: $CWD/testdata
+dir: $CWD/testdata
+files:
+ $CWD/testdata/anon.cue`,
}, {
+ // NOTE: dir should probably be set to $CWD/testdata, but either way.
+ cfg: dirCfg,
args: args("non-existing"),
- want: ": (0 files)",
- err: `cannot find package "non-existing"`,
+ want: `
+err: cannot find package "non-existing"
+path: ""
+module: example.org/test
+root: $CWD/testdata
+dir: non-existing `,
}, {
+ cfg: dirCfg,
args: args("./empty"),
- want: ": (0 files)",
- err: `no CUE files in ./empty`,
+ want: `
+err: no CUE files in ./empty
+path: example.org/test/empty
+module: example.org/test
+root: $CWD/testdata
+dir: $CWD/testdata/empty`,
}, {
+ cfg: dirCfg,
args: args("./imports"),
want: `
-imports: imports/imports.cue (1 files)
- catch: pkg/acme.com/catch/catch.cue (1 files)
- helper: pkg/acme.com/helper/helper.cue (1 files)`,
- err: ``,
+path: example.org/test/imports
+module: example.org/test
+root: $CWD/testdata
+dir: $CWD/testdata/imports
+files:
+ $CWD/testdata/imports/imports.cue
+imports:
+ acme.com/catch: $CWD/testdata/pkg/acme.com/catch/catch.cue
+ acme.com/helper: $CWD/testdata/pkg/acme.com/helper/helper.cue`,
}}
for i, tc := range testCases {
t.Run(strconv.Itoa(i)+"/"+strings.Join(tc.args, ":"), func(t *testing.T) {
- c := &Config{Dir: filepath.Join(cwd, testdata)}
- pkgs := Instances(tc.args, c)
+ pkgs := Instances(tc.args, tc.cfg)
- var errs, data []string
- for _, p := range pkgs {
- if p.Err != nil {
- errs = append(errs, p.Err.Error())
- }
- got := strings.TrimSpace(pkgInfo(pkgs[0]))
- data = append(data, got)
+ buf := &bytes.Buffer{}
+ err := pkgInfo.Execute(buf, pkgs)
+ if err != nil {
+ t.Fatal(err)
}
- if err := strings.Join(errs, ";"); err == "" != (tc.err == "") ||
- err != "" && !strings.Contains(err, tc.err) {
- t.Errorf("error:\n got: %v\nwant: %v", err, tc.err)
- }
- got := strings.Join(data, ";")
+ got := strings.TrimSpace(buf.String())
+ got = strings.Replace(got, cwd, "$CWD", -1)
// Make test work with Windows.
got = strings.Replace(got, string(filepath.Separator), "/", -1)
+
want := strings.TrimSpace(tc.want)
+ want = strings.Replace(want, "\t", " ", -1)
if got != want {
- t.Errorf("got:\n%v\nwant:\n%v", got, want)
+ t.Errorf("\n%s", diff.Diff(got, want))
+ t.Logf("\n%s", got)
}
})
}
}
-func pkgInfo(p *build.Instance) string {
- b := &bytes.Buffer{}
- fmt.Fprintf(b, "%s: %s (%d files)\n",
- p.PkgName, strings.Join(p.CUEFiles, " "), len(p.Files))
- for _, p := range p.Imports {
- fmt.Fprintf(b, "\t%s\n", pkgInfo(p))
- }
- return b.String()
-}
+var pkgInfo = template.Must(template.New("pkg").Parse(`
+{{- range . -}}
+{{- if .Err}}err: {{.Err}}{{end}}
+path: {{if .ImportPath}}{{.ImportPath}}{{else}}""{{end}}
+module: {{if .Module}}{{.Module}}{{else}}""{{end}}
+root: {{.Root}}
+dir: {{.Dir}}
+{{if .Files -}}
+files:
+{{- range .Files}}
+ {{.Filename}}
+{{- end -}}
+{{- end}}
+{{if .Imports -}}
+imports:
+{{- range .Dependencies}}
+ {{.ImportPath}}:{{range .Files}} {{.Filename}}{{end}}
+{{- end}}
+{{end -}}
+{{- end -}}
+`))
func TestOverlays(t *testing.T) {
cwd, _ := os.Getwd()