...

Source file src/github.com/linkerd/linkerd2/testutil/test_data_diff.go

Documentation: github.com/linkerd/linkerd2/testutil

     1  package testutil
     2  
     3  import (
     4  	"bytes"
     5  	"errors"
     6  	"fmt"
     7  	"io"
     8  	"os"
     9  	"path/filepath"
    10  	"testing"
    11  	"text/template"
    12  
    13  	"github.com/go-test/deep"
    14  	"github.com/sergi/go-diff/diffmatchpatch"
    15  	"gopkg.in/yaml.v2"
    16  )
    17  
    18  // TestDataDiffer holds configuration for generating test diff
    19  type TestDataDiffer struct {
    20  	PrettyDiff     bool
    21  	UpdateFixtures bool
    22  	RejectPath     string
    23  }
    24  
    25  // DiffTestYAML compares a YAML structure to a fixture on the filestystem.
    26  func (td *TestDataDiffer) DiffTestYAML(path string, actualYAML string) error {
    27  	expectedYAML := ReadTestdata(path)
    28  	return td.diffTestYAML(path, actualYAML, expectedYAML)
    29  }
    30  
    31  // DiffTestYAMLTemplate compares a YAML structure to a parameterized fixture on the filestystem.
    32  func (td *TestDataDiffer) DiffTestYAMLTemplate(path string, actualYAML string, params any) error {
    33  	file := filepath.Join("testdata", path)
    34  	t, err := template.New(path).ParseFiles(file)
    35  	if err != nil {
    36  		return fmt.Errorf("Failed to read YAML template from %s: %w", path, err)
    37  	}
    38  	var buf bytes.Buffer
    39  	err = t.Execute(&buf, params)
    40  	if err != nil {
    41  		return fmt.Errorf("Failed to build YAML from template %s: %w", path, err)
    42  	}
    43  	return td.diffTestYAML(path, actualYAML, buf.String())
    44  }
    45  
    46  func (td *TestDataDiffer) diffTestYAML(path, actualYAML, expectedYAML string) error {
    47  	actual, err := unmarshalYAML([]byte(actualYAML))
    48  	if err != nil {
    49  		return fmt.Errorf("Failed to unmarshal generated YAML: %w", err)
    50  	}
    51  	expected, err := unmarshalYAML([]byte(expectedYAML))
    52  	if err != nil {
    53  		return fmt.Errorf("Failed to unmarshal expected YAML: %w", err)
    54  	}
    55  	diff := deep.Equal(expected, actual)
    56  	if diff == nil {
    57  		return nil
    58  	}
    59  	td.storeActual(path, []byte(actualYAML))
    60  	e := fmt.Sprintf("YAML mismatches %s:", path)
    61  	for _, d := range diff {
    62  		e += fmt.Sprintf("\n	%s", d)
    63  	}
    64  	return errors.New(e)
    65  }
    66  
    67  // DiffTestdata generates the diff for actual w.r.the file in path
    68  func (td *TestDataDiffer) DiffTestdata(t *testing.T, path, actual string) {
    69  	t.Helper()
    70  	expected := ReadTestdata(path)
    71  	if actual == expected {
    72  		return
    73  	}
    74  	dmp := diffmatchpatch.New()
    75  	diffs := dmp.DiffMain(expected, actual, true)
    76  	diffs = dmp.DiffCleanupSemantic(diffs)
    77  	var diff string
    78  	if td.PrettyDiff {
    79  		diff = dmp.DiffPrettyText(diffs)
    80  	} else {
    81  		diff = dmp.PatchToText(dmp.PatchMake(diffs))
    82  	}
    83  	t.Errorf("mismatch: %s\n%s", path, diff)
    84  
    85  	td.storeActual(path, []byte(actual))
    86  }
    87  
    88  func (td *TestDataDiffer) storeActual(path string, actual []byte) {
    89  	if td.UpdateFixtures {
    90  		writeTestdata(path, actual)
    91  	}
    92  
    93  	if td.RejectPath != "" {
    94  		writeRejects(path, actual, td.RejectPath)
    95  	}
    96  }
    97  
    98  // ReadTestdata reads a file and returns the contents of that file as a string.
    99  func ReadTestdata(fileName string) string {
   100  	file, err := os.Open(filepath.Join("testdata", fileName))
   101  	if err != nil {
   102  		panic(fmt.Sprintf("Failed to open expected output file: %v", err))
   103  	}
   104  
   105  	fixture, err := io.ReadAll(file)
   106  	if err != nil {
   107  		panic(fmt.Sprintf("Failed to read expected output file: %v", err))
   108  	}
   109  
   110  	return string(fixture)
   111  }
   112  
   113  func unmarshalYAML(data []byte) ([]interface{}, error) {
   114  	objs := make([]interface{}, 0)
   115  	rd := bytes.NewReader(data)
   116  	decoder := yaml.NewDecoder(rd)
   117  	for {
   118  		var obj interface{}
   119  		if err := decoder.Decode(&obj); err != nil {
   120  			if errors.Is(err, io.EOF) {
   121  				return objs, nil
   122  			}
   123  			return nil, err
   124  		}
   125  
   126  		objs = append(objs, obj)
   127  	}
   128  }
   129  
   130  func writeTestdata(fileName string, data []byte) {
   131  	p := filepath.Join("testdata", fileName)
   132  	if err := os.WriteFile(p, data, 0600); err != nil {
   133  		panic(err)
   134  	}
   135  }
   136  
   137  func writeRejects(origFileName string, data []byte, rejectPath string) {
   138  	p := filepath.Join(rejectPath, origFileName+".rej")
   139  	if err := os.WriteFile(p, data, 0600); err != nil {
   140  		panic(err)
   141  	}
   142  }
   143  

View as plain text