github.com/Finschia/finschia-sdk@v0.48.1/snapshots/helpers_test.go (about) 1 package snapshots_test 2 3 import ( 4 "bufio" 5 "bytes" 6 "compress/zlib" 7 "crypto/sha256" 8 "errors" 9 "io" 10 "os" 11 "testing" 12 "time" 13 14 protoio "github.com/gogo/protobuf/io" 15 "github.com/stretchr/testify/require" 16 17 dbm "github.com/tendermint/tm-db" 18 19 "github.com/Finschia/finschia-sdk/snapshots" 20 snapshottypes "github.com/Finschia/finschia-sdk/snapshots/types" 21 sdkerrors "github.com/Finschia/finschia-sdk/types/errors" 22 ) 23 24 func checksums(slice [][]byte) [][]byte { 25 hasher := sha256.New() 26 checksums := make([][]byte, len(slice)) 27 for i, chunk := range slice { 28 hasher.Write(chunk) 29 checksums[i] = hasher.Sum(nil) 30 hasher.Reset() 31 } 32 return checksums 33 } 34 35 func hash(chunks [][]byte) []byte { 36 hasher := sha256.New() 37 for _, chunk := range chunks { 38 hasher.Write(chunk) 39 } 40 return hasher.Sum(nil) 41 } 42 43 func makeChunks(chunks [][]byte) <-chan io.ReadCloser { 44 ch := make(chan io.ReadCloser, len(chunks)) 45 for _, chunk := range chunks { 46 ch <- io.NopCloser(bytes.NewReader(chunk)) 47 } 48 close(ch) 49 return ch 50 } 51 52 func readChunks(chunks <-chan io.ReadCloser) [][]byte { 53 bodies := [][]byte{} 54 for chunk := range chunks { 55 body, err := io.ReadAll(chunk) 56 if err != nil { 57 panic(err) 58 } 59 bodies = append(bodies, body) 60 } 61 return bodies 62 } 63 64 // snapshotItems serialize a array of bytes as SnapshotItem_ExtensionPayload, and return the chunks. 65 func snapshotItems(items [][]byte) [][]byte { 66 // copy the same parameters from the code 67 snapshotChunkSize := uint64(10e6) 68 snapshotBufferSize := int(snapshotChunkSize) 69 70 ch := make(chan io.ReadCloser) 71 go func() { 72 chunkWriter := snapshots.NewChunkWriter(ch, snapshotChunkSize) 73 bufWriter := bufio.NewWriterSize(chunkWriter, snapshotBufferSize) 74 zWriter, _ := zlib.NewWriterLevel(bufWriter, 7) 75 protoWriter := protoio.NewDelimitedWriter(zWriter) 76 for _, item := range items { 77 snapshottypes.WriteExtensionItem(protoWriter, item) 78 } 79 protoWriter.Close() 80 zWriter.Close() 81 bufWriter.Flush() 82 chunkWriter.Close() 83 }() 84 85 var chunks [][]byte 86 for chunkBody := range ch { 87 chunk, err := io.ReadAll(chunkBody) 88 if err != nil { 89 panic(err) 90 } 91 chunks = append(chunks, chunk) 92 } 93 return chunks 94 } 95 96 type mockSnapshotter struct { 97 items [][]byte 98 } 99 100 func (m *mockSnapshotter) Restore( 101 height uint64, format uint32, protoReader protoio.Reader, 102 ) (snapshottypes.SnapshotItem, error) { 103 if format == 0 { 104 return snapshottypes.SnapshotItem{}, snapshottypes.ErrUnknownFormat 105 } 106 if m.items != nil { 107 return snapshottypes.SnapshotItem{}, errors.New("already has contents") 108 } 109 110 m.items = [][]byte{} 111 for { 112 item := &snapshottypes.SnapshotItem{} 113 err := protoReader.ReadMsg(item) 114 if err == io.EOF { 115 break 116 } else if err != nil { 117 return snapshottypes.SnapshotItem{}, sdkerrors.Wrap(err, "invalid protobuf message") 118 } 119 payload := item.GetExtensionPayload() 120 if payload == nil { 121 return snapshottypes.SnapshotItem{}, sdkerrors.Wrap(err, "invalid protobuf message") 122 } 123 m.items = append(m.items, payload.Payload) 124 } 125 126 return snapshottypes.SnapshotItem{}, nil 127 } 128 129 func (m *mockSnapshotter) Snapshot(height uint64, protoWriter protoio.Writer) error { 130 for _, item := range m.items { 131 if err := snapshottypes.WriteExtensionItem(protoWriter, item); err != nil { 132 return err 133 } 134 } 135 return nil 136 } 137 138 func (m *mockSnapshotter) SnapshotFormat() uint32 { 139 return 1 140 } 141 142 func (m *mockSnapshotter) SupportedFormats() []uint32 { 143 return []uint32{1} 144 } 145 146 // setupBusyManager creates a manager with an empty store that is busy creating a snapshot at height 1. 147 // The snapshot will complete when the returned closer is called. 148 func setupBusyManager(t *testing.T) *snapshots.Manager { 149 // os.MkdirTemp() is used instead of testing.T.TempDir() 150 // see https://github.com/cosmos/cosmos-sdk/pull/8475 for 151 // this change's rationale. 152 tempdir, err := os.MkdirTemp("", "") 153 require.NoError(t, err) 154 t.Cleanup(func() { _ = os.RemoveAll(tempdir) }) 155 156 store, err := snapshots.NewStore(dbm.NewMemDB(), tempdir) 157 require.NoError(t, err) 158 hung := newHungSnapshotter() 159 mgr := snapshots.NewManager(store, hung) 160 161 go func() { 162 _, err := mgr.Create(1) 163 require.NoError(t, err) 164 }() 165 time.Sleep(10 * time.Millisecond) 166 t.Cleanup(hung.Close) 167 168 return mgr 169 } 170 171 // hungSnapshotter can be used to test operations in progress. Call close to end the snapshot. 172 type hungSnapshotter struct { 173 ch chan struct{} 174 } 175 176 func newHungSnapshotter() *hungSnapshotter { 177 return &hungSnapshotter{ 178 ch: make(chan struct{}), 179 } 180 } 181 182 func (m *hungSnapshotter) Close() { 183 close(m.ch) 184 } 185 186 func (m *hungSnapshotter) Snapshot(height uint64, protoWriter protoio.Writer) error { 187 <-m.ch 188 return nil 189 } 190 191 func (m *hungSnapshotter) Restore( 192 height uint64, format uint32, protoReader protoio.Reader, 193 ) (snapshottypes.SnapshotItem, error) { 194 panic("not implemented") 195 }