src.elv.sh@v0.21.0-dev.0.20240515223629-06979efb9a2a/pkg/tt/tt_test.go (about)

     1  package tt
     2  
     3  import (
     4  	"fmt"
     5  	"strings"
     6  	"testing"
     7  )
     8  
     9  // Simple functions to test.
    10  
    11  func add(x, y int) int {
    12  	return x + y
    13  }
    14  
    15  func addsub(x int, y int) (int, int) {
    16  	return x + y, x - y
    17  }
    18  
    19  func TestTest(t *testing.T) {
    20  	Test(t, test,
    21  		It("reports no errors for passing tests").
    22  			Args(add, Args(1, 1).Rets(2)).
    23  			Rets([]testResult{{"", nil}}),
    24  		It("supports multiple tests").
    25  			Args(add, Args(1, 1).Rets(2), Args(1, 2).Rets(3)),
    26  		It("supports for multiple return values").
    27  			Args(addsub, Args(1, 2).Rets(3, -1)).
    28  			Rets([]testResult{{"", nil}}),
    29  		It("supports named tests").
    30  			Args(add, It("can add 1 and 1").Args(1, 1).Rets(2)).
    31  			Rets([]testResult{{"can add 1 and 1", nil}}),
    32  
    33  		It("reports error for failed test").
    34  			Args(Fn(add).Named("add"), Args(2, 2).Rets(5)).
    35  			Rets(testResultsMatcher{
    36  				{"", []string{"add(2, 2) returns (-want +got):\n"}},
    37  			}),
    38  		It("respects custom argument format strings when reporting errors").
    39  			Args(Fn(add).Named("add").ArgsFmt("x = %d, y = %d"), Args(1, 2).Rets(5)).
    40  			Rets(testResultsMatcher{
    41  				{"", []string{"add(x = 1, y = 2) returns (-want +got):\n"}},
    42  			}),
    43  	)
    44  }
    45  
    46  // An alternative to the exported [Test] that uses a mock test runner that
    47  // collects results from all the subtests.
    48  func test(fn any, tests ...*Case) []testResult {
    49  	var tr mockTestRunner
    50  	testInner[*mockSubtestRunner](&tr, fn, tests...)
    51  	return tr
    52  }
    53  
    54  // Mock implementations of testRunner and subtestRunner.
    55  
    56  type testResult struct {
    57  	Name   string
    58  	Errors []string
    59  }
    60  
    61  type mockTestRunner []testResult
    62  
    63  func (tr *mockTestRunner) Helper() {}
    64  
    65  func (tr *mockTestRunner) Run(name string, f func(*mockSubtestRunner)) bool {
    66  	sr := mockSubtestRunner{name, nil}
    67  	f(&sr)
    68  	*tr = append(*tr, testResult(sr))
    69  	return len(sr.Errors) == 0
    70  }
    71  
    72  type mockSubtestRunner testResult
    73  
    74  func (sr *mockSubtestRunner) Errorf(format string, args ...any) {
    75  	sr.Errors = append(sr.Errors, fmt.Sprintf(format, args...))
    76  }
    77  
    78  // Matches []testResult, but doesn't check the exact content of the error
    79  // messages, only that they contain a substring.
    80  type testResultsMatcher []testResult
    81  
    82  func (m testResultsMatcher) Match(ret RetValue) bool {
    83  	results, ok := ret.([]testResult)
    84  	if !ok {
    85  		return false
    86  	}
    87  	if len(results) != len(m) {
    88  		return false
    89  	}
    90  	for i, result := range results {
    91  		if result.Name != m[i].Name || len(result.Errors) != len(m[i].Errors) {
    92  			return false
    93  		}
    94  		for i, s := range result.Errors {
    95  			if !strings.Contains(s, m[i].Errors[i]) {
    96  				return false
    97  			}
    98  		}
    99  	}
   100  	return true
   101  }