cuelang.org/go@v0.13.0/cue/load/loader_test.go (about)

     1  // Copyright 2018 The CUE Authors
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package load
    16  
    17  import (
    18  	"bytes"
    19  	"fmt"
    20  	"os"
    21  	"path/filepath"
    22  	"strings"
    23  	"sync"
    24  	"testing"
    25  	"text/template"
    26  	"unicode"
    27  
    28  	"github.com/go-quicktest/qt"
    29  
    30  	"cuelang.org/go/cue"
    31  	"cuelang.org/go/cue/cuecontext"
    32  	"cuelang.org/go/cue/errors"
    33  	"cuelang.org/go/cue/format"
    34  	"cuelang.org/go/internal/tdtest"
    35  )
    36  
    37  func init() {
    38  	// The user running `go test` might have a broken environment,
    39  	// such as an invalid $CUE_REGISTRY like the one below,
    40  	// or a broken $DOCKER_CONFIG/config.json due to syntax errors.
    41  	// Go tests should be hermetic by explicitly setting load.Config.Env;
    42  	// catch any that do not by leaving a broken $CUE_REGISTRY in os.Environ.
    43  	os.Setenv("CUE_REGISTRY", "inline:{")
    44  }
    45  
    46  // TestLoad is an end-to-end test.
    47  func TestLoad(t *testing.T) {
    48  	cwd, err := os.Getwd()
    49  	if err != nil {
    50  		t.Fatal(err)
    51  	}
    52  	testdataDir := testdata("testmod")
    53  	dirCfg := &Config{
    54  		Dir:   testdataDir,
    55  		Tools: true,
    56  	}
    57  	badModCfg := &Config{
    58  		Dir: testdata("badmod"),
    59  	}
    60  	type loadTest struct {
    61  		name string
    62  		cfg  *Config
    63  		args []string
    64  		want string
    65  	}
    66  
    67  	testCases := []loadTest{{
    68  		name: "BadModuleFile",
    69  		cfg:  badModCfg,
    70  		args: []string{"."},
    71  		want: `err:    module: 2 errors in empty disjunction:
    72  module: conflicting values 123 and "" (mismatched types int and string):
    73      $CWD/testdata/badmod/cue.mod/module.cue:2:9
    74      cuelang.org/go/mod/modfile/schema.cue:56:22
    75  module: conflicting values 123 and string (mismatched types int and string):
    76      $CWD/testdata/badmod/cue.mod/module.cue:2:9
    77      cuelang.org/go/mod/modfile/schema.cue:98:12
    78  path:   ""
    79  module: ""
    80  root:   ""
    81  dir:    ""
    82  display:""`,
    83  	}, {
    84  		name: "DefaultPackage",
    85  		// Even though the directory is called testdata, the last path in
    86  		// the module is test. So "package test" is correctly the default
    87  		// package of this directory.
    88  		cfg:  dirCfg,
    89  		args: nil,
    90  		want: `path:   mod.test/test@v0
    91  module: mod.test/test@v0
    92  root:   $CWD/testdata/testmod
    93  dir:    $CWD/testdata/testmod
    94  display:.
    95  files:
    96      $CWD/testdata/testmod/test.cue
    97  imports:
    98      mod.test/test/sub: $CWD/testdata/testmod/sub/sub.cue`}, {
    99  		name: "DefaultPackageWithExplicitDotArgument",
   100  		// Even though the directory is called testdata, the last path in
   101  		// the module is test. So "package test" is correctly the default
   102  		// package of this directory.
   103  		cfg:  dirCfg,
   104  		args: []string{"."},
   105  		want: `path:   mod.test/test@v0
   106  module: mod.test/test@v0
   107  root:   $CWD/testdata/testmod
   108  dir:    $CWD/testdata/testmod
   109  display:.
   110  files:
   111      $CWD/testdata/testmod/test.cue
   112  imports:
   113      mod.test/test/sub: $CWD/testdata/testmod/sub/sub.cue`}, {
   114  		name: "RelativeImportPathWildcard",
   115  		cfg:  dirCfg,
   116  		args: []string{"./other/..."},
   117  		want: `err:    import failed: relative import paths not allowed ("./file"):
   118      $CWD/testdata/testmod/other/main.cue:6:2
   119  path:   ""
   120  module: mod.test/test@v0
   121  root:   $CWD/testdata/testmod
   122  dir:    ""
   123  display:""`}, {
   124  		name: "NoMatchingPackageName",
   125  		cfg:  dirCfg,
   126  		args: []string{"./anon"},
   127  		want: `err:    build constraints exclude all CUE files in ./anon:
   128      anon/anon.cue: no package name
   129  path:   mod.test/test/anon@v0
   130  module: mod.test/test@v0
   131  root:   $CWD/testdata/testmod
   132  dir:    $CWD/testdata/testmod/anon
   133  display:./anon`}, {
   134  		name: "RelativeImportPathSingle",
   135  		cfg:  dirCfg,
   136  		args: []string{"./other"},
   137  		want: `err:    import failed: relative import paths not allowed ("./file"):
   138      $CWD/testdata/testmod/other/main.cue:6:2
   139  path:   mod.test/test/other@v0:main
   140  module: mod.test/test@v0
   141  root:   $CWD/testdata/testmod
   142  dir:    $CWD/testdata/testmod/other
   143  display:./other
   144  files:
   145      $CWD/testdata/testmod/other/main.cue`}, {
   146  		name: "RelativePathSuccess",
   147  		cfg:  dirCfg,
   148  		args: []string{"./hello"},
   149  		want: `path:   mod.test/test/hello@v0:test
   150  module: mod.test/test@v0
   151  root:   $CWD/testdata/testmod
   152  dir:    $CWD/testdata/testmod/hello
   153  display:./hello
   154  files:
   155      $CWD/testdata/testmod/test.cue
   156      $CWD/testdata/testmod/hello/test.cue
   157  imports:
   158      mod.test/test/sub: $CWD/testdata/testmod/sub/sub.cue`}, {
   159  		name: "ExplicitPackageIdentifier",
   160  		cfg:  dirCfg,
   161  		args: []string{"mod.test/test/hello:test"},
   162  		want: `path:   mod.test/test/hello:test
   163  module: mod.test/test@v0
   164  root:   $CWD/testdata/testmod
   165  dir:    $CWD/testdata/testmod/hello
   166  display:mod.test/test/hello:test
   167  files:
   168      $CWD/testdata/testmod/test.cue
   169      $CWD/testdata/testmod/hello/test.cue
   170  imports:
   171      mod.test/test/sub: $CWD/testdata/testmod/sub/sub.cue`}, {
   172  		name: "NoPackageName",
   173  		cfg:  dirCfg,
   174  		args: []string{"mod.test/test/hello:nonexist"},
   175  		want: `err:    cannot find package "mod.test/test/hello": no files in package directory with package name "nonexist"
   176  path:   mod.test/test/hello:nonexist
   177  module: ""
   178  root:   $CWD/testdata/testmod
   179  dir:    ""
   180  display:mod.test/test/hello:nonexist`,
   181  	}, {
   182  		name: "ExplicitNonPackageFiles",
   183  		cfg:  dirCfg,
   184  		args: []string{"./anon.cue", "./other/anon.cue"},
   185  		want: `path:   ""
   186  module: ""
   187  root:   $CWD/testdata/testmod
   188  dir:    $CWD/testdata/testmod
   189  display:command-line-arguments
   190  files:
   191      $CWD/testdata/testmod/anon.cue
   192      $CWD/testdata/testmod/other/anon.cue`,
   193  	}, {
   194  		name: "AbsoluteFileIsNormalized", // TODO(rogpeppe) what is this actually testing?
   195  		cfg:  dirCfg,
   196  		// Absolute file is normalized.
   197  		args: []string{filepath.Join(cwd, testdata("testmod", "anon.cue"))},
   198  		want: `path:   ""
   199  module: ""
   200  root:   $CWD/testdata/testmod
   201  dir:    $CWD/testdata/testmod
   202  display:command-line-arguments
   203  files:
   204      $CWD/testdata/testmod/anon.cue`}, {
   205  		name: "StandardInput",
   206  		cfg:  dirCfg,
   207  		args: []string{"-"},
   208  		want: `path:   ""
   209  module: ""
   210  root:   $CWD/testdata/testmod
   211  dir:    $CWD/testdata/testmod
   212  display:command-line-arguments
   213  files:
   214      -`}, {
   215  		name: "BadIdentifier",
   216  		cfg:  dirCfg,
   217  		args: []string{"foo.com/bad-identifier"},
   218  		want: `err:    cannot determine package name for "foo.com/bad-identifier"; set it explicitly with ':'
   219  cannot find package "foo.com/bad-identifier": cannot find module providing package foo.com/bad-identifier
   220  path:   foo.com/bad-identifier
   221  module: ""
   222  root:   $CWD/testdata/testmod
   223  dir:    ""
   224  display:foo.com/bad-identifier`,
   225  	}, {
   226  		name: "NonexistentStdlibImport",
   227  		cfg:  dirCfg,
   228  		args: []string{"nonexisting"},
   229  		want: `err:    standard library import path "nonexisting" cannot be imported as a CUE package
   230  path:   nonexisting
   231  module: ""
   232  root:   $CWD/testdata/testmod
   233  dir:    ""
   234  display:nonexisting`,
   235  	}, {
   236  		name: "ExistingStdlibImport",
   237  		cfg:  dirCfg,
   238  		args: []string{"strconv"},
   239  		want: `err:    standard library import path "strconv" cannot be imported as a CUE package
   240  path:   strconv
   241  module: ""
   242  root:   $CWD/testdata/testmod
   243  dir:    ""
   244  display:strconv`,
   245  	}, {
   246  		name: "EmptyPackageDirectory",
   247  		cfg:  dirCfg,
   248  		args: []string{"./empty"},
   249  		want: `err:    no CUE files in ./empty
   250  path:   mod.test/test/empty@v0
   251  module: mod.test/test@v0
   252  root:   $CWD/testdata/testmod
   253  dir:    $CWD/testdata/testmod/empty
   254  display:./empty`,
   255  	}, {
   256  		name: "PackageWithImports",
   257  		cfg:  dirCfg,
   258  		args: []string{"./imports"},
   259  		want: `path:   mod.test/test/imports@v0
   260  module: mod.test/test@v0
   261  root:   $CWD/testdata/testmod
   262  dir:    $CWD/testdata/testmod/imports
   263  display:./imports
   264  files:
   265      $CWD/testdata/testmod/imports/imports.cue
   266  imports:
   267      mod.test/catch: $CWD/testdata/testmod/cue.mod/pkg/mod.test/catch/catch.cue
   268      mod.test/helper:helper1: $CWD/testdata/testmod/cue.mod/pkg/mod.test/helper/helper1.cue`}, {
   269  		name: "PackageWithImportsWithSkipImportsConfig",
   270  		cfg: &Config{
   271  			Dir:         testdataDir,
   272  			Tools:       true,
   273  			SkipImports: true,
   274  		},
   275  		args: []string{"./imports"},
   276  		want: `path:   mod.test/test/imports@v0
   277  module: mod.test/test@v0
   278  root:   $CWD/testdata/testmod
   279  dir:    $CWD/testdata/testmod/imports
   280  display:./imports
   281  files:
   282      $CWD/testdata/testmod/imports/imports.cue`}, {
   283  		name: "OnlyToolFiles",
   284  		cfg:  dirCfg,
   285  		args: []string{"./toolonly"},
   286  		want: `path:   mod.test/test/toolonly@v0:foo
   287  module: mod.test/test@v0
   288  root:   $CWD/testdata/testmod
   289  dir:    $CWD/testdata/testmod/toolonly
   290  display:./toolonly
   291  files:
   292      $CWD/testdata/testmod/toolonly/foo_tool.cue`}, {
   293  		name: "OnlyToolFilesWithToolsDisabledInConfig",
   294  		cfg: &Config{
   295  			Dir: testdataDir,
   296  		},
   297  		args: []string{"./toolonly"},
   298  		want: `err:    build constraints exclude all CUE files in ./toolonly:
   299      test.cue: package is test, want foo
   300      toolonly/foo_tool.cue: _tool.cue files excluded in non-cmd mode
   301  path:   mod.test/test/toolonly@v0:foo
   302  module: mod.test/test@v0
   303  root:   $CWD/testdata/testmod
   304  dir:    $CWD/testdata/testmod/toolonly
   305  display:./toolonly`}, {
   306  		name: "WithBoolTag",
   307  		cfg: &Config{
   308  			Dir:  testdataDir,
   309  			Tags: []string{"prod"},
   310  		},
   311  		args: []string{"./tags"},
   312  		want: `path:   mod.test/test/tags@v0
   313  module: mod.test/test@v0
   314  root:   $CWD/testdata/testmod
   315  dir:    $CWD/testdata/testmod/tags
   316  display:./tags
   317  files:
   318      $CWD/testdata/testmod/tags/prod.cue`}, {
   319  		name: "WithAttrValTag",
   320  		cfg: &Config{
   321  			Dir:  testdataDir,
   322  			Tags: []string{"prod", "foo=bar"},
   323  		},
   324  		args: []string{"./tags"},
   325  		want: `path:   mod.test/test/tags@v0
   326  module: mod.test/test@v0
   327  root:   $CWD/testdata/testmod
   328  dir:    $CWD/testdata/testmod/tags
   329  display:./tags
   330  files:
   331      $CWD/testdata/testmod/tags/prod.cue`}, {
   332  		name: "UnusedTag",
   333  		cfg: &Config{
   334  			Dir:  testdataDir,
   335  			Tags: []string{"prod"},
   336  		},
   337  		args: []string{"./tagsbad"},
   338  		want: `err:    tag "prod" not used in any file
   339  previous declaration here:
   340      $CWD/testdata/testmod/tagsbad/prod.cue:1:1
   341  multiple @if attributes:
   342      $CWD/testdata/testmod/tagsbad/prod.cue:2:1
   343  path:   mod.test/test/tagsbad@v0
   344  module: mod.test/test@v0
   345  root:   $CWD/testdata/testmod
   346  dir:    $CWD/testdata/testmod/tagsbad
   347  display:./tagsbad`}, {
   348  		name: "ImportCycle",
   349  		cfg: &Config{
   350  			Dir: testdataDir,
   351  		},
   352  		args: []string{"./cycle"},
   353  		want: `err:    import failed: import failed: import failed: package import cycle not allowed:
   354      $CWD/testdata/testmod/cycle/cycle.cue:3:8
   355      $CWD/testdata/testmod/cue.mod/pkg/mod.test/cycle/bar/bar.cue:3:8
   356      $CWD/testdata/testmod/cue.mod/pkg/mod.test/cycle/foo/foo.cue:3:8
   357  path:   mod.test/test/cycle@v0
   358  module: mod.test/test@v0
   359  root:   $CWD/testdata/testmod
   360  dir:    $CWD/testdata/testmod/cycle
   361  display:./cycle
   362  files:
   363      $CWD/testdata/testmod/cycle/cycle.cue`}, {
   364  		name: "AcceptLegacyModuleWithLegacyModule",
   365  		cfg: &Config{
   366  			Dir:                 testdata("testmod_legacy"),
   367  			AcceptLegacyModules: true,
   368  		},
   369  		want: `path:   test.example/foo@v0
   370  module: test.example/foo@v0
   371  root:   $CWD/testdata/testmod_legacy
   372  dir:    $CWD/testdata/testmod_legacy
   373  display:.
   374  files:
   375      $CWD/testdata/testmod_legacy/foo.cue`}, {
   376  		name: "AcceptLegacyModuleWithNonLegacyModule",
   377  		cfg: &Config{
   378  			Dir:                 testdataDir,
   379  			Tools:               true,
   380  			AcceptLegacyModules: true,
   381  		},
   382  		args: []string{"./imports"},
   383  		want: `path:   mod.test/test/imports@v0
   384  module: mod.test/test@v0
   385  root:   $CWD/testdata/testmod
   386  dir:    $CWD/testdata/testmod/imports
   387  display:./imports
   388  files:
   389      $CWD/testdata/testmod/imports/imports.cue
   390  imports:
   391      mod.test/catch: $CWD/testdata/testmod/cue.mod/pkg/mod.test/catch/catch.cue
   392      mod.test/helper:helper1: $CWD/testdata/testmod/cue.mod/pkg/mod.test/helper/helper1.cue`}, {
   393  		name: "MismatchedModulePathInConfig",
   394  		cfg: &Config{
   395  			Dir:    testdataDir,
   396  			Tools:  true,
   397  			Module: "wrong.test@v0",
   398  		},
   399  		args: []string{"./imports"},
   400  		want: `err:    inconsistent modules: got "mod.test/test@v0", want "wrong.test@v0"
   401  path:   ""
   402  module: wrong.test@v0
   403  root:   ""
   404  dir:    ""
   405  display:""`}, {
   406  		name: "ModulePathInConfigWithoutMajorVersion",
   407  		cfg: &Config{
   408  			Dir:    testdataDir,
   409  			Tools:  true,
   410  			Module: "mod.test/test",
   411  		},
   412  		args: []string{"./imports"},
   413  		want: `err:    inconsistent modules: got "mod.test/test@v0", want "mod.test/test"
   414  path:   ""
   415  module: mod.test/test
   416  root:   ""
   417  dir:    ""
   418  display:""`}, {
   419  		name: "ModulePathInConfigWithoutMajorVersionAndMismatchedPath",
   420  		cfg: &Config{
   421  			Dir:    testdataDir,
   422  			Tools:  true,
   423  			Module: "mod.test/wrong",
   424  		},
   425  		args: []string{"./imports"},
   426  		want: `err:    inconsistent modules: got "mod.test/test@v0", want "mod.test/wrong"
   427  path:   ""
   428  module: mod.test/wrong
   429  root:   ""
   430  dir:    ""
   431  display:""`}, {
   432  		name: "ExplicitPackageWithUnqualifiedImportPath#1",
   433  		cfg: &Config{
   434  			Dir:     filepath.Join(testdataDir, "multi"),
   435  			Package: "main",
   436  		},
   437  		args: []string{"."},
   438  		want: `path:   mod.test/test/multi@v0:main
   439  module: mod.test/test@v0
   440  root:   $CWD/testdata/testmod
   441  dir:    $CWD/testdata/testmod/multi
   442  display:.
   443  files:
   444      $CWD/testdata/testmod/multi/file.cue`}, {
   445  		name: "ExplicitPackageWithUnqualifiedImportPath#2",
   446  		// This test replicates the failure reported in https://cuelang.org/issue/3213
   447  		cfg: &Config{
   448  			Dir:     filepath.Join(testdataDir, "multi2"),
   449  			Package: "other",
   450  		},
   451  		args: []string{"."},
   452  		want: `path:   mod.test/test/multi2@v0:other
   453  module: mod.test/test@v0
   454  root:   $CWD/testdata/testmod
   455  dir:    $CWD/testdata/testmod/multi2
   456  display:.
   457  files:
   458      $CWD/testdata/testmod/multi2/other.cue
   459  imports:
   460      mod.test/test/sub: $CWD/testdata/testmod/sub/sub.cue`}, {
   461  		name: "ExplicitPackageWithUnqualifiedImportPath#3",
   462  		cfg: &Config{
   463  			Dir:     filepath.Join(testdataDir, "multi3"),
   464  			Package: "other",
   465  		},
   466  		args: []string{"."},
   467  		want: `path:   mod.test/test/multi3@v0:other
   468  module: mod.test/test@v0
   469  root:   $CWD/testdata/testmod
   470  dir:    $CWD/testdata/testmod/multi3
   471  display:.
   472  files:
   473      $CWD/testdata/testmod/multi3/other.cue
   474  imports:
   475      mod.test/test/sub: $CWD/testdata/testmod/sub/sub.cue`}, {
   476  		// Test that we can explicitly ask for non-package
   477  		// CUE files by setting Config.Package to "_".
   478  		name: "ExplicitPackageWithUnqualifiedImportPath#4",
   479  		cfg: &Config{
   480  			Dir:     filepath.Join(testdataDir, "multi4"),
   481  			Package: "_",
   482  		},
   483  		args: []string{"."},
   484  		want: `path:   mod.test/test/multi4@v0:_
   485  module: mod.test/test@v0
   486  root:   $CWD/testdata/testmod
   487  dir:    $CWD/testdata/testmod/multi4
   488  display:.
   489  files:
   490      $CWD/testdata/testmod/multi4/nopackage1.cue
   491      $CWD/testdata/testmod/multi4/nopackage2.cue`}, {
   492  		// Test what happens when there's a single CUE file
   493  		// with an explicit `package _` directive.
   494  		name: "ExplicitPackageWithUnqualifiedImportPath#5",
   495  		cfg: &Config{
   496  			Dir:     filepath.Join(testdataDir, "multi5"),
   497  			Package: "_",
   498  		},
   499  		args: []string{"."},
   500  		want: `path:   mod.test/test/multi5@v0:_
   501  module: mod.test/test@v0
   502  root:   $CWD/testdata/testmod
   503  dir:    $CWD/testdata/testmod/multi5
   504  display:.
   505  files:
   506      $CWD/testdata/testmod/multi5/nopackage.cue`}, {
   507  		// Check that imports are only considered from files
   508  		// that match the build paths.
   509  		name: "BuildTagsWithImports#1",
   510  		cfg: &Config{
   511  			Dir:  filepath.Join(testdataDir, "tagswithimports"),
   512  			Tags: []string{"prod"},
   513  		},
   514  		args: []string{"."},
   515  		want: `path:   mod.test/test/tagswithimports@v0
   516  module: mod.test/test@v0
   517  root:   $CWD/testdata/testmod
   518  dir:    $CWD/testdata/testmod/tagswithimports
   519  display:.
   520  files:
   521      $CWD/testdata/testmod/tagswithimports/prod.cue
   522  imports:
   523      mod.test/test/hello:test: $CWD/testdata/testmod/test.cue $CWD/testdata/testmod/hello/test.cue
   524      mod.test/test/sub: $CWD/testdata/testmod/sub/sub.cue`}, {
   525  		// Check that imports are only considered from files
   526  		// that match the build paths. When we don't have the prod
   527  		// tag, the bad import path mentioned in testdata/testmod/tagswithimports/nonprod.cue
   528  		// surfaces in the errors.
   529  		name: "BuildTagsWithImports#2",
   530  		cfg: &Config{
   531  			Dir: filepath.Join(testdataDir, "tagswithimports"),
   532  		},
   533  		args: []string{"."},
   534  		want: `err:    mod.test/test/tagswithimports@v0: import failed: cannot find package "bad-import.example/foo": cannot find module providing package bad-import.example/foo:
   535      $CWD/testdata/testmod/tagswithimports/nonprod.cue:5:8
   536  path:   mod.test/test/tagswithimports@v0
   537  module: mod.test/test@v0
   538  root:   $CWD/testdata/testmod
   539  dir:    $CWD/testdata/testmod/tagswithimports
   540  display:.
   541  files:
   542      $CWD/testdata/testmod/tagswithimports/nonprod.cue`}, {
   543  		name: "ModuleFileNonDirectory",
   544  		cfg: &Config{
   545  			Dir: testdata("testmod_legacymodfile"),
   546  		},
   547  		args: []string{"."},
   548  		want: `err:    cue.mod files are no longer supported; use cue.mod/module.cue
   549  path:   ""
   550  module: ""
   551  root:   ""
   552  dir:    ""
   553  display:""`}, {
   554  		// This test checks that files in parent directories
   555  		// do not result in irrelevant instances appearing
   556  		// in the result of Instances.
   557  		name: "Issue3306",
   558  		cfg: &Config{
   559  			Dir:         testdataDir,
   560  			Package:     "*",
   561  			SkipImports: true,
   562  		},
   563  		args: []string{"./issue3306/..."},
   564  		want: `path:   mod.test/test/issue3306@v0:x
   565  module: mod.test/test@v0
   566  root:   $CWD/testdata/testmod
   567  dir:    $CWD/testdata/testmod/issue3306
   568  display:./issue3306
   569  files:
   570      $CWD/testdata/testmod/issue3306/x.cue
   571  
   572  path:   mod.test/test/issue3306/a@v0:a
   573  module: mod.test/test@v0
   574  root:   $CWD/testdata/testmod
   575  dir:    $CWD/testdata/testmod/issue3306/a
   576  display:./issue3306/a
   577  files:
   578      $CWD/testdata/testmod/issue3306/a/a.cue
   579  
   580  path:   mod.test/test/issue3306/a@v0:b
   581  module: mod.test/test@v0
   582  root:   $CWD/testdata/testmod
   583  dir:    $CWD/testdata/testmod/issue3306/a
   584  display:./issue3306/a
   585  files:
   586      $CWD/testdata/testmod/issue3306/a/b.cue
   587  
   588  path:   mod.test/test/issue3306/a@v0:x
   589  module: mod.test/test@v0
   590  root:   $CWD/testdata/testmod
   591  dir:    $CWD/testdata/testmod/issue3306/a
   592  display:./issue3306/a
   593  files:
   594      $CWD/testdata/testmod/issue3306/x.cue
   595      $CWD/testdata/testmod/issue3306/a/x.cue
   596  
   597  path:   mod.test/test/issue3306/x@v0:x
   598  module: mod.test/test@v0
   599  root:   $CWD/testdata/testmod
   600  dir:    $CWD/testdata/testmod/issue3306/x
   601  display:./issue3306/x
   602  files:
   603      $CWD/testdata/testmod/issue3306/x.cue
   604      $CWD/testdata/testmod/issue3306/x/x.cue`}, {
   605  		// This test checks that when we use Package: "*",
   606  		// we can still use imported packages.
   607  		name: "AllPackagesWithImports",
   608  		cfg: &Config{
   609  			Dir:     testdataDir,
   610  			Package: "*",
   611  		},
   612  		args: []string{"."},
   613  		want: `path:   mod.test/test@v0:_
   614  module: mod.test/test@v0
   615  root:   $CWD/testdata/testmod
   616  dir:    $CWD/testdata/testmod
   617  display:.
   618  files:
   619      $CWD/testdata/testmod/anon.cue
   620  
   621  path:   mod.test/test@v0:test
   622  module: mod.test/test@v0
   623  root:   $CWD/testdata/testmod
   624  dir:    $CWD/testdata/testmod
   625  display:.
   626  files:
   627      $CWD/testdata/testmod/test.cue
   628  imports:
   629      mod.test/test/sub: $CWD/testdata/testmod/sub/sub.cue`}, {
   630  		// This tests that we can load a CUE package by pointing Dir to it
   631  		// even when the package's directory name ends with ".cue".
   632  		name: "DirWithCUEFileExtension",
   633  		cfg: &Config{
   634  			Dir: filepath.Join(testdataDir, "testdir.cue"),
   635  		},
   636  		args: []string{"."},
   637  		want: `path:   mod.test/test/testdir.cue@v0:testdir
   638  module: mod.test/test@v0
   639  root:   $CWD/testdata/testmod
   640  dir:    $CWD/testdata/testmod/testdir.cue
   641  display:.
   642  files:
   643      $CWD/testdata/testmod/testdir.cue/test.cue`,
   644  	}}
   645  	tdtest.Run(t, testCases, func(t *tdtest.T, tc *loadTest) {
   646  		pkgs := Instances(tc.args, tc.cfg)
   647  
   648  		buf := &bytes.Buffer{}
   649  		err := pkgInfo.Execute(buf, pkgs)
   650  		if err != nil {
   651  			t.Fatal(err)
   652  		}
   653  
   654  		got := strings.TrimSpace(buf.String())
   655  		got = strings.Replace(got, cwd, "$CWD", -1)
   656  		// Errors are printed with slashes, so replace
   657  		// the slash-separated form of CWD too.
   658  		got = strings.Replace(got, filepath.ToSlash(cwd), "$CWD", -1)
   659  		// Make test work with Windows.
   660  		got = strings.Replace(got, string(filepath.Separator), "/", -1)
   661  
   662  		t.Equal(got, tc.want)
   663  	})
   664  }
   665  
   666  var pkgInfo = template.Must(template.New("pkg").Funcs(template.FuncMap{
   667  	"errordetails": func(err error) string {
   668  		s := errors.Details(err, &errors.Config{
   669  			ToSlash: true,
   670  		})
   671  		s = strings.TrimSuffix(s, "\n")
   672  		return s
   673  	}}).Parse(`
   674  {{- range . -}}
   675  {{- if .Err}}err:    {{errordetails .Err}}{{end}}
   676  path:   {{if .ImportPath}}{{.ImportPath}}{{else}}""{{end}}
   677  module: {{with .Module}}{{.}}{{else}}""{{end}}
   678  root:   {{with .Root}}{{.}}{{else}}""{{end}}
   679  dir:    {{with .Dir}}{{.}}{{else}}""{{end}}
   680  display:{{with .DisplayPath}}{{.}}{{else}}""{{end}}
   681  {{if .Files -}}
   682  files:
   683  {{- range .Files}}
   684      {{.Filename}}
   685  {{- end -}}
   686  {{- end}}
   687  {{if .Imports -}}
   688  imports:
   689  {{- range .Dependencies}}
   690      {{.ImportPath}}:{{range .Files}} {{.Filename}}{{end}}
   691  {{- end}}
   692  {{end -}}
   693  {{- end -}}
   694  `))
   695  
   696  func TestOverlays(t *testing.T) {
   697  	cwd, _ := os.Getwd()
   698  	abs := func(path string) string {
   699  		return filepath.Join(cwd, path)
   700  	}
   701  	c := &Config{
   702  		Overlay: map[string]Source{
   703  			// Not necessary, but nice to add.
   704  			abs("cue.mod/module.cue"): FromString(`module: "mod.test", language: version: "v0.9.0"`),
   705  
   706  			abs("dir/top.cue"): FromBytes([]byte(`
   707  			   package top
   708  			   msg: "Hello"
   709  			`)),
   710  			abs("dir/b/foo.cue"): FromString(`
   711  			   package foo
   712  
   713  			   a: <= 5
   714  			`),
   715  			abs("dir/b/bar.cue"): FromString(`
   716  			   package foo
   717  
   718  			   a: >= 5
   719  			`),
   720  		},
   721  	}
   722  	want := []string{
   723  		`{msg:"Hello"}`,
   724  		`{a:5}`,
   725  	}
   726  	rmSpace := func(r rune) rune {
   727  		if unicode.IsSpace(r) {
   728  			return -1
   729  		}
   730  		return r
   731  	}
   732  	ctx := cuecontext.New()
   733  	insts, err := ctx.BuildInstances(Instances([]string{"./dir/..."}, c))
   734  	if err != nil {
   735  		t.Fatal(err)
   736  	}
   737  	for i, inst := range insts {
   738  		if err := inst.Err(); err != nil {
   739  			t.Error(err)
   740  			continue
   741  		}
   742  		b, err := format.Node(inst.Value().Syntax(cue.Final()))
   743  		if err != nil {
   744  			t.Error(err)
   745  			continue
   746  		}
   747  		if got := string(bytes.Map(rmSpace, b)); got != want[i] {
   748  			t.Errorf("%s: got %s; want %s", inst.BuildInstance().Dir, got, want[i])
   749  		}
   750  	}
   751  }
   752  
   753  func TestLoadOrder(t *testing.T) {
   754  	testDir := t.TempDir()
   755  	letters := "abcdefghij"
   756  
   757  	for _, c := range letters {
   758  		contents := fmt.Sprintf(`
   759  package %s
   760  
   761  x: 1
   762  `, string(c))
   763  		err := os.WriteFile(filepath.Join(testDir, string(c)+".cue"), []byte(contents), 0o666)
   764  		qt.Assert(t, qt.IsNil(err))
   765  	}
   766  
   767  	insts := Instances([]string{"."}, &Config{
   768  		Package: "*",
   769  		Dir:     testDir,
   770  	})
   771  
   772  	var actualFiles = []string{}
   773  	for _, inst := range insts {
   774  		for _, f := range inst.BuildFiles {
   775  			if strings.Contains(f.Filename, testDir) {
   776  				actualFiles = append(actualFiles, filepath.Base(f.Filename))
   777  			}
   778  		}
   779  	}
   780  	var expectedFiles []string
   781  	for _, c := range letters {
   782  		expectedFiles = append(expectedFiles, string(c)+".cue")
   783  	}
   784  	qt.Assert(t, qt.DeepEquals(actualFiles, expectedFiles))
   785  }
   786  
   787  func TestLoadInstancesConcurrent(t *testing.T) {
   788  	// This test is designed to fail when run with the race detector
   789  	// if there's an underlying race condition.
   790  	// See https://cuelang.org/issue/1746
   791  	race(t, func() error {
   792  		_, err := getInst(".", testdata("testmod", "hello"))
   793  		return err
   794  	})
   795  }
   796  
   797  func race(t *testing.T, f func() error) {
   798  	var wg sync.WaitGroup
   799  	for i := 0; i < 2; i++ {
   800  		wg.Add(1)
   801  		go func() {
   802  			if err := f(); err != nil {
   803  				t.Error(err)
   804  			}
   805  			wg.Done()
   806  		}()
   807  	}
   808  	wg.Wait()
   809  }