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  }