| // Copyright 2025 The LUCI Authors. |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| package lucicfg |
| |
| import ( |
| "context" |
| "os" |
| "path/filepath" |
| "testing" |
| |
| "go.chromium.org/luci/common/testing/truth/assert" |
| |
| "go.chromium.org/luci/lucicfg/pkg" |
| ) |
| |
| // TestPkg executes package tests (testdata/pkg/*, one per subdirectory). |
| func TestPkg(t *testing.T) { |
| t.Parallel() |
| |
| dirs, err := os.ReadDir(filepath.Join("testdata", "pkg")) |
| assert.NoErr(t, err) |
| |
| gotExpectationErrors := false |
| for _, dir := range dirs { |
| t.Run(dir.Name(), func(t *testing.T) { |
| if ok := runPkgTest(t, filepath.Join("testdata", "pkg", dir.Name())); !ok { |
| gotExpectationErrors = true |
| } |
| }) |
| } |
| |
| if gotExpectationErrors { |
| t.Errorf("\n\n"+ |
| "========================================================\n"+ |
| "If you want to update expectations stored in *.star run:\n"+ |
| "$ %s=1 go test .\n"+ |
| "========================================================", RegenEnvVar) |
| } |
| } |
| |
| // runPkgTest runs main.star of some single package. |
| // |
| // Returns false if the output expectation needs to be regenerated. |
| func runPkgTest(t *testing.T, dir string) bool { |
| // Note: avoid t.Parallel() because we want the test to finish synchronously |
| // in TestPkg to check gotExpectationErrors. |
| |
| main := filepath.Join(dir, "main.star") |
| ctx := context.Background() |
| |
| blob, err := os.ReadFile(main) |
| assert.NoErr(t, err) |
| body := string(blob) |
| |
| expectErrExct := readCommentBlock(body, expectErrorsHeader) |
| expectErrLike := readCommentBlock(body, expectErrorsLikeHeader) |
| expectCfg := readCommentBlock(body, expectConfigsHeader) |
| if expectErrExct != "" && expectErrLike != "" { |
| t.Errorf("Cannot use %q and %q at the same time", expectErrorsHeader, expectErrorsLikeHeader) |
| return true |
| } |
| |
| var repoMgr pkg.RepoManager |
| depsDir := filepath.Join(dir, "remote") |
| if _, err := os.Stat(depsDir); err == nil { |
| repoMgr = &pkg.TestRepoManager{Root: depsDir} |
| } |
| |
| var state *State |
| entry, err := pkg.EntryOnDisk(ctx, main, repoMgr) |
| |
| // Verify formatter is initialized.. |
| if err == nil && entry.Local.Formatter != nil { |
| err = entry.Local.Formatter.CheckValid(ctx) |
| if err == nil { |
| _, err = entry.Local.Formatter.RewriterForPath(ctx, "main.star") |
| } |
| } |
| |
| if err == nil { |
| state, err = Generate(ctx, Inputs{ |
| Entry: entry, |
| |
| // Don't spit out "# This file is generated by lucicfg" headers. |
| testOmitHeader: true, |
| // Do not put frequently changing version string into test outputs. |
| testVersion: "1.1.1", |
| }) |
| } |
| |
| // If test was expected to fail, make sure it did, in an expected way. |
| if expectErrExct != "" || expectErrLike != "" { |
| checkExpectedErrs(t, err, expectErrExct, expectErrLike) |
| return true |
| } |
| |
| // Otherwise just report all errors (if any) to Mr. T. |
| reportErr(t, err) |
| if err != nil { |
| return true // the error has been reported already |
| } |
| |
| // If was expecting to see some configs, assert we did see them. |
| if expectCfg != "" { |
| return checkOrRegenExpectedOut(t, main, &state.Output, expectCfg) |
| } |
| return true |
| } |