kythe.io@v0.0.68-0.20240422202219-7225dbc01741/kythe/go/test/testutil/testutil.go (about)

     1  /*
     2   * Copyright 2015 The Kythe Authors. All rights reserved.
     3   *
     4   * Licensed under the Apache License, Version 2.0 (the "License");
     5   * you may not use this file except in compliance with the License.
     6   * You may obtain a copy of the License at
     7   *
     8   *   http://www.apache.org/licenses/LICENSE-2.0
     9   *
    10   * Unless required by applicable law or agreed to in writing, software
    11   * distributed under the License is distributed on an "AS IS" BASIS,
    12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13   * See the License for the specific language governing permissions and
    14   * limitations under the License.
    15   */
    16  
    17  // Package testutil contains common utilities to test Kythe libraries.
    18  package testutil // import "kythe.io/kythe/go/test/testutil"
    19  
    20  import (
    21  	"encoding/json"
    22  	"flag"
    23  	"fmt"
    24  	"math/rand"
    25  	"os"
    26  	"path/filepath"
    27  	"regexp"
    28  	"runtime"
    29  	"strings"
    30  	"testing"
    31  
    32  	"kythe.io/kythe/go/util/compare"
    33  
    34  	"github.com/google/go-cmp/cmp"
    35  	"sigs.k8s.io/yaml"
    36  )
    37  
    38  // DeepEqual determines if expected is deeply equal to got, returning a
    39  // detailed error if not. It is okay for expected and got to be protobuf
    40  // message values.
    41  func DeepEqual[T any](expected, got T, opts ...cmp.Option) error {
    42  	if diff := compare.ProtoDiff(expected, got, opts...); diff != "" {
    43  		return fmt.Errorf("(-expected; +found)\n%s", diff)
    44  	}
    45  	return nil
    46  }
    47  
    48  var multipleNewLines = regexp.MustCompile("\n{2,}")
    49  
    50  // TrimmedEqual compares two strings after collapsing irrelevant whitespace at
    51  // the beginning or end of lines. It returns both a boolean indicating equality,
    52  // as well as any relevant diff.
    53  func TrimmedEqual(got, want []byte) (bool, string) {
    54  	// remove superfluous whitespace
    55  	gotStr := strings.Trim(string(got[:]), " \n")
    56  	wantStr := strings.Trim(string(want[:]), " \n")
    57  	gotStr = multipleNewLines.ReplaceAllString(gotStr, "\n")
    58  	wantStr = multipleNewLines.ReplaceAllString(wantStr, "\n")
    59  
    60  	// diff want vs got
    61  	diff := cmp.Diff(gotStr, wantStr)
    62  	return diff == "", diff
    63  }
    64  
    65  // YAMLEqual compares two bytes assuming they are yaml, by converting to json
    66  // and doing an ordering-agnostic comparison.  Note this carries some
    67  // restrictions because yaml->json conversion fails for nil or binary map keys.
    68  func YAMLEqual(expected, got []byte) error {
    69  	e, err := yaml.YAMLToJSON(expected)
    70  	if err != nil {
    71  		return fmt.Errorf("yaml->json failure for expected: %v", err)
    72  	}
    73  	g, err := yaml.YAMLToJSON(got)
    74  	if err != nil {
    75  		return fmt.Errorf("yaml->json failure for got: %v", err)
    76  	}
    77  	return JSONEqual(e, g)
    78  }
    79  
    80  // JSONEqual compares two bytes assuming they are json, using encoding/json
    81  // and DeepEqual.
    82  func JSONEqual(expected, got []byte) error {
    83  	var e, g any
    84  	if err := json.Unmarshal(expected, &e); err != nil {
    85  		return fmt.Errorf("decoding expected json: %v", err)
    86  	}
    87  	if err := json.Unmarshal(got, &g); err != nil {
    88  		return fmt.Errorf("decoding got json: %v", err)
    89  	}
    90  	return DeepEqual(e, g)
    91  }
    92  
    93  func caller(up int) (file string, line int) {
    94  	_, file, line, ok := runtime.Caller(up + 2)
    95  	if !ok {
    96  		panic("could not get runtime.Caller")
    97  	}
    98  	return filepath.Base(file), line
    99  }
   100  
   101  // Errorf is equivalent to t.Errorf(msg, err, args...) if err != nil.
   102  func Errorf(t testing.TB, msg string, err error, args ...any) {
   103  	if err != nil {
   104  		t.Helper()
   105  		t.Errorf(msg, append([]any{err}, args...)...)
   106  	}
   107  }
   108  
   109  // Fatalf is equivalent to t.Fatalf(msg, err, args...) if err != nil.
   110  func Fatalf(t testing.TB, msg string, err error, args ...any) {
   111  	if err != nil {
   112  		t.Helper()
   113  		t.Fatalf(msg, append([]any{err}, args...)...)
   114  	}
   115  }
   116  
   117  // RandStr returns a random string of the given length
   118  func RandStr(size int) string {
   119  	const chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
   120  	buf := make([]byte, size)
   121  	RandBytes(buf)
   122  	for i, b := range buf {
   123  		buf[i] = chars[b%byte(len(chars))]
   124  	}
   125  	return string(buf)
   126  }
   127  
   128  // RandBytes fills the given slice with random bytes
   129  func RandBytes(bytes []byte) {
   130  	i := len(bytes) - 1
   131  	for {
   132  		n := rand.Int63()
   133  		for j := 0; j < 8; j++ {
   134  			bytes[i] = byte(n)
   135  			i--
   136  			if i == -1 {
   137  				return
   138  			}
   139  			n >>= 8
   140  		}
   141  	}
   142  }
   143  
   144  // TestFilePath takes a path and resolves it based on the testdir.  If it
   145  // cannot successfully do so, it calls t.Fatal and abandons.
   146  func TestFilePath(t *testing.T, path string) string {
   147  	t.Helper()
   148  	pwd, err := os.Getwd()
   149  	if err != nil {
   150  		t.Fatalf("Failed to resolve path %s: %v", path, err)
   151  	}
   152  	return filepath.Join(pwd, filepath.FromSlash(path))
   153  }
   154  
   155  // SetFlag sets a flag to a value for the duration of the test, resetting it
   156  // to the flag's default value during cleanup.
   157  func SetFlag(t testing.TB, flagName string, value string) {
   158  	f := flag.Lookup(flagName)
   159  	if err := f.Value.Set(value); err != nil {
   160  		t.Fatalf("Error setting --%s: %v", flagName, err)
   161  	}
   162  	t.Cleanup(func() { f.Value.Set(f.DefValue) })
   163  }