cuelang.org/go@v0.13.0/internal/cuetest/cuetest.go (about) 1 // Copyright 2021 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 testing is a helper package for test packages in the CUE project. 16 // As such it should only be imported in _test.go files. 17 package cuetest 18 19 import ( 20 "fmt" 21 "os" 22 "regexp" 23 "testing" 24 25 "cuelang.org/go/internal/tdtest" 26 ) 27 28 const ( 29 envUpdate = "CUE_UPDATE" 30 31 // envNonIssues can be set to a regular expression which indicates what 32 // issues we no longer consider issues, i.e. they should have been fixed. 33 // This should generally result in tests that would otherwise be skipped no 34 // longer being skipped. e.g. CUE_NON_ISSUES=. will cause all issue 35 // tracker conditions (e.g. [golang.org/issues/1234]) to be considered 36 // non-issues. 37 envNonIssues = "CUE_NON_ISSUES" 38 39 envFormatTxtar = "CUE_FORMAT_TXTAR" 40 ) 41 42 var ( 43 // issuesConditions is a set of regular expressions that defines the set of 44 // conditions that can be used to declare links to issues in various issue 45 // trackers. e.g. in testscript condition form 46 // 47 // [golang.org/issues/1234] 48 // [github.com/govim/govim/issues/4321] 49 issuesConditions = []*regexp.Regexp{ 50 regexp.MustCompile(`^golang\.org/issues?/\d+$`), 51 regexp.MustCompile(`^cuelang\.org/issues?/\d+$`), 52 } 53 ) 54 55 // UpdateGoldenFiles determines whether tests should update expected 56 // output in test files in the event of comparison failures (for example 57 // after a cmp failure in a testscript-based test). It is controlled by 58 // setting CUE_UPDATE to a non-empty string like "1" or "true". It 59 // corresponds to testscript.Params.UpdateGoldenFiles; see its docs for 60 // details. 61 // 62 // In some cases, tests might refuse to perform some updates by default. 63 // The special value "force" can be used to force updates in that situation. 64 var UpdateGoldenFiles = os.Getenv(envUpdate) != "" 65 66 // ForceUpdateGoldenFiles determines whether tests should update 67 // expected output in test files even when they would not be updated 68 // usually (for example when there are test regressions). 69 var ForceUpdateGoldenFiles = os.Getenv(envUpdate) == "force" 70 71 // FormatTxtar ensures that .cue files in txtar test archives are well 72 // formatted, updating the archive as required prior to running a test. 73 // It is controlled by setting CUE_FORMAT_TXTAR to a non-empty string like "true". 74 var FormatTxtar = os.Getenv(envFormatTxtar) != "" 75 76 // Condition adds support for CUE-specific testscript conditions within 77 // testscript scripts. Supported conditions include: 78 // 79 // [golang.org/issue/N] - evaluates to true unless CUE_NON_ISSUES 80 // is set to a regexp that matches the condition, i.e. golang.org/issue/N 81 // in this case 82 // 83 // [cuelang.org/issue/N] - evaluates to true unless CUE_NON_ISSUES 84 // is set to a regexp that matches the condition, i.e. cuelang.org/issue/N 85 // in this case 86 func Condition(cond string) (bool, error) { 87 isIssue, nonIssue, err := checkIssueCondition(cond) 88 if err != nil { 89 return false, err 90 } 91 if isIssue { 92 return !nonIssue, nil 93 } 94 return false, fmt.Errorf("unknown condition %v", cond) 95 } 96 97 // T is an alias to tdtest.T 98 type T = tdtest.T 99 100 func init() { 101 tdtest.UpdateTests = UpdateGoldenFiles 102 } 103 104 // Run creates a new table-driven test using the CUE testing defaults. 105 // 106 // TODO: move this wrapper out to cuetdtest. Users should either use the full 107 // version of tdtest directly, or use the cuetdtest wrapper. 108 func Run[TC any](t *testing.T, table []TC, fn func(t *T, tc *TC)) { 109 tdtest.Run(t, table, fn) 110 } 111 112 // IssueSkip causes the test t to be skipped unless the issue identified 113 // by s is deemed to be a non-issue by CUE_NON_ISSUES. 114 func IssueSkip(t *testing.T, s string) { 115 t.Helper() 116 117 isIssue, nonIssue, err := checkIssueCondition(s) 118 if err != nil { 119 t.Fatal(err) 120 } 121 if !isIssue { 122 t.Fatalf("issue %q does not match a known issue pattern", s) 123 } 124 if nonIssue { 125 t.Skipf("issue %s", s) 126 } 127 } 128 129 // checkIssueCondition examines s to determine whether it is an issue 130 // condition, in which case isIssue is true. If isIssue, then we check 131 // CUE_NON_ISSUES for a match, in which case nonIssue is true (a value of true 132 // indicates roughly that we don't believe issue s is an issue any more). In 133 // case of any errors err is set. 134 func checkIssueCondition(s string) (isIssue bool, nonIssue bool, err error) { 135 var r *regexp.Regexp 136 if v := os.Getenv(envNonIssues); v != "" { 137 r, err = regexp.Compile(v) 138 if err != nil { 139 return false, false, fmt.Errorf("failed to compile regexp %q specified via %v: %v", v, envNonIssues, err) 140 } 141 } 142 for _, c := range issuesConditions { 143 if c.MatchString(s) { 144 isIssue = true 145 } 146 } 147 if !isIssue { 148 return false, false, nil 149 } 150 return isIssue, r != nil && r.MatchString(s), nil 151 }