github.com/mavryk-network/mvgo@v1.19.9/micheline/value_test.go (about) 1 // Copyright (c) 2021 Blockwatch Data Inc. 2 // Author: alex@blockwatch.cc 3 // 4 5 // Run with tracing enabled 6 // go test -tags trace ./micheline/ 7 8 package micheline 9 10 import ( 11 "encoding/hex" 12 "encoding/json" 13 "flag" 14 "fmt" 15 "io" 16 "io/fs" 17 "os" 18 "path/filepath" 19 "reflect" 20 "strings" 21 "testing" 22 23 "github.com/pmezard/go-difflib/difflib" 24 ) 25 26 const testDataRootPathPrefix = "testdata" 27 28 var ( 29 testfiles = make(map[string][]string) 30 testmask string 31 ) 32 33 type testcase struct { 34 Name string `json:"name"` 35 NoUnpack bool `json:"no_unpack"` 36 Type json.RawMessage `json:"type"` 37 Value json.RawMessage `json:"value"` 38 Key json.RawMessage `json:"key"` 39 TypeHex string `json:"type_hex"` 40 ValueHex string `json:"value_hex"` 41 KeyHex string `json:"key_hex"` 42 WantValue json.RawMessage `json:"want_value"` 43 WantKey json.RawMessage `json:"want_key"` 44 } 45 46 func TestMain(m *testing.M) { 47 // call flag.Parse() here if TestMain uses flags 48 flag.StringVar(&testmask, "only", "", "limit test to contract or op") 49 flag.Parse() 50 os.Exit(m.Run()) 51 } 52 53 func scanTestFiles(t *testing.T, category string) { 54 if len(testfiles[category]) > 0 { 55 return 56 } 57 testfiles[category] = make([]string, 0) 58 // find all test data directories 59 testPaths := make([]string, 0) 60 _ = filepath.WalkDir(".", func(path string, d fs.DirEntry, err error) error { 61 if err != nil { 62 return err 63 } 64 if !d.IsDir() || path == "." { 65 return nil 66 } 67 if strings.HasPrefix(filepath.Base(path), testDataRootPathPrefix) { 68 testPaths = append(testPaths, path) 69 } 70 return fs.SkipDir 71 }) 72 // load tests from subdirs 73 for _, testPath := range testPaths { 74 err := filepath.WalkDir( 75 filepath.Join(testPath, category), 76 func(path string, d fs.DirEntry, err error) error { 77 if err != nil { 78 return err 79 } 80 if d.IsDir() || !strings.HasSuffix(path, "json") { 81 return nil 82 } 83 if testmask != "" && !strings.Contains(path, testmask) { 84 return nil 85 } 86 testfiles[category] = append(testfiles[category], path) 87 return nil 88 }) 89 if err != nil { 90 t.Logf("WARN: loading testfiles from %s: %v", testPath, err) 91 } 92 } 93 } 94 95 func jsonDiff(t *testing.T, a, b []byte) bool { 96 var ja, jb interface{} 97 if err := json.Unmarshal(a, &ja); err != nil { 98 t.Error(err) 99 } 100 if err := json.Unmarshal(b, &jb); err != nil { 101 t.Error(err) 102 } 103 if reflect.DeepEqual(ja, jb) { 104 return true 105 } 106 107 // log line-wise differences 108 textA, _ := json.MarshalIndent(ja, "", " ") 109 textB, _ := json.MarshalIndent(jb, "", " ") 110 diff := difflib.UnifiedDiff{ 111 A: difflib.SplitLines(string(textA)), 112 B: difflib.SplitLines(string(textB)), 113 FromFile: "GOT", 114 ToFile: "WANT", 115 Context: 3, 116 } 117 text, _ := difflib.GetUnifiedDiffString(diff) 118 t.Log("DIFF:\n" + text) 119 return false 120 } 121 122 func loadNextTestFile(category string, offset int, val interface{}) (int, error) { 123 files, ok := testfiles[category] 124 if !ok { 125 return 0, fmt.Errorf("invalid category %s", category) 126 } 127 if len(files) <= offset { 128 return offset, io.EOF 129 } 130 buf, err := os.ReadFile(files[offset]) 131 if err != nil { 132 return offset + 1, err 133 } 134 err = json.Unmarshal(buf, val) 135 return offset + 1, err 136 } 137 138 func checkTypeEncoding(t *testing.T, test testcase) Type { 139 // decode type (hex & json and compare trees) 140 buf, err := hex.DecodeString(test.TypeHex) 141 if err != nil { 142 t.Errorf("invalid binary type: %v", err) 143 t.FailNow() 144 } 145 typ1 := Type{} 146 if err := typ1.UnmarshalBinary(buf); err != nil { 147 t.Errorf("invalid binary type: %v", err) 148 t.FailNow() 149 } 150 typ2 := Type{} 151 if err := typ2.UnmarshalJSON(test.Type); err != nil { 152 t.Errorf("invalid json type: %v", err) 153 t.FailNow() 154 } 155 // compare prim trees 156 if !typ1.IsEqualWithAnno(typ2) { 157 b1, _ := typ1.MarshalBinary() 158 b2, _ := typ2.MarshalBinary() 159 t.Errorf("bigmap type decoding mismatch:\n want=%s %x\n have=%s %x", 160 typ1.Dump(), b1, typ2.Dump(), b2) 161 } 162 return typ1 163 } 164 165 func checkValueEncoding(t *testing.T, test testcase) Prim { 166 // decode value (hex & json and compare trees) 167 buf, err := hex.DecodeString(test.ValueHex) 168 if err != nil { 169 t.Errorf("invalid binary value: %v", err) 170 t.FailNow() 171 } 172 val1 := Prim{} 173 if err := val1.UnmarshalBinary(buf); err != nil { 174 t.Errorf("invalid binary value: %v", err) 175 t.FailNow() 176 } 177 val2 := Prim{} 178 if err := val2.UnmarshalJSON(test.Value); err != nil { 179 t.Errorf("invalid json value: %v", err) 180 t.FailNow() 181 } 182 if !val1.IsEqualWithAnno(val2) { 183 b1, _ := val1.MarshalBinary() 184 b2, _ := val2.MarshalBinary() 185 t.Errorf("json/hex value mismatch:\n A=%s %x\n B=%s %x", 186 val1.Dump(), b1, val2.Dump(), b2) 187 t.FailNow() 188 } 189 return val1 190 } 191 192 func checkKeyEncoding(t *testing.T, test testcase) Prim { 193 // decode key (hex & json and compare trees) 194 buf, err := hex.DecodeString(test.KeyHex) 195 if err != nil { 196 t.Errorf("invalid binary key: %v", err) 197 t.FailNow() 198 } 199 key1 := Prim{} 200 if err := key1.UnmarshalBinary(buf); err != nil { 201 t.Errorf("invalid binary key: %v", err) 202 t.FailNow() 203 } 204 key2 := Prim{} 205 if err := key2.UnmarshalJSON(test.Key); err != nil { 206 t.Errorf("invalid json key: %v", err) 207 t.FailNow() 208 } 209 // compare prim trees 210 if !key1.IsEqualWithAnno(key2) { 211 t.Errorf("json/hex key mismatch:\n A=%s\n B=%s\n a=%#v\n b=%#v", 212 key1.Dump(), key2.Dump(), key1, key2) 213 t.FailNow() 214 } 215 return key1 216 } 217 218 func TestBigmapValues(t *testing.T) { 219 var ( 220 next int 221 err error 222 ) 223 scanTestFiles(t, "bigmap") 224 trace = t.Logf 225 // dbg = t.Logf 226 for { 227 var tests []testcase 228 next, err = loadNextTestFile("bigmap", next, &tests) 229 if err != nil { 230 if err == io.EOF { 231 break 232 } 233 t.Error(err) 234 if len(tests) == 0 { 235 break 236 } 237 continue 238 } 239 for _, test := range tests { 240 t.Run(test.Name, func(t *testing.T) { 241 typ1 := checkTypeEncoding(t, test) 242 key1 := checkKeyEncoding(t, test) 243 val1 := checkValueEncoding(t, test) 244 245 // test bigmap key 246 k, err := NewKey( 247 typ1.Left(), // from binary (use key type) 248 key1, // from binary 249 ) 250 if err != nil { 251 t.Logf("typ: %s", typ1.Left().Dump()) 252 t.Logf("key: %s", key1.Dump()) 253 t.Errorf("key render error: %v", err) 254 } 255 // try unpack 256 if k.IsPacked() && !test.NoUnpack { 257 up, err := k.Unpack() 258 if err != nil { 259 t.Errorf("key unpack error: %v", err) 260 } 261 k = up 262 } 263 buf, err := k.MarshalJSON() 264 if err != nil { 265 t.Errorf("value render error: %v", err) 266 } 267 if !jsonDiff(t, buf, test.WantKey) { 268 t.Error("key render mismatch!") 269 t.FailNow() 270 } 271 272 // test bigmap value 273 v := Value{ 274 Type: typ1.Right(), // from binary (use value type) 275 Value: val1, // from binary 276 Render: RENDER_TYPE_FAIL, 277 } 278 if v.IsPackedAny() && !test.NoUnpack { 279 up, err := v.UnpackAll() 280 if err != nil { 281 t.Errorf("value unpack error: %v", err) 282 } 283 v = up 284 } 285 286 buf, err = v.MarshalJSON() 287 if err != nil { 288 t.Errorf("value render error: %v", err) 289 } 290 if !jsonDiff(t, buf, test.WantValue) { 291 t.Error("value render mismatch!") 292 t.FailNow() 293 } 294 }) 295 } 296 } 297 } 298 299 func TestStorageValues(t *testing.T) { 300 var ( 301 next int 302 err error 303 ) 304 scanTestFiles(t, "storage") 305 trace = t.Logf 306 // dbg = t.Logf 307 for { 308 var tests []testcase 309 next, err = loadNextTestFile("storage", next, &tests) 310 if err != nil { 311 if err == io.EOF { 312 break 313 } 314 t.Error(err) 315 if len(tests) == 0 { 316 break 317 } 318 continue 319 } 320 for _, test := range tests { 321 t.Run(test.Name, func(t *testing.T) { 322 typ1 := checkTypeEncoding(t, test) 323 val1 := checkValueEncoding(t, test) 324 325 // test storage value 326 v := Value{ 327 Type: typ1, // from binary 328 Value: val1, // from binary 329 Render: RENDER_TYPE_FAIL, 330 } 331 if v.IsPackedAny() && !test.NoUnpack { 332 up, err := v.UnpackAll() 333 if err != nil { 334 t.Errorf("value unpack error: %v", err) 335 t.FailNow() 336 } 337 v = up 338 } 339 340 buf, err := v.MarshalJSON() 341 if err != nil { 342 t.Errorf("value render error: %v", err) 343 t.FailNow() 344 } 345 if !jsonDiff(t, buf, test.WantValue) { 346 t.Error("value render mismatch, see log for details") 347 t.FailNow() 348 } 349 }) 350 } 351 } 352 } 353 354 func TestParamsValues(t *testing.T) { 355 var ( 356 next int 357 err error 358 ) 359 scanTestFiles(t, "params") 360 trace = t.Logf 361 // dbg = t.Logf 362 for { 363 var tests []testcase 364 next, err = loadNextTestFile("params", next, &tests) 365 if err != nil { 366 if err == io.EOF { 367 break 368 } 369 t.Error(err) 370 if len(tests) == 0 { 371 break 372 } 373 continue 374 } 375 for _, test := range tests { 376 t.Run(test.Name, func(t *testing.T) { 377 typ1 := checkTypeEncoding(t, test) 378 val1 := checkValueEncoding(t, test) 379 380 // test storage value 381 v := Value{ 382 Type: typ1, // from binary 383 Value: val1, // from binary 384 Render: RENDER_TYPE_FAIL, 385 } 386 if v.IsPackedAny() && !test.NoUnpack { 387 up, err := v.UnpackAll() 388 if err != nil { 389 t.Errorf("value unpack error: %v", err) 390 } 391 v = up 392 } 393 394 buf, err := v.MarshalJSON() 395 if err != nil { 396 t.Errorf("value render error: %v", err) 397 } 398 if !jsonDiff(t, buf, test.WantValue) { 399 t.Error("value render mismatch, see log for details") 400 t.FailNow() 401 } 402 }) 403 } 404 } 405 }