github.com/minio/minio@v0.0.0-20240328213742-3f72439b8a27/cmd/erasure_test.go (about) 1 // Copyright (c) 2015-2021 MinIO, Inc. 2 // 3 // This file is part of MinIO Object Storage stack 4 // 5 // This program is free software: you can redistribute it and/or modify 6 // it under the terms of the GNU Affero General Public License as published by 7 // the Free Software Foundation, either version 3 of the License, or 8 // (at your option) any later version. 9 // 10 // This program is distributed in the hope that it will be useful 11 // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 // GNU Affero General Public License for more details. 14 // 15 // You should have received a copy of the GNU Affero General Public License 16 // along with this program. If not, see <http://www.gnu.org/licenses/>. 17 18 package cmd 19 20 import ( 21 "bytes" 22 "context" 23 "crypto/rand" 24 "io" 25 "testing" 26 ) 27 28 var erasureEncodeDecodeTests = []struct { 29 dataBlocks, parityBlocks int 30 missingData, missingParity int 31 reconstructParity bool 32 shouldFail bool 33 }{ 34 {dataBlocks: 2, parityBlocks: 2, missingData: 0, missingParity: 0, reconstructParity: true, shouldFail: false}, 35 {dataBlocks: 3, parityBlocks: 3, missingData: 1, missingParity: 0, reconstructParity: true, shouldFail: false}, 36 {dataBlocks: 4, parityBlocks: 4, missingData: 2, missingParity: 0, reconstructParity: false, shouldFail: false}, 37 {dataBlocks: 5, parityBlocks: 5, missingData: 0, missingParity: 1, reconstructParity: true, shouldFail: false}, 38 {dataBlocks: 6, parityBlocks: 6, missingData: 0, missingParity: 2, reconstructParity: true, shouldFail: false}, 39 {dataBlocks: 7, parityBlocks: 7, missingData: 1, missingParity: 1, reconstructParity: false, shouldFail: false}, 40 {dataBlocks: 8, parityBlocks: 8, missingData: 3, missingParity: 2, reconstructParity: false, shouldFail: false}, 41 {dataBlocks: 2, parityBlocks: 2, missingData: 2, missingParity: 1, reconstructParity: true, shouldFail: true}, 42 {dataBlocks: 4, parityBlocks: 2, missingData: 2, missingParity: 2, reconstructParity: false, shouldFail: true}, 43 {dataBlocks: 8, parityBlocks: 4, missingData: 2, missingParity: 2, reconstructParity: false, shouldFail: false}, 44 } 45 46 func TestErasureEncodeDecode(t *testing.T) { 47 data := make([]byte, 256) 48 if _, err := io.ReadFull(rand.Reader, data); err != nil { 49 t.Fatalf("Failed to read random data: %v", err) 50 } 51 for i, test := range erasureEncodeDecodeTests { 52 buffer := make([]byte, len(data), 2*len(data)) 53 copy(buffer, data) 54 55 erasure, err := NewErasure(context.Background(), test.dataBlocks, test.parityBlocks, blockSizeV2) 56 if err != nil { 57 t.Fatalf("Test %d: failed to create erasure: %v", i, err) 58 } 59 encoded, err := erasure.EncodeData(context.Background(), buffer) 60 if err != nil { 61 t.Fatalf("Test %d: failed to encode data: %v", i, err) 62 } 63 64 for j := range encoded[:test.missingData] { 65 encoded[j] = nil 66 } 67 for j := test.dataBlocks; j < test.dataBlocks+test.missingParity; j++ { 68 encoded[j] = nil 69 } 70 71 if test.reconstructParity { 72 err = erasure.DecodeDataAndParityBlocks(context.Background(), encoded) 73 } else { 74 err = erasure.DecodeDataBlocks(encoded) 75 } 76 77 if err == nil && test.shouldFail { 78 t.Errorf("Test %d: test should fail but it passed", i) 79 } 80 if err != nil && !test.shouldFail { 81 t.Errorf("Test %d: test should pass but it failed: %v", i, err) 82 } 83 84 decoded := encoded 85 if !test.shouldFail { 86 if test.reconstructParity { 87 for j := range decoded { 88 if decoded[j] == nil { 89 t.Errorf("Test %d: failed to reconstruct shard %d", i, j) 90 } 91 } 92 } else { 93 for j := range decoded[:test.dataBlocks] { 94 if decoded[j] == nil { 95 t.Errorf("Test %d: failed to reconstruct data shard %d", i, j) 96 } 97 } 98 } 99 100 decodedData := new(bytes.Buffer) 101 if _, err = writeDataBlocks(context.Background(), decodedData, decoded, test.dataBlocks, 0, int64(len(data))); err != nil { 102 t.Errorf("Test %d: failed to write data blocks: %v", i, err) 103 } 104 if !bytes.Equal(decodedData.Bytes(), data) { 105 t.Errorf("Test %d: Decoded data does not match original data: got: %v want: %v", i, decodedData.Bytes(), data) 106 } 107 } 108 } 109 } 110 111 // Setup for erasureCreateFile and erasureReadFile tests. 112 type erasureTestSetup struct { 113 dataBlocks int 114 parityBlocks int 115 blockSize int64 116 diskPaths []string 117 disks []StorageAPI 118 } 119 120 // Returns an initialized setup for erasure tests. 121 func newErasureTestSetup(tb testing.TB, dataBlocks int, parityBlocks int, blockSize int64) (*erasureTestSetup, error) { 122 diskPaths := make([]string, dataBlocks+parityBlocks) 123 disks := make([]StorageAPI, len(diskPaths)) 124 var err error 125 for i := range diskPaths { 126 disks[i], diskPaths[i], err = newXLStorageTestSetup(tb) 127 if err != nil { 128 return nil, err 129 } 130 err = disks[i].MakeVol(context.Background(), "testbucket") 131 if err != nil { 132 return nil, err 133 } 134 } 135 return &erasureTestSetup{dataBlocks, parityBlocks, blockSize, diskPaths, disks}, nil 136 }