...

Source file src/github.com/launchdarkly/go-jsonstream/v3/internal/commontest/test_factory.go

Documentation: github.com/launchdarkly/go-jsonstream/v3/internal/commontest

     1  package commontest
     2  
     3  import (
     4  	"fmt"
     5  	"strings"
     6  )
     7  
     8  // This file contains the logic for generating all the valid JSON permutations and error conditions
     9  // that will be tested by ReaderTestSuite and WriterTestSuite.
    10  
    11  type testFactory struct {
    12  	valueTestFactory     ValueTestFactory
    13  	readErrorTestFactory ReadErrorTestFactory
    14  	encodingBehavior     encodingBehavior
    15  }
    16  
    17  // This struct associates a test Action with some JSON data.
    18  type testDef struct {
    19  	// A descriptive name, used in test output.
    20  	name string
    21  
    22  	// For readers, encoding is the JSON input data; for writers, the expected JSON output. It is
    23  	// defined as a list of substrings, with the expectation that there may be any amount of
    24  	// whitespace between each substring.
    25  	encoding []string
    26  
    27  	// The test action for either reading or writing this piece of JSON.
    28  	action Action
    29  }
    30  
    31  type testDefs []testDef
    32  
    33  func (td testDef) then(next testDef) testDef {
    34  	return testDef{
    35  		name:     td.name + ", " + next.name,
    36  		encoding: append(td.encoding, next.encoding...),
    37  		action: func(ctx TestContext) error {
    38  			if err := td.action(ctx); err != nil {
    39  				return err
    40  			}
    41  			return next.action(ctx)
    42  		},
    43  	}
    44  }
    45  
    46  func (tds testDefs) then(next testDef) testDefs {
    47  	ret := make(testDefs, 0, len(tds))
    48  	for _, td := range tds {
    49  		ret = append(ret, td.then(next))
    50  	}
    51  	return ret
    52  }
    53  
    54  func (f testFactory) MakeAllValueTests() testDefs {
    55  	ret := testDefs{}
    56  	eofTest := testDef{name: "EOF", action: f.valueTestFactory.EOF()}
    57  	ret = append(ret, f.makeScalarValueTests(true).then(eofTest)...)
    58  	ret = append(ret, f.makeArrayTests().then(eofTest)...)
    59  	ret = append(ret, f.makeObjectTests().then(eofTest)...)
    60  	return ret
    61  }
    62  
    63  func maybeVariants(vs []ValueVariant) []ValueVariant {
    64  	if len(vs) == 0 {
    65  		return []ValueVariant{""}
    66  	}
    67  	return vs
    68  }
    69  
    70  func (f testFactory) MakeAllReadErrorTests() testDefs {
    71  	ret := testDefs{}
    72  	addErrors := func(tds testDefs) {
    73  		for _, td := range tds {
    74  			for i, enc := range td.encoding {
    75  				if enc == "" { // this means we want to force an unexpected EOF
    76  					td.encoding = td.encoding[0:i]
    77  					break
    78  				}
    79  			}
    80  			ret = append(ret, td)
    81  		}
    82  	}
    83  	addErrors(f.makeScalarValueReadErrorTests())
    84  	return ret
    85  }
    86  
    87  func (f testFactory) makeScalarValueTests(allPermutations bool) testDefs {
    88  	ret := testDefs{}
    89  	values := f.makeScalarValues(allPermutations)
    90  	for _, tv := range values {
    91  		variants := maybeVariants(f.valueTestFactory.Variants(tv.value))
    92  		for _, variant := range variants {
    93  			name := tv.name
    94  			if variant != "" {
    95  				name = string(variant) + " " + name
    96  			}
    97  			td := testDef{
    98  				name:     name,
    99  				encoding: []string{tv.encoding},
   100  				action:   f.valueTestFactory.Value(tv.value, variant),
   101  			}
   102  			ret = append(ret, td)
   103  		}
   104  	}
   105  	return ret
   106  }
   107  
   108  func (f testFactory) makeScalarValueReadErrorTests() testDefs {
   109  	ret := testDefs{}
   110  	values := f.makeScalarValues(false)
   111  	oneVariant := []ValueVariant{""}
   112  	for _, testValue := range values {
   113  		tv := testValue
   114  		variants := f.valueTestFactory.Variants(tv.value)
   115  		if variants == nil {
   116  			variants = oneVariant
   117  		}
   118  		for _, variant := range variants {
   119  			v := variant
   120  			name := tv.name
   121  			if v != "" {
   122  				name = string(v) + " " + name
   123  			}
   124  			testAction := f.valueTestFactory.Value(tv.value, variant)
   125  
   126  			// error: want a value, got a value of some other type
   127  			if v != UntypedVariant && v != SkipValueVariant {
   128  				for _, wrongValue := range f.makeScalarValues(false) {
   129  					wv := wrongValue
   130  					if wv.value.Kind == tv.value.Kind {
   131  						continue
   132  					}
   133  					ret = append(ret, testDef{
   134  						name:     fmt.Sprintf("%s (but got %s)", name, wv.name),
   135  						encoding: []string{wv.encoding},
   136  						action: func(c TestContext) error {
   137  							return f.readErrorTestFactory.ExpectWrongTypeError(testAction(c), tv.value.Kind, v, wv.value.Kind)
   138  						},
   139  					})
   140  				}
   141  				if tv.value.Kind != NullValue {
   142  					ret = append(ret, testDef{
   143  						name:     fmt.Sprintf("%s (but got array)", name),
   144  						encoding: []string{"[]"},
   145  						action: func(c TestContext) error {
   146  							return f.readErrorTestFactory.ExpectWrongTypeError(testAction(c), tv.value.Kind, v, ArrayValue)
   147  						},
   148  					})
   149  					ret = append(ret, testDef{
   150  						name:     fmt.Sprintf("%s (but got object)", name),
   151  						encoding: []string{"{}"},
   152  						action: func(c TestContext) error {
   153  							return f.readErrorTestFactory.ExpectWrongTypeError(testAction(c), tv.value.Kind, v, ObjectValue)
   154  						},
   155  					})
   156  				}
   157  			}
   158  
   159  			// error: want a value, got some invalid JSON
   160  			for _, badThing := range []struct {
   161  				name     string
   162  				encoding string
   163  			}{
   164  				{"invalid identifier", "bad"},
   165  				{"unknown delimiter", "+"},
   166  				{"unexpected end array", "]"},
   167  				{"unexpected object", "}"},
   168  			} {
   169  				ret = append(ret, testDef{
   170  					name:     fmt.Sprintf("%s (but got %s)", name, badThing.name),
   171  					encoding: []string{badThing.encoding},
   172  					action: func(c TestContext) error {
   173  						return f.readErrorTestFactory.ExpectSyntaxError(testAction(c))
   174  					},
   175  				})
   176  			}
   177  			ret = append(ret, testDef{
   178  				name:     fmt.Sprintf("%s (but got unexpected EOF)", name),
   179  				encoding: []string{""},
   180  				action: func(c TestContext) error {
   181  					return f.readErrorTestFactory.ExpectEOFError(testAction(c))
   182  				},
   183  			})
   184  		}
   185  	}
   186  	return ret
   187  }
   188  
   189  func (f testFactory) makeScalarValues(allPermutations bool) []testValue {
   190  	var values []testValue
   191  	values = append(values, testValue{
   192  		name:     "null",
   193  		encoding: "null",
   194  		value:    AnyValue{Kind: NullValue},
   195  	})
   196  	values = append(values, makeBoolTestValues()...)
   197  	values = append(values, makeNumberTestValues(f.encodingBehavior)...)
   198  	values = append(values, makeStringTestValues(f.encodingBehavior, allPermutations)...)
   199  	return values
   200  }
   201  
   202  func (f testFactory) makeArrayTests() testDefs {
   203  	ret := testDefs{}
   204  	for elementCount := 0; elementCount <= 2; elementCount++ {
   205  		for _, contents := range f.makeValueListsOfLength(elementCount) {
   206  			var names []string
   207  			var encoding = []string{"["}
   208  			var actions []Action
   209  			for i, td := range contents {
   210  				names = append(names, td.name)
   211  				if i > 0 {
   212  					encoding = append(encoding, ",")
   213  				}
   214  				encoding = append(encoding, td.encoding...)
   215  				actions = append(actions, td.action)
   216  			}
   217  			encoding = append(encoding, "]")
   218  			value := AnyValue{Kind: ArrayValue, Array: actions}
   219  			for _, variant := range maybeVariants(f.valueTestFactory.Variants(value)) {
   220  				arrayTest := testDef{
   221  					name:     "array(" + strings.Join(names, ", ") + ")",
   222  					encoding: encoding,
   223  					action:   f.valueTestFactory.Value(value, variant),
   224  				}
   225  				ret = append(ret, arrayTest)
   226  			}
   227  		}
   228  	}
   229  	return ret
   230  }
   231  
   232  func (f testFactory) makeObjectTests() testDefs {
   233  	ret := testDefs{}
   234  	for propertyCount := 0; propertyCount <= 2; propertyCount++ {
   235  		for _, contents := range f.makeValueListsOfLength(propertyCount) {
   236  			var names []string
   237  			var encoding = []string{"{"}
   238  			var propActions []PropertyAction
   239  			for i, td := range contents {
   240  				propName := fmt.Sprintf("prop%d", i)
   241  				names = append(names, fmt.Sprintf("%s: %s", propName, td.name))
   242  				if i > 0 {
   243  					encoding = append(encoding, ",")
   244  				}
   245  				encoding = append(encoding, fmt.Sprintf(`"%s"`, propName))
   246  				encoding = append(encoding, ":")
   247  				encoding = append(encoding, td.encoding...)
   248  				propActions = append(propActions, PropertyAction{Name: propName, Action: td.action})
   249  			}
   250  			encoding = append(encoding, "}")
   251  			value := AnyValue{Kind: ObjectValue, Object: propActions}
   252  			for _, variant := range maybeVariants(f.valueTestFactory.Variants(value)) {
   253  				objectTest := testDef{
   254  					name:     "object(" + strings.Join(names, ", ") + ")",
   255  					encoding: encoding,
   256  					action:   f.valueTestFactory.Value(value, variant),
   257  				}
   258  				ret = append(ret, objectTest)
   259  			}
   260  		}
   261  	}
   262  	return ret
   263  }
   264  
   265  func (f testFactory) makeValueListsOfLength(count int) []testDefs {
   266  	if count == 0 {
   267  		return []testDefs{testDefs{}}
   268  	}
   269  	previousLists := f.makeValueListsOfLength(count - 1)
   270  	ret := []testDefs{}
   271  	for _, previous := range previousLists {
   272  		for _, elementTest := range f.makeScalarValueTests(false) {
   273  			ret = append(ret, append(previous, elementTest))
   274  		}
   275  	}
   276  	return ret
   277  }
   278  

View as plain text