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