github.com/mirantis/virtlet@v1.5.2-0.20191204181327-1659b8a48e9b/tests/gm/data.go (about)

     1  /*
     2  Copyright 2017 Mirantis
     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 gm
    18  
    19  import (
    20  	"bytes"
    21  	"encoding/json"
    22  	"fmt"
    23  	"io/ioutil"
    24  	"os"
    25  	"path/filepath"
    26  	"reflect"
    27  	"regexp"
    28  	"strings"
    29  
    30  	"github.com/davecgh/go-spew/spew"
    31  	"github.com/ghodss/yaml"
    32  	"github.com/golang/glog"
    33  )
    34  
    35  const (
    36  	jsonDataIndent = "  "
    37  )
    38  
    39  // Verifier describes a type that can verify its contents
    40  // against a Golden Master data file and also generate
    41  // the contents of such file
    42  type Verifier interface {
    43  	// Suffix returns the suffix for the file name of the Golden Master
    44  	// data file for this value.
    45  	Suffix() string
    46  	// Marshal generates the contents of the Golden Master data file.
    47  	Marshal() ([]byte, error)
    48  	// Verify returns true if the contents can be considered
    49  	// the same as the value of the Verifier. It should not return
    50  	// an error if content is invalid.
    51  	Verify(content []byte) (bool, error)
    52  }
    53  
    54  type textVerifier string
    55  
    56  var _ Verifier = textVerifier("")
    57  
    58  func (v textVerifier) Suffix() string {
    59  	return ".txt"
    60  }
    61  
    62  func (v textVerifier) Verify(content []byte) (bool, error) {
    63  	return string(v) == string(content), nil
    64  }
    65  
    66  func (v textVerifier) Marshal() ([]byte, error) {
    67  	return []byte(v), nil
    68  }
    69  
    70  type JSONVerifier struct {
    71  	data interface{}
    72  }
    73  
    74  var _ Verifier = JSONVerifier{}
    75  
    76  func NewJSONVerifier(data interface{}) JSONVerifier {
    77  	return JSONVerifier{data}
    78  }
    79  
    80  func (v JSONVerifier) Suffix() string {
    81  	return ".json"
    82  }
    83  
    84  func (v JSONVerifier) Verify(content []byte) (bool, error) {
    85  	var curData interface{}
    86  	if err := json.Unmarshal(content, &curData); err != nil {
    87  		glog.Warningf("Failed to unmarshal to JSON: %v:\n%s", err, content)
    88  		return false, nil
    89  	}
    90  
    91  	out, err := json.Marshal(v.data)
    92  	if err != nil {
    93  		return false, fmt.Errorf("failed to marshal data: %v. Input:\n%s",
    94  			err, spew.Sdump(v))
    95  	}
    96  
    97  	var newData interface{}
    98  	if err := json.Unmarshal(out, &newData); err != nil {
    99  		return false, fmt.Errorf("failed to unmarshal back marshalled value: %v. JSON:\n%s\nOriginal data:\n%s",
   100  			err, string(out), spew.Sdump(v))
   101  	}
   102  
   103  	return reflect.DeepEqual(curData, newData), nil
   104  }
   105  
   106  func (v JSONVerifier) Marshal() ([]byte, error) {
   107  	switch d := v.data.(type) {
   108  	case []byte:
   109  		return d, nil
   110  	case string:
   111  		return []byte(d), nil
   112  	default:
   113  		out, err := json.MarshalIndent(v.data, "", jsonDataIndent)
   114  		if err != nil {
   115  			return nil, fmt.Errorf("failed to marshal json data: %v. Input:\n%s",
   116  				err, spew.Sdump(v.data))
   117  		}
   118  		return out, nil
   119  	}
   120  }
   121  
   122  // YamlVerifier verifies the data using YAML representation.
   123  type YamlVerifier struct {
   124  	data interface{}
   125  }
   126  
   127  var _ Verifier = YamlVerifier{}
   128  
   129  // NewYamlVerifier makes a YamlVerifier with the specified content.
   130  func NewYamlVerifier(data interface{}) YamlVerifier {
   131  	return YamlVerifier{data}
   132  }
   133  
   134  // Suffix implements Suffix method of the Verifier interface.
   135  func (v YamlVerifier) Suffix() string {
   136  	return ".yaml"
   137  }
   138  
   139  func marshalMultiple(data []interface{}) ([]byte, error) {
   140  	var out bytes.Buffer
   141  	for _, v := range data {
   142  		bs, err := yaml.Marshal(v)
   143  		if err != nil {
   144  			return nil, err
   145  		}
   146  		fmt.Fprintf(&out, "---\n%s", bs)
   147  	}
   148  	return out.Bytes(), nil
   149  }
   150  
   151  func unmarshalMultiple(in []byte) ([]interface{}, error) {
   152  	var r []interface{}
   153  	for _, part := range bytes.Split(in, []byte("---\n")) {
   154  		if len(bytes.TrimSpace(part)) == 0 {
   155  			continue
   156  		}
   157  		var data interface{}
   158  		if err := yaml.Unmarshal(part, &data); err != nil {
   159  			return nil, err
   160  		}
   161  		r = append(r, data)
   162  	}
   163  	return r, nil
   164  }
   165  
   166  func (v YamlVerifier) verifyMultiple(content []byte) (bool, error) {
   167  	curData, err := unmarshalMultiple(content)
   168  	if err != nil {
   169  		glog.Warningf("Failed to unmarshal to YAML: %v:\n%s", err, content)
   170  		return false, nil
   171  	}
   172  
   173  	out, err := v.Marshal()
   174  	if err != nil {
   175  		return false, err
   176  	}
   177  
   178  	newData, err := unmarshalMultiple(out)
   179  	if err != nil {
   180  		return false, fmt.Errorf("failed to unmarshal back marshalled value: %v. YAML:\n%s\nOriginal data:\n%s",
   181  			err, string(out), content)
   182  	}
   183  
   184  	return reflect.DeepEqual(curData, newData), nil
   185  }
   186  
   187  // Verify implements Verify method of the Verifier interface.
   188  func (v YamlVerifier) Verify(content []byte) (bool, error) {
   189  	switch v.data.(type) {
   190  	case []byte:
   191  		return v.verifyMultiple(content)
   192  	case string:
   193  		return v.verifyMultiple(content)
   194  	}
   195  
   196  	var curData interface{}
   197  	if err := yaml.Unmarshal(content, &curData); err != nil {
   198  		glog.Warningf("Failed to unmarshal to YAML: %v:\n%s", err, content)
   199  		return false, nil
   200  	}
   201  
   202  	out, err := v.Marshal()
   203  	if err != nil {
   204  		return false, err
   205  	}
   206  
   207  	var newData interface{}
   208  	if err := yaml.Unmarshal(out, &newData); err != nil {
   209  		return false, fmt.Errorf("failed to unmarshal back marshalled value: %v. YAML:\n%s\nOriginal data:\n%s",
   210  			err, string(out), spew.Sdump(v))
   211  	}
   212  
   213  	return reflect.DeepEqual(curData, newData), nil
   214  }
   215  
   216  // Marshal implements Marshal method of the Verifier interface.
   217  func (v YamlVerifier) Marshal() ([]byte, error) {
   218  	switch d := v.data.(type) {
   219  	case []byte:
   220  		return d, nil
   221  	case string:
   222  		return []byte(d), nil
   223  	default:
   224  		out, err := yaml.Marshal(v.data)
   225  		if err != nil {
   226  			return nil, fmt.Errorf("failed to marshal yaml data: %v. Input:\n%s",
   227  				err, spew.Sdump(v.data))
   228  		}
   229  		return out, nil
   230  	}
   231  }
   232  
   233  // Replacement specifies a replacement for SubstVerifier.
   234  type Replacement struct {
   235  	Old string
   236  	New string
   237  }
   238  
   239  // SubstVerifier wraps another verifier and replaces the specified
   240  // substrings in the data it generates.
   241  type SubstVerifier struct {
   242  	next         Verifier
   243  	replacements []Replacement
   244  }
   245  
   246  var _ Verifier = SubstVerifier{}
   247  
   248  // NewSubstVerifier makes a SubstVerifier that wraps another verifier
   249  // and does the specified replacements.
   250  func NewSubstVerifier(next Verifier, replacements []Replacement) SubstVerifier {
   251  	return SubstVerifier{next, replacements}
   252  }
   253  
   254  // Suffix implements Suffix method of the Verifier interface.
   255  func (v SubstVerifier) Suffix() string {
   256  	return v.next.Suffix()
   257  }
   258  
   259  // Verify implements Verify method of the Verifier interface.
   260  func (v SubstVerifier) Verify(content []byte) (bool, error) {
   261  	return v.next.Verify(content)
   262  }
   263  
   264  // Marshal implements Marshal method of the Verifier interface.
   265  func (v SubstVerifier) Marshal() ([]byte, error) {
   266  	d, err := v.next.Marshal()
   267  	if err != nil {
   268  		return nil, err
   269  	}
   270  	for _, rep := range v.replacements {
   271  		d = bytes.Replace(d, []byte(rep.Old), []byte(rep.New), -1)
   272  	}
   273  	return d, nil
   274  }
   275  
   276  func getVerifier(data interface{}) Verifier {
   277  	switch v := data.(type) {
   278  	case Verifier:
   279  		return v
   280  	case string:
   281  		return textVerifier(v)
   282  	case []byte:
   283  		return textVerifier(string(v))
   284  	default:
   285  		return NewJSONVerifier(v)
   286  	}
   287  }
   288  
   289  var badFilenameCharRx = regexp.MustCompile(`[^\w-]`)
   290  
   291  // GetFilenameForTest converts a Go test name to filename
   292  func GetFilenameForTest(testName string, v interface{}) (string, error) {
   293  	suffix := ".out" + getVerifier(v).Suffix()
   294  	filename := strings.Replace(testName, "/", "__", -1)
   295  	filename = badFilenameCharRx.ReplaceAllString(filename, "_") + suffix
   296  	wd, err := os.Getwd()
   297  	if err != nil {
   298  		return "", fmt.Errorf("can't get current directory: %v", err)
   299  	}
   300  	return filepath.Join(wd, filename), nil
   301  }
   302  
   303  // WriteDataFile serializes the specified value into a data file
   304  func WriteDataFile(filename string, v interface{}) error {
   305  	out, err := getVerifier(v).Marshal()
   306  	if err != nil {
   307  		return fmt.Errorf("error generating the data for %q: %v", filename, err)
   308  	}
   309  	if err := ioutil.WriteFile(filename, out, 0777); err != nil {
   310  		return fmt.Errorf("error writing %q: %v", filename, err)
   311  	}
   312  	return nil
   313  }
   314  
   315  // DataFileDiffers compares the specified value against the stored data file
   316  func DataFileDiffers(filename string, v interface{}) (bool, error) {
   317  	content, err := ioutil.ReadFile(filename)
   318  	if err != nil {
   319  		if os.IsNotExist(err) {
   320  			return true, nil
   321  		}
   322  		return false, fmt.Errorf("error reading %q: %v", filename, err)
   323  	}
   324  
   325  	ok, err := getVerifier(v).Verify(content)
   326  	if err != nil {
   327  		return false, fmt.Errorf("error parsing %q: %v", filename, err)
   328  	}
   329  
   330  	return !ok, nil
   331  }