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 }