You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

184 lines
5.9 KiB

  1. /*
  2. * Minio Cloud Storage, (C) 2016 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. package cmd
  17. import (
  18. "bytes"
  19. "testing"
  20. )
  21. // mustEncodeData - encodes data slice and provides encoded 2 dimensional slice.
  22. func mustEncodeData(data []byte, dataBlocks, parityBlocks int) [][]byte {
  23. encodedData, err := encodeData(data, dataBlocks, parityBlocks)
  24. if err != nil {
  25. // Upon failure panic this function.
  26. panic(err)
  27. }
  28. return encodedData
  29. }
  30. // Generates good encoded data with one parity block and data block missing.
  31. func getGoodEncodedData(data []byte, dataBlocks, parityBlocks int) [][]byte {
  32. encodedData := mustEncodeData(data, dataBlocks, parityBlocks)
  33. encodedData[3] = nil
  34. encodedData[1] = nil
  35. return encodedData
  36. }
  37. // Generates bad encoded data with one parity block and data block with garbage data.
  38. func getBadEncodedData(data []byte, dataBlocks, parityBlocks int) [][]byte {
  39. encodedData := mustEncodeData(data, dataBlocks, parityBlocks)
  40. encodedData[3] = []byte("garbage")
  41. encodedData[1] = []byte("garbage")
  42. return encodedData
  43. }
  44. // Generates encoded data with all data blocks missing.
  45. func getMissingData(data []byte, dataBlocks, parityBlocks int) [][]byte {
  46. encodedData := mustEncodeData(data, dataBlocks, parityBlocks)
  47. for i := 0; i < dataBlocks+1; i++ {
  48. encodedData[i] = nil
  49. }
  50. return encodedData
  51. }
  52. // Generates encoded data with less number of blocks than expected data blocks.
  53. func getInsufficientData(data []byte, dataBlocks, parityBlocks int) [][]byte {
  54. encodedData := mustEncodeData(data, dataBlocks, parityBlocks)
  55. // Return half of the data.
  56. return encodedData[:dataBlocks/2]
  57. }
  58. // Represents erasure encoding matrix dataBlocks and paritBlocks.
  59. type encodingMatrix struct {
  60. dataBlocks int
  61. parityBlocks int
  62. }
  63. // List of encoding matrices the tests will run on.
  64. var encodingMatrices = []encodingMatrix{
  65. {3, 3}, // 3 data, 3 parity blocks.
  66. {4, 4}, // 4 data, 4 parity blocks.
  67. {5, 5}, // 5 data, 5 parity blocks.
  68. {6, 6}, // 6 data, 6 parity blocks.
  69. {7, 7}, // 7 data, 7 parity blocks.
  70. {8, 8}, // 8 data, 8 parity blocks.
  71. }
  72. // Tests erasure decoding functionality for various types of inputs.
  73. func TestErasureDecode(t *testing.T) {
  74. data := []byte("Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.")
  75. // List of decoding cases
  76. // - validates bad encoded data.
  77. // - validates good encoded data.
  78. // - validates insufficient data.
  79. testDecodeCases := []struct {
  80. enFn func([]byte, int, int) [][]byte
  81. shouldPass bool
  82. }{
  83. // Generates bad encoded data.
  84. {
  85. enFn: getBadEncodedData,
  86. shouldPass: false,
  87. },
  88. // Generates good encoded data.
  89. {
  90. enFn: getGoodEncodedData,
  91. shouldPass: true,
  92. },
  93. // Generates missing data.
  94. {
  95. enFn: getMissingData,
  96. shouldPass: false,
  97. },
  98. // Generates short data.
  99. {
  100. enFn: getInsufficientData,
  101. shouldPass: false,
  102. },
  103. }
  104. // Validates all decode tests.
  105. for i, testCase := range testDecodeCases {
  106. for _, encodingMatrix := range encodingMatrices {
  107. // Encoding matrix.
  108. dataBlocks := encodingMatrix.dataBlocks
  109. parityBlocks := encodingMatrix.parityBlocks
  110. // Data block size.
  111. blockSize := len(data)
  112. // Generates encoded data based on type of testCase function.
  113. encodedData := testCase.enFn(data, dataBlocks, parityBlocks)
  114. // Decodes the data.
  115. err := decodeData(encodedData, dataBlocks, parityBlocks)
  116. if err != nil && testCase.shouldPass {
  117. t.Errorf("Test %d: Expected to pass by failed instead with %s", i+1, err)
  118. }
  119. // Proceed to extract the data blocks.
  120. decodedDataWriter := new(bytes.Buffer)
  121. _, err = writeDataBlocks(decodedDataWriter, encodedData, dataBlocks, 0, int64(blockSize))
  122. if err != nil && testCase.shouldPass {
  123. t.Errorf("Test %d: Expected to pass by failed instead with %s", i+1, err)
  124. }
  125. // Validate if decoded data is what we expected.
  126. if bytes.Equal(decodedDataWriter.Bytes(), data) != testCase.shouldPass {
  127. err := errUnexpected
  128. t.Errorf("Test %d: Expected to pass by failed instead %s", i+1, err)
  129. }
  130. }
  131. }
  132. }
  133. // Setup for erasureCreateFile and erasureReadFile tests.
  134. type erasureTestSetup struct {
  135. dataBlocks int
  136. parityBlocks int
  137. blockSize int64
  138. diskPaths []string
  139. disks []StorageAPI
  140. }
  141. // Removes the temporary disk directories.
  142. func (e erasureTestSetup) Remove() {
  143. for _, path := range e.diskPaths {
  144. removeAll(path)
  145. }
  146. }
  147. // Returns an initialized setup for erasure tests.
  148. func newErasureTestSetup(dataBlocks int, parityBlocks int, blockSize int64) (*erasureTestSetup, error) {
  149. diskPaths := make([]string, dataBlocks+parityBlocks)
  150. disks := make([]StorageAPI, len(diskPaths))
  151. var err error
  152. for i := range diskPaths {
  153. disks[i], diskPaths[i], err = newPosixTestSetup()
  154. if err != nil {
  155. return nil, err
  156. }
  157. err = disks[i].MakeVol("testbucket")
  158. if err != nil {
  159. return nil, err
  160. }
  161. }
  162. return &erasureTestSetup{dataBlocks, parityBlocks, blockSize, diskPaths, disks}, nil
  163. }