github.com/lino-network/lino@v0.6.11/testsuites/golden.go (about) 1 package testsuites 2 3 import ( 4 "encoding/json" 5 "flag" 6 "io/ioutil" 7 "os" 8 "path/filepath" 9 "sort" 10 "strings" 11 "time" 12 13 sdk "github.com/cosmos/cosmos-sdk/types" 14 "github.com/lino-network/lino/testutils" 15 ) 16 17 var update = flag.Bool("update", false, "update .golden files") 18 19 type dumperManager interface { 20 NewDumper() *testutils.Dumper 21 } 22 23 type GoldenTestSuite struct { 24 CtxTestSuite 25 dm dumperManager 26 storeKey *sdk.KVStoreKey 27 } 28 29 func NewGoldenTestSuite(dm dumperManager, storeKey *sdk.KVStoreKey) GoldenTestSuite { 30 return GoldenTestSuite{ 31 dm: dm, 32 storeKey: storeKey, 33 } 34 } 35 36 func (suite *GoldenTestSuite) LoadState(enableSubTest bool, importJsonPaths ...string) { 37 // Maybe we shouldn't SetupCtx but only reset the store here. 38 suite.SetupCtx(suite.height, time.Unix(0, 0), suite.storeKey) 39 if suite.dm == nil { 40 suite.FailNow("Should register a dumpManager before calling LoadState") 41 } 42 d := suite.dm.NewDumper() 43 t := suite.T() 44 name := t.Name() 45 parentName := name[:strings.LastIndex(name, "/")] 46 47 var fileName string 48 if enableSubTest { 49 fileName = name 50 } else { 51 fileName = parentName 52 } 53 54 gp := filepath.Join("input", fileName+".input") 55 if fileExists(gp) { 56 d.LoadFromFile(suite.Ctx, gp) 57 } 58 for _, path := range importJsonPaths { 59 gp := filepath.Join("input", "common", path+".input") 60 d.LoadFromFile(suite.Ctx, gp) 61 } 62 } 63 64 // XXX(@ryanli): Maybe we use delta for golden, better readability 65 func (suite *GoldenTestSuite) Golden() { 66 if suite.dm == nil { 67 suite.FailNow("Should register a dumpManager before calling Golden") 68 } 69 d := suite.dm.NewDumper() 70 b := d.ToJSON(suite.Ctx) 71 // Add a newline character at the end of file 72 b = append(b, '\n') 73 t := suite.T() 74 gp := filepath.Join("golden", t.Name()+".golden") 75 dir, _ := filepath.Split(gp) 76 err := ensureDir(dir) 77 suite.NoError(err) 78 if *update { 79 err := ioutil.WriteFile(gp, b, 0644) 80 suite.NoError(err) 81 } 82 g, err := ioutil.ReadFile(gp) 83 suite.NoError(err) 84 suite.Equal(b, g) 85 } 86 87 func (suite *GoldenTestSuite) AssertStateUnchanged(enableSubTest bool, importJsonPaths ...string) { 88 t := suite.T() 89 name := t.Name() 90 parentName := name[:strings.LastIndex(name, "/")] 91 var fileName string 92 if enableSubTest { 93 fileName = name 94 } else { 95 fileName = parentName 96 } 97 gp := filepath.Join("input", fileName+".input") 98 states := make(testutils.JSONState, 0) 99 if fileExists(gp) { 100 input, err := ioutil.ReadFile(gp) 101 suite.NoError(err) 102 err = json.Unmarshal(input, &states) 103 suite.NoError(err) 104 } 105 106 for _, p := range importJsonPaths { 107 tmp := make(testutils.JSONState, 0) 108 gp := filepath.Join("input", "common", p+".input") 109 input, err := ioutil.ReadFile(gp) 110 suite.NoError(err) 111 err = json.Unmarshal(input, &tmp) 112 suite.NoError(err) 113 states = append(states, tmp...) 114 } 115 116 // sort by prefix and then key 117 sort.Slice(states, func(i, j int) bool { 118 return states[i].Prefix < states[j].Prefix || 119 (states[i].Prefix == states[j].Prefix && states[i].Key < states[j].Key) 120 }) 121 122 inputStates, err := json.Marshal(states) 123 suite.NoError(err) 124 125 gp = filepath.Join("golden", t.Name()+".golden") 126 suite.NoError(err) 127 golden, err := ioutil.ReadFile(gp) 128 suite.NoError(err) 129 suite.JSONEq(string(inputStates), string(golden)) 130 } 131 132 func ensureDir(dirName string) error { 133 err := os.MkdirAll(dirName, 0700) 134 if err == nil || os.IsExist(err) { 135 return nil 136 } else { 137 return err 138 } 139 } 140 141 func fileExists(filename string) bool { 142 info, err := os.Stat(filename) 143 if os.IsNotExist(err) { 144 return false 145 } 146 return !info.IsDir() 147 }