...

Source file src/github.com/pelletier/go-toml/v2/internal/testsuite/json.go

Documentation: github.com/pelletier/go-toml/v2/internal/testsuite

     1  package testsuite
     2  
     3  import (
     4  	"fmt"
     5  	"strconv"
     6  	"strings"
     7  	"testing"
     8  	"time"
     9  )
    10  
    11  func CmpJSON(t *testing.T, key string, want, have interface{}) {
    12  	switch w := want.(type) {
    13  	case map[string]interface{}:
    14  		cmpJSONMaps(t, key, w, have)
    15  	case []interface{}:
    16  		cmpJSONArrays(t, key, w, have)
    17  	default:
    18  		t.Errorf(
    19  			"Key '%s' in expected output should be a map or a list of maps, but it's a %T",
    20  			key, want)
    21  	}
    22  }
    23  
    24  func cmpJSONMaps(t *testing.T, key string, want map[string]interface{}, have interface{}) {
    25  	haveMap, ok := have.(map[string]interface{})
    26  	if !ok {
    27  		mismatch(t, key, "table", want, haveMap)
    28  		return
    29  	}
    30  
    31  	// Check to make sure both or neither are values.
    32  	if isValue(want) && !isValue(haveMap) {
    33  		t.Fatalf("Key '%s' is supposed to be a value, but the parser reports it as a table", key)
    34  	}
    35  	if !isValue(want) && isValue(haveMap) {
    36  		t.Fatalf("Key '%s' is supposed to be a table, but the parser reports it as a value", key)
    37  	}
    38  	if isValue(want) && isValue(haveMap) {
    39  		cmpJSONValues(t, key, want, haveMap)
    40  		return
    41  	}
    42  
    43  	// Check that the keys of each map are equivalent.
    44  	for k := range want {
    45  		if _, ok := haveMap[k]; !ok {
    46  			bunk := kjoin(key, k)
    47  			t.Fatalf("Could not find key '%s' in parser output.", bunk)
    48  		}
    49  	}
    50  	for k := range haveMap {
    51  		if _, ok := want[k]; !ok {
    52  			bunk := kjoin(key, k)
    53  			t.Fatalf("Could not find key '%s' in expected output.", bunk)
    54  		}
    55  	}
    56  
    57  	// Okay, now make sure that each value is equivalent.
    58  	for k := range want {
    59  		CmpJSON(t, kjoin(key, k), want[k], haveMap[k])
    60  	}
    61  }
    62  
    63  func cmpJSONArrays(t *testing.T, key string, want, have interface{}) {
    64  	wantSlice, ok := want.([]interface{})
    65  	if !ok {
    66  		panic(fmt.Sprintf("'value' should be a JSON array when 'type=array', but it is a %T", want))
    67  	}
    68  
    69  	haveSlice, ok := have.([]interface{})
    70  	if !ok {
    71  		t.Fatalf("Malformed output from your encoder: 'value' is not a JSON array: %T", have)
    72  	}
    73  
    74  	if len(wantSlice) != len(haveSlice) {
    75  		t.Fatalf("Array lengths differ for key '%s':\n"+
    76  			"  Expected:     %d\n"+
    77  			"  Your encoder: %d",
    78  			key, len(wantSlice), len(haveSlice))
    79  	}
    80  	for i := 0; i < len(wantSlice); i++ {
    81  		CmpJSON(t, key, wantSlice[i], haveSlice[i])
    82  	}
    83  }
    84  
    85  func cmpJSONValues(t *testing.T, key string, want, have map[string]interface{}) {
    86  	wantType, ok := want["type"].(string)
    87  	if !ok {
    88  		panic(fmt.Sprintf("'type' should be a string, but it is a %T", want["type"]))
    89  	}
    90  
    91  	haveType, ok := have["type"].(string)
    92  	if !ok {
    93  		t.Fatalf("Malformed output from your encoder: 'type' is not a string: %T", have["type"])
    94  	}
    95  
    96  	if wantType != haveType {
    97  		valMismatch(t, key, wantType, haveType, want, have)
    98  	}
    99  
   100  	// If this is an array, then we've got to do some work to check equality.
   101  	if wantType == "array" {
   102  		cmpJSONArrays(t, key, want, have)
   103  		return
   104  	}
   105  
   106  	// Atomic values are always strings
   107  	wantVal, ok := want["value"].(string)
   108  	if !ok {
   109  		panic(fmt.Sprintf("'value' %v should be a string, but it is a %[1]T", want["value"]))
   110  	}
   111  
   112  	haveVal, ok := have["value"].(string)
   113  	if !ok {
   114  		panic(fmt.Sprintf("Malformed output from your encoder: %T is not a string", have["value"]))
   115  	}
   116  
   117  	// Excepting floats and datetimes, other values can be compared as strings.
   118  	switch wantType {
   119  	case "float":
   120  		cmpFloats(t, key, wantVal, haveVal)
   121  	case "datetime", "datetime-local", "date-local", "time-local":
   122  		cmpAsDatetimes(t, key, wantType, wantVal, haveVal)
   123  	default:
   124  		cmpAsStrings(t, key, wantVal, haveVal)
   125  	}
   126  }
   127  
   128  func cmpAsStrings(t *testing.T, key string, want, have string) {
   129  	if want != have {
   130  		t.Fatalf("Values for key '%s' don't match:\n"+
   131  			"  Expected:     %s\n"+
   132  			"  Your encoder: %s",
   133  			key, want, have)
   134  	}
   135  }
   136  
   137  func cmpFloats(t *testing.T, key string, want, have string) {
   138  	// Special case for NaN, since NaN != NaN.
   139  	if strings.HasSuffix(want, "nan") || strings.HasSuffix(have, "nan") {
   140  		if want != have {
   141  			t.Fatalf("Values for key '%s' don't match:\n"+
   142  				"  Expected:     %v\n"+
   143  				"  Your encoder: %v",
   144  				key, want, have)
   145  		}
   146  		return
   147  	}
   148  
   149  	wantF, err := strconv.ParseFloat(want, 64)
   150  	if err != nil {
   151  		panic(fmt.Sprintf("Could not read '%s' as a float value for key '%s'", want, key))
   152  	}
   153  
   154  	haveF, err := strconv.ParseFloat(have, 64)
   155  	if err != nil {
   156  		panic(fmt.Sprintf("Malformed output from your encoder: key '%s' is not a float: '%s'", key, have))
   157  	}
   158  
   159  	if wantF != haveF {
   160  		t.Fatalf("Values for key '%s' don't match:\n"+
   161  			"  Expected:     %v\n"+
   162  			"  Your encoder: %v",
   163  			key, wantF, haveF)
   164  	}
   165  }
   166  
   167  var datetimeRepl = strings.NewReplacer(
   168  	" ", "T",
   169  	"t", "T",
   170  	"z", "Z")
   171  
   172  var layouts = map[string]string{
   173  	"datetime":       time.RFC3339Nano,
   174  	"datetime-local": "2006-01-02T15:04:05.999999999",
   175  	"date-local":     "2006-01-02",
   176  	"time-local":     "15:04:05",
   177  }
   178  
   179  func cmpAsDatetimes(t *testing.T, key string, kind, want, have string) {
   180  	layout, ok := layouts[kind]
   181  	if !ok {
   182  		panic("should never happen")
   183  	}
   184  
   185  	wantT, err := time.Parse(layout, datetimeRepl.Replace(want))
   186  	if err != nil {
   187  		panic(fmt.Sprintf("Could not read '%s' as a datetime value for key '%s'", want, key))
   188  	}
   189  
   190  	haveT, err := time.Parse(layout, datetimeRepl.Replace(want))
   191  	if err != nil {
   192  		t.Fatalf("Malformed output from your encoder: key '%s' is not a datetime: '%s'", key, have)
   193  		return
   194  	}
   195  	if !wantT.Equal(haveT) {
   196  		t.Fatalf("Values for key '%s' don't match:\n"+
   197  			"  Expected:     %v\n"+
   198  			"  Your encoder: %v",
   199  			key, wantT, haveT)
   200  	}
   201  }
   202  
   203  func cmpAsDatetimesLocal(t *testing.T, key string, want, have string) {
   204  	if datetimeRepl.Replace(want) != datetimeRepl.Replace(have) {
   205  		t.Fatalf("Values for key '%s' don't match:\n"+
   206  			"  Expected:     %v\n"+
   207  			"  Your encoder: %v",
   208  			key, want, have)
   209  	}
   210  }
   211  
   212  func kjoin(old, key string) string {
   213  	if len(old) == 0 {
   214  		return key
   215  	}
   216  	return old + "." + key
   217  }
   218  
   219  func isValue(m map[string]interface{}) bool {
   220  	if len(m) != 2 {
   221  		return false
   222  	}
   223  	if _, ok := m["type"]; !ok {
   224  		return false
   225  	}
   226  	if _, ok := m["value"]; !ok {
   227  		return false
   228  	}
   229  	return true
   230  }
   231  
   232  func mismatch(t *testing.T, key string, wantType string, want, have interface{}) {
   233  	t.Fatalf("Key '%s' is not an %s but %[4]T:\n"+
   234  		"  Expected:     %#[3]v\n"+
   235  		"  Your encoder: %#[4]v",
   236  		key, wantType, want, have)
   237  }
   238  
   239  func valMismatch(t *testing.T, key string, wantType, haveType string, want, have interface{}) {
   240  	t.Fatalf("Key '%s' is not an %s but %s:\n"+
   241  		"  Expected:     %#[3]v\n"+
   242  		"  Your encoder: %#[4]v",
   243  		key, wantType, want, have)
   244  }
   245  

View as plain text