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  }