github.com/urso/go-structform@v0.0.2/sftest/sftest.go (about)

     1  package sftest
     2  
     3  import (
     4  	"encoding/json"
     5  	"errors"
     6  	"fmt"
     7  	"testing"
     8  
     9  	"github.com/stretchr/testify/assert"
    10  	structform "github.com/urso/go-structform"
    11  )
    12  
    13  type Recording []Record
    14  
    15  type Record interface {
    16  	Replay(v structform.ExtVisitor) error
    17  }
    18  
    19  type NilRec struct{}
    20  type BoolRec struct{ Value bool }
    21  type StringRec struct{ Value string }
    22  type IntRec struct{ Value int }
    23  type Int8Rec struct{ Value int8 }
    24  type Int16Rec struct{ Value int16 }
    25  type Int32Rec struct{ Value int32 }
    26  type Int64Rec struct{ Value int64 }
    27  type UintRec struct{ Value uint }
    28  type ByteRec struct{ Value byte }
    29  type Uint8Rec struct{ Value uint8 }
    30  type Uint16Rec struct{ Value uint16 }
    31  type Uint32Rec struct{ Value uint32 }
    32  type Uint64Rec struct{ Value uint64 }
    33  type Float32Rec struct{ Value float32 }
    34  type Float64Rec struct{ Value float64 }
    35  
    36  // extended (yet) non-recordible records
    37  type Int8ArrRec struct{ Value []int8 }
    38  type StringArrRec struct{ Value []string }
    39  type StringObjRec struct{ Value map[string]string }
    40  type UintObjRec struct{ Value map[string]uint }
    41  
    42  type ObjectStartRec struct {
    43  	Len int
    44  	T   structform.BaseType
    45  }
    46  type ObjectFinishRec struct{}
    47  type ObjectKeyRec struct{ Value string }
    48  
    49  type ArrayStartRec struct {
    50  	Len int
    51  	T   structform.BaseType
    52  }
    53  type ArrayFinishRec struct{}
    54  
    55  func (NilRec) Replay(vs structform.ExtVisitor) error            { return vs.OnNil() }
    56  func (r BoolRec) Replay(vs structform.ExtVisitor) error         { return vs.OnBool(r.Value) }
    57  func (r StringRec) Replay(vs structform.ExtVisitor) error       { return vs.OnString(r.Value) }
    58  func (r IntRec) Replay(vs structform.ExtVisitor) error          { return vs.OnInt(r.Value) }
    59  func (r Int8Rec) Replay(vs structform.ExtVisitor) error         { return vs.OnInt8(r.Value) }
    60  func (r Int16Rec) Replay(vs structform.ExtVisitor) error        { return vs.OnInt16(r.Value) }
    61  func (r Int32Rec) Replay(vs structform.ExtVisitor) error        { return vs.OnInt32(r.Value) }
    62  func (r Int64Rec) Replay(vs structform.ExtVisitor) error        { return vs.OnInt64(r.Value) }
    63  func (r UintRec) Replay(vs structform.ExtVisitor) error         { return vs.OnUint(r.Value) }
    64  func (r ByteRec) Replay(vs structform.ExtVisitor) error         { return vs.OnByte(r.Value) }
    65  func (r Uint8Rec) Replay(vs structform.ExtVisitor) error        { return vs.OnUint8(r.Value) }
    66  func (r Uint16Rec) Replay(vs structform.ExtVisitor) error       { return vs.OnUint16(r.Value) }
    67  func (r Uint32Rec) Replay(vs structform.ExtVisitor) error       { return vs.OnUint32(r.Value) }
    68  func (r Uint64Rec) Replay(vs structform.ExtVisitor) error       { return vs.OnUint64(r.Value) }
    69  func (r Float32Rec) Replay(vs structform.ExtVisitor) error      { return vs.OnFloat32(r.Value) }
    70  func (r Float64Rec) Replay(vs structform.ExtVisitor) error      { return vs.OnFloat64(r.Value) }
    71  func (r ObjectStartRec) Replay(vs structform.ExtVisitor) error  { return vs.OnObjectStart(r.Len, r.T) }
    72  func (r ObjectFinishRec) Replay(vs structform.ExtVisitor) error { return vs.OnObjectFinished() }
    73  func (r ObjectKeyRec) Replay(vs structform.ExtVisitor) error    { return vs.OnKey(r.Value) }
    74  func (r ArrayStartRec) Replay(vs structform.ExtVisitor) error   { return vs.OnArrayStart(r.Len, r.T) }
    75  func (r ArrayFinishRec) Replay(vs structform.ExtVisitor) error  { return vs.OnArrayFinished() }
    76  
    77  func (r Int8ArrRec) Replay(vs structform.ExtVisitor) error   { return vs.OnInt8Array(r.Value) }
    78  func (r StringArrRec) Replay(vs structform.ExtVisitor) error { return vs.OnStringArray(r.Value) }
    79  func (r UintObjRec) Replay(vs structform.ExtVisitor) error   { return vs.OnUintObject(r.Value) }
    80  func (r StringObjRec) Replay(vs structform.ExtVisitor) error { return vs.OnStringObject(r.Value) }
    81  
    82  func (rec *Recording) Replay(vs structform.Visitor) error {
    83  	evs := structform.EnsureExtVisitor(vs)
    84  	for _, r := range *rec {
    85  		if err := r.Replay(evs); err != nil {
    86  			return err
    87  		}
    88  	}
    89  	return nil
    90  }
    91  
    92  func (r *Recording) add(v Record) error {
    93  	*r = append(*r, v)
    94  	return nil
    95  }
    96  
    97  func (r *Recording) OnNil() error              { return r.add(NilRec{}) }
    98  func (r *Recording) OnBool(b bool) error       { return r.add(BoolRec{b}) }
    99  func (r *Recording) OnString(s string) error   { return r.add(StringRec{s}) }
   100  func (r *Recording) OnInt8(i int8) error       { return r.add(Int8Rec{i}) }
   101  func (r *Recording) OnInt16(i int16) error     { return r.add(Int16Rec{i}) }
   102  func (r *Recording) OnInt32(i int32) error     { return r.add(Int32Rec{i}) }
   103  func (r *Recording) OnInt64(i int64) error     { return r.add(Int64Rec{i}) }
   104  func (r *Recording) OnInt(i int) error         { return r.add(IntRec{i}) }
   105  func (r *Recording) OnUint8(i uint8) error     { return r.add(Uint8Rec{i}) }
   106  func (r *Recording) OnByte(i byte) error       { return r.add(ByteRec{i}) }
   107  func (r *Recording) OnUint16(i uint16) error   { return r.add(Uint16Rec{i}) }
   108  func (r *Recording) OnUint32(i uint32) error   { return r.add(Uint32Rec{i}) }
   109  func (r *Recording) OnUint64(i uint64) error   { return r.add(Uint64Rec{i}) }
   110  func (r *Recording) OnUint(i uint) error       { return r.add(UintRec{i}) }
   111  func (r *Recording) OnFloat32(i float32) error { return r.add(Float32Rec{i}) }
   112  func (r *Recording) OnFloat64(i float64) error { return r.add(Float64Rec{i}) }
   113  
   114  func (r *Recording) OnArrayStart(len int, baseType structform.BaseType) error {
   115  	return r.add(ArrayStartRec{len, baseType})
   116  }
   117  func (r *Recording) OnArrayFinished() error {
   118  	return r.add(ArrayFinishRec{})
   119  }
   120  
   121  func (r *Recording) OnObjectStart(len int, baseType structform.BaseType) error {
   122  	return r.add(ObjectStartRec{len, baseType})
   123  }
   124  func (r *Recording) OnObjectFinished() error {
   125  	return r.add(ObjectFinishRec{})
   126  }
   127  func (r *Recording) OnKey(s string) error {
   128  	return r.add(ObjectKeyRec{s})
   129  }
   130  
   131  func (r *Recording) expand() Recording {
   132  	var to Recording
   133  	r.Replay(&to)
   134  	return to
   135  }
   136  
   137  func (r Recording) Assert(t *testing.T, expected Recording) {
   138  	exp, err := expected.ToJSON()
   139  	if err != nil {
   140  		t.Error("Assert (expected): ", err)
   141  		t.Logf("  recording: %#v", exp)
   142  		return
   143  	}
   144  
   145  	act, err := r.ToJSON()
   146  	if err != nil {
   147  		t.Error("Assert (actual): ", err)
   148  		t.Logf("  recording: %#v", r)
   149  		return
   150  	}
   151  
   152  	assert.Equal(t, exp, act)
   153  }
   154  
   155  func (r Recording) ToJSON() (string, error) {
   156  	v, _, err := buildValue(r.expand())
   157  	if err != nil {
   158  		return "", err
   159  	}
   160  
   161  	b, err := json.MarshalIndent(v, "", "  ")
   162  	if err != nil {
   163  		return "", err
   164  	}
   165  
   166  	return string(b), nil
   167  }
   168  
   169  type builder struct {
   170  	stack []interface{}
   171  	value interface{}
   172  }
   173  
   174  func buildValue(rec Recording) (interface{}, Recording, error) {
   175  	if len(rec) == 0 {
   176  		return nil, nil, errors.New("empty recording")
   177  	}
   178  
   179  	switch v := rec[0].(type) {
   180  	case NilRec:
   181  		return nil, rec[1:], nil
   182  	case BoolRec:
   183  		return v.Value, rec[1:], nil
   184  	case StringRec:
   185  		return v.Value, rec[1:], nil
   186  	case IntRec:
   187  		return v.Value, rec[1:], nil
   188  	case Int8Rec:
   189  		return v.Value, rec[1:], nil
   190  	case Int16Rec:
   191  		return v.Value, rec[1:], nil
   192  	case Int32Rec:
   193  		return v.Value, rec[1:], nil
   194  	case Int64Rec:
   195  		return v.Value, rec[1:], nil
   196  	case UintRec:
   197  		return v.Value, rec[1:], nil
   198  	case ByteRec:
   199  		return v.Value, rec[1:], nil
   200  	case Uint8Rec:
   201  		return v.Value, rec[1:], nil
   202  	case Uint16Rec:
   203  		return v.Value, rec[1:], nil
   204  	case Uint32Rec:
   205  		return v.Value, rec[1:], nil
   206  	case Uint64Rec:
   207  		return v.Value, rec[1:], nil
   208  	case Float32Rec:
   209  		return v.Value, rec[1:], nil
   210  	case Float64Rec:
   211  		return v.Value, rec[1:], nil
   212  	case ArrayStartRec:
   213  		return buildArray(rec[1:])
   214  	case ObjectStartRec:
   215  		return buildObject(rec[1:])
   216  
   217  	default:
   218  		return nil, nil, fmt.Errorf("Invalid record entry: %v", v)
   219  	}
   220  }
   221  
   222  func buildArray(rec Recording) (interface{}, Recording, error) {
   223  	a := []interface{}{}
   224  
   225  	for len(rec) > 0 {
   226  		var (
   227  			v   interface{}
   228  			err error
   229  		)
   230  
   231  		if _, end := rec[0].(ArrayFinishRec); end {
   232  			return a, rec[1:], nil
   233  		}
   234  
   235  		v, rec, err = buildValue(rec)
   236  		if err != nil {
   237  			return nil, nil, err
   238  		}
   239  
   240  		a = append(a, v)
   241  	}
   242  
   243  	return nil, nil, errors.New("missing array finish record")
   244  }
   245  
   246  func buildObject(rec Recording) (interface{}, Recording, error) {
   247  	obj := map[string]interface{}{}
   248  
   249  	for len(rec) > 0 {
   250  		var (
   251  			key string
   252  			v   interface{}
   253  			err error
   254  		)
   255  
   256  		switch v := rec[0].(type) {
   257  		case ObjectFinishRec:
   258  			return obj, rec[1:], nil
   259  		case ObjectKeyRec:
   260  			key = v.Value
   261  		}
   262  
   263  		v, rec, err = buildValue(rec[1:])
   264  		if err != nil {
   265  			return nil, nil, err
   266  		}
   267  
   268  		obj[key] = v
   269  	}
   270  
   271  	return nil, nil, errors.New("missing object finish record")
   272  }
   273  
   274  func TestEncodeParseConsistent(
   275  	t *testing.T,
   276  	samples []Recording,
   277  	constr func() (structform.Visitor, func(structform.Visitor) error),
   278  ) {
   279  	for i, sample := range samples {
   280  		expected, err := sample.ToJSON()
   281  		if err != nil {
   282  			panic(err)
   283  		}
   284  
   285  		t.Logf("test %v: %#v => %v", i, sample, expected)
   286  
   287  		enc, dec := constr()
   288  
   289  		err = sample.Replay(enc)
   290  		if err != nil {
   291  			t.Errorf("Failed to encode %#v with %v", sample, err)
   292  			return
   293  		}
   294  
   295  		var target Recording
   296  		err = dec(&target)
   297  		if err != nil {
   298  			t.Errorf("Failed to decode %#v with %v", target, err)
   299  			t.Logf("  recording: %#v", target)
   300  		}
   301  
   302  		target.Assert(t, sample)
   303  	}
   304  }
   305  
   306  func concatSamples(recs ...[]Recording) []Recording {
   307  	var out []Recording
   308  	for _, r := range recs {
   309  		out = append(out, r...)
   310  	}
   311  	return out
   312  }