github.com/avahowell/sia@v0.5.1-beta.0.20160524050156-83dcc3d37c94/modules/host/storagemanager/storagefolders_test.go (about) 1 package storagemanager 2 3 import ( 4 "testing" 5 6 "github.com/NebulousLabs/Sia/crypto" 7 "github.com/NebulousLabs/Sia/modules" 8 ) 9 10 // TestStorageFolderUIDString probes the uidString method of the storage 11 // folder. 12 func TestStorageFolderUIDString(t *testing.T) { 13 if testing.Short() { 14 t.SkipNow() 15 } 16 t.Parallel() 17 18 // Create a series of uid->string mappings that represent the expected 19 // output of calling uidString on a storage folder. 20 trials := []struct { 21 uid []byte 22 str string 23 }{ 24 { 25 []byte{0}, 26 "00", 27 }, 28 { 29 []byte{255}, 30 "ff", 31 }, 32 { 33 []byte{50}, 34 "32", 35 }, 36 { 37 []byte{61}, 38 "3d", 39 }, 40 { 41 []byte{248}, 42 "f8", 43 }, 44 } 45 for _, trial := range trials { 46 sf := &storageFolder{ 47 UID: trial.uid, 48 } 49 str := sf.uidString() 50 if str != trial.str { 51 t.Error("failed UID string trial", str, sf.uidString()) 52 } 53 } 54 } 55 56 // TestStorageFolderUIDStringSanity probes the sanity checks of the uidString 57 // method of the storage folder. 58 func TestStorageFolderUIDStringSanity(t *testing.T) { 59 if testing.Short() { 60 t.SkipNow() 61 } 62 t.Parallel() 63 64 // Create a storage folder with an illegal UID size. 65 sf := &storageFolder{ 66 UID: []byte{0, 1}, 67 } 68 // Catch the resulting panic. 69 defer func() { 70 r := recover() 71 if r == nil { 72 t.Error("sanity check was not triggered upon incorrect usage of uidString") 73 } 74 }() 75 _ = sf.uidString() 76 } 77 78 // TestAddStorageFolderUIDCollisions checks that storage folders can be added 79 // with no risk of producing collisions in the storage folder UIDs. This test 80 // relies on (explicitly checked) assumptions about the size of the name and 81 // the number of allowed storage folders. 82 func TestAddStorageFolderUIDCollisions(t *testing.T) { 83 if testing.Short() { 84 t.SkipNow() 85 } 86 t.Parallel() 87 smt, err := newStorageManagerTester("TestAddStorageFolderUIDCollisions") 88 if err != nil { 89 t.Fatal(err) 90 } 91 defer smt.Close() 92 93 // Check that the environment requirements for the test have been met. 94 if storageFolderUIDSize != 1 { 95 t.Fatal("For this test, the storage manager must be using storage folder UIDs that are 1 byte") 96 } 97 if maximumStorageFolders < 100 { 98 t.Fatal("For this test, the storage manager must be allowed to have at least 100 storage folders") 99 } 100 101 // Create 100 storage folders, and check that there are no collisions 102 // between any of them. Because the UID is only using 1 byte, once there 103 // are more than 64 there will be at least 1/4 chance of a collision for 104 // each randomly selected UID. Running into collisions is virtually 105 // guaranteed, and running into repeated collisions (where two UIDs 106 // consecutively collide with existing UIDs) are highly likely. 107 for i := 0; i < maximumStorageFolders; i++ { 108 err = smt.addRandFolder(minimumStorageFolderSize) 109 if err != nil { 110 t.Fatal(err) 111 } 112 } 113 // Check that there are no collisions. 114 uidMap := make(map[uint8]struct{}) 115 for _, sf := range smt.sm.storageFolders { 116 _, exists := uidMap[uint8(sf.UID[0])] 117 if exists { 118 t.Error("Collision") 119 } 120 uidMap[uint8(sf.UID[0])] = struct{}{} 121 } 122 // For coverage purposes, try adding a storage folder after the maximum 123 // number of storage folders has been reached. 124 err = smt.addRandFolder(minimumStorageFolderSize) 125 if err != errMaxStorageFolders { 126 t.Fatal("expecting errMaxStorageFolders:", err) 127 } 128 129 // Try again, this time removing a random storage folder and then adding 130 // another one repeatedly - enough times to exceed the 256 possible folder 131 // UIDs that can be chosen in the testing environment. 132 for i := 0; i < 300; i++ { 133 // Replace the very first storage folder. 134 err = smt.sm.RemoveStorageFolder(0, false) 135 if err != nil { 136 t.Fatal(err) 137 } 138 err = smt.addRandFolder(minimumStorageFolderSize) 139 if err != nil { 140 t.Fatal(err) 141 } 142 143 // Replace a random storage folder. 144 n, err := crypto.RandIntn(100) 145 if err != nil { 146 t.Fatal(err) 147 } 148 err = smt.sm.RemoveStorageFolder(n, false) 149 if err != nil { 150 t.Fatal(err) 151 } 152 err = smt.addRandFolder(minimumStorageFolderSize) 153 if err != nil { 154 t.Fatal(err) 155 } 156 } 157 uidMap = make(map[uint8]struct{}) 158 for _, sf := range smt.sm.storageFolders { 159 _, exists := uidMap[uint8(sf.UID[0])] 160 if exists { 161 t.Error("Collision") 162 } 163 uidMap[uint8(sf.UID[0])] = struct{}{} 164 } 165 } 166 167 // TestEmptiestStorageFolder checks that emptiestStorageFolder will correctly 168 // select the emptiest storage folder out of a provided list of storage 169 // folders. 170 func TestEmptiestStorageFolder(t *testing.T) { 171 if testing.Short() { 172 t.SkipNow() 173 } 174 t.Parallel() 175 176 // Create a series of uid->string mappings that represent the expected 177 // output of calling uidString on a storage folder. 178 trials := []struct { 179 folders []*storageFolder 180 emptiestIndex int 181 }{ 182 // Empty input. 183 { 184 []*storageFolder{}, 185 -1, 186 }, 187 // Single valid storage folder. 188 { 189 []*storageFolder{ 190 { 191 Size: minimumStorageFolderSize, 192 SizeRemaining: minimumStorageFolderSize, 193 }, 194 }, 195 0, 196 }, 197 // Single full storage folder. 198 { 199 []*storageFolder{ 200 { 201 Size: minimumStorageFolderSize, 202 SizeRemaining: 0, 203 }, 204 }, 205 -1, 206 }, 207 // Single nearly full storage folder. 208 { 209 []*storageFolder{ 210 { 211 Size: minimumStorageFolderSize, 212 SizeRemaining: modules.SectorSize - 1, 213 }, 214 }, 215 -1, 216 }, 217 // Two valid storage folders, first is emptier. 218 { 219 []*storageFolder{ 220 { 221 Size: minimumStorageFolderSize, 222 SizeRemaining: modules.SectorSize + 1, 223 }, 224 { 225 Size: minimumStorageFolderSize, 226 SizeRemaining: modules.SectorSize, 227 }, 228 }, 229 0, 230 }, 231 // Two valid storage folders, second is emptier. 232 { 233 []*storageFolder{ 234 { 235 Size: minimumStorageFolderSize, 236 SizeRemaining: modules.SectorSize, 237 }, 238 { 239 Size: minimumStorageFolderSize, 240 SizeRemaining: modules.SectorSize + 1, 241 }, 242 }, 243 1, 244 }, 245 // Two valid storage folders, first is emptier by percentage but can't 246 // hold a new sector. 247 { 248 []*storageFolder{ 249 { 250 Size: minimumStorageFolderSize, 251 SizeRemaining: modules.SectorSize - 1, 252 }, 253 { 254 Size: minimumStorageFolderSize * 5, 255 SizeRemaining: modules.SectorSize, 256 }, 257 }, 258 1, 259 }, 260 // Two valid storage folders, first is emptier by volume but not 261 // percentage. 262 { 263 []*storageFolder{ 264 { 265 Size: minimumStorageFolderSize * 4, 266 SizeRemaining: modules.SectorSize * 2, 267 }, 268 { 269 Size: minimumStorageFolderSize, 270 SizeRemaining: modules.SectorSize, 271 }, 272 }, 273 1, 274 }, 275 // Two valid storage folders, second is emptier by volume but not 276 // percentage. 277 { 278 []*storageFolder{ 279 { 280 Size: minimumStorageFolderSize, 281 SizeRemaining: modules.SectorSize, 282 }, 283 { 284 Size: minimumStorageFolderSize * 4, 285 SizeRemaining: modules.SectorSize * 2, 286 }, 287 }, 288 0, 289 }, 290 // Three valid storage folders, second is emptier by percentage but not 291 // volume. 292 { 293 []*storageFolder{ 294 { 295 Size: minimumStorageFolderSize * 4, 296 SizeRemaining: modules.SectorSize * 2, 297 }, 298 { 299 Size: minimumStorageFolderSize, 300 SizeRemaining: modules.SectorSize, 301 }, 302 { 303 Size: minimumStorageFolderSize * 4, 304 SizeRemaining: modules.SectorSize * 2, 305 }, 306 }, 307 1, 308 }, 309 // Three storage folders, none have room for a sector. 310 { 311 []*storageFolder{ 312 { 313 Size: minimumStorageFolderSize * 4, 314 SizeRemaining: modules.SectorSize - 1, 315 }, 316 { 317 Size: minimumStorageFolderSize, 318 SizeRemaining: 0, 319 }, 320 { 321 Size: minimumStorageFolderSize * 4, 322 SizeRemaining: 1, 323 }, 324 }, 325 -1, 326 }, 327 } 328 for i, trial := range trials { 329 sf, index := emptiestStorageFolder(trial.folders) 330 if index != trial.emptiestIndex { 331 t.Error("trial", i, "index mismatch") 332 } 333 if index > 0 && sf != trial.folders[index] { 334 t.Error("trial", i, "folder mismatch") 335 } 336 if index < 0 && sf != nil { 337 t.Error("non-nil storage folder returned but there was no winner") 338 } 339 } 340 } 341 342 // TestRepeatStorageFolderPath checks that the host correctly rejects a storage 343 // folder if there is already a storage folder linked to the same path. 344 func TestRepeatStorageFolderPath(t *testing.T) { 345 if testing.Short() { 346 t.SkipNow() 347 } 348 t.Parallel() 349 smt, err := newStorageManagerTester("TestRepeatStorageFolderPath") 350 if err != nil { 351 t.Fatal(err) 352 } 353 defer smt.Close() 354 355 err = smt.sm.AddStorageFolder(smt.persistDir, minimumStorageFolderSize) 356 if err != nil { 357 t.Fatal(err) 358 } 359 err = smt.sm.AddStorageFolder(smt.persistDir, minimumStorageFolderSize) 360 if err != errRepeatFolder { 361 t.Fatal("expected errRepeatFolder, got", err) 362 } 363 }