storj.io/minio@v0.0.0-20230509071714-0cbc90f649b1/cmd/erasure_test.go (about)

     1  /*
     2   * MinIO Cloud Storage, (C) 2016-2020 MinIO, Inc.
     3   *
     4   * Licensed under the Apache License, Version 2.0 (the "License");
     5   * you may not use this file except in compliance with the License.
     6   * You may obtain a copy of the License at
     7   *
     8   *     http://www.apache.org/licenses/LICENSE-2.0
     9   *
    10   * Unless required by applicable law or agreed to in writing, software
    11   * distributed under the License is distributed on an "AS IS" BASIS,
    12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13   * See the License for the specific language governing permissions and
    14   * limitations under the License.
    15   */
    16  
    17  package cmd
    18  
    19  import (
    20  	"bytes"
    21  	"context"
    22  	"crypto/rand"
    23  	"io"
    24  	"os"
    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  // Removes the temporary disk directories.
   121  func (e erasureTestSetup) Remove() {
   122  	for _, path := range e.diskPaths {
   123  		os.RemoveAll(path)
   124  	}
   125  }
   126  
   127  // Returns an initialized setup for erasure tests.
   128  func newErasureTestSetup(dataBlocks int, parityBlocks int, blockSize int64) (*erasureTestSetup, error) {
   129  	diskPaths := make([]string, dataBlocks+parityBlocks)
   130  	disks := make([]StorageAPI, len(diskPaths))
   131  	var err error
   132  	for i := range diskPaths {
   133  		disks[i], diskPaths[i], err = newXLStorageTestSetup()
   134  		if err != nil {
   135  			return nil, err
   136  		}
   137  		err = disks[i].MakeVol(context.Background(), "testbucket")
   138  		if err != nil {
   139  			return nil, err
   140  		}
   141  	}
   142  	return &erasureTestSetup{dataBlocks, parityBlocks, blockSize, diskPaths, disks}, nil
   143  }