gitlab.com/SiaPrime/SiaPrime@v1.4.1/modules/renter/siadir/siadirset_test.go (about) 1 package siadir 2 3 import ( 4 "os" 5 "path/filepath" 6 "sync" 7 "testing" 8 "time" 9 10 "gitlab.com/NebulousLabs/errors" 11 "gitlab.com/NebulousLabs/fastrand" 12 13 "gitlab.com/SiaPrime/SiaPrime/modules" 14 ) 15 16 // newTestSiaDirSet creates a new SiaDirSet 17 func newTestSiaDirSet() *SiaDirSet { 18 // Create params 19 dir := filepath.Join(os.TempDir(), "siadirs") 20 wal, _ := newTestWAL() 21 return NewSiaDirSet(dir, wal) 22 } 23 24 // newTestSiaDirSetWithDir creates a new SiaDirSet and SiaDir and makes sure 25 // that they are linked 26 func newTestSiaDirSetWithDir() (*SiaDirSetEntry, *SiaDirSet, error) { 27 // Create directory 28 dir := filepath.Join(os.TempDir(), "siadirs") 29 // Create SiaDirSet and SiaDirSetEntry 30 wal, _ := newTestWAL() 31 sds := NewSiaDirSet(dir, wal) 32 entry, err := sds.NewSiaDir(modules.RandomSiaPath()) 33 if err != nil { 34 return nil, nil, err 35 } 36 return entry, sds, nil 37 } 38 39 // TestInitRootDir checks that InitRootDir creates a siadir on disk and that it 40 // can be called again without returning an error 41 func TestInitRootDir(t *testing.T) { 42 if testing.Short() { 43 t.SkipNow() 44 } 45 t.Parallel() 46 47 // Create new SiaDirSet 48 sds := newTestSiaDirSet() 49 50 // Create a root SiaDirt 51 if err := sds.InitRootDir(); err != nil { 52 t.Fatal(err) 53 } 54 55 // Verify the siadir exists on disk 56 siaPath := modules.RootSiaPath().SiaDirMetadataSysPath(sds.staticRootDir) 57 _, err := os.Stat(siaPath) 58 if err != nil { 59 t.Fatal(err) 60 } 61 62 // Verify that the siadir is not stored in memory 63 if len(sds.siaDirMap) != 0 { 64 t.Fatal("SiaDirSet has siadirs in memory") 65 } 66 67 // Try initializing the root directory again, there should be no error 68 if err := sds.InitRootDir(); err != nil { 69 t.Fatal(err) 70 } 71 } 72 73 // TestSiaDirSetOpenClose tests that the threadCount of the siadir is 74 // incremented and decremented properly when Open() and Close() are called 75 func TestSiaDirSetOpenClose(t *testing.T) { 76 if testing.Short() { 77 t.SkipNow() 78 } 79 t.Parallel() 80 81 // Create SiaDirSet with SiaDir 82 entry, sds, err := newTestSiaDirSetWithDir() 83 if err != nil { 84 t.Fatal(err) 85 } 86 siaPath := entry.SiaPath() 87 exists, err := sds.Exists(siaPath) 88 if !exists { 89 t.Fatal("No SiaDirSetEntry found") 90 } 91 if err != nil { 92 t.Fatal(err) 93 } 94 95 // Confirm dir is in memory 96 if len(sds.siaDirMap) != 1 { 97 t.Fatalf("Expected SiaDirSet map to be of length 1, instead is length %v", len(sds.siaDirMap)) 98 } 99 100 // Confirm threadCount is incremented properly 101 if len(entry.threadMap) != 1 { 102 t.Fatalf("Expected threadMap to be of length 1, got %v", len(entry.threadMap)) 103 } 104 105 // Close SiaDirSetEntry 106 entry.Close() 107 108 // Confirm that threadCount was decremented 109 if len(entry.threadMap) != 0 { 110 t.Fatalf("Expected threadCount to be 0, got %v", len(entry.threadMap)) 111 } 112 113 // Confirm dir was removed from memory 114 if len(sds.siaDirMap) != 0 { 115 t.Fatalf("Expected SiaDirSet map to be empty, instead is length %v", len(sds.siaDirMap)) 116 } 117 118 // Open siafile again and confirm threadCount was incremented 119 entry, err = sds.Open(siaPath) 120 if err != nil { 121 t.Fatal(err) 122 } 123 if len(entry.threadMap) != 1 { 124 t.Fatalf("Expected threadCount to be 1, got %v", len(entry.threadMap)) 125 } 126 } 127 128 // TestDirsInMemory confirms that files are added and removed from memory 129 // as expected when files are in use and not in use 130 func TestDirsInMemory(t *testing.T) { 131 if testing.Short() { 132 t.SkipNow() 133 } 134 t.Parallel() 135 136 // Create SiaDirSet with SiaDir 137 entry, sds, err := newTestSiaDirSetWithDir() 138 if err != nil { 139 t.Fatal(err) 140 } 141 siaPath := entry.SiaPath() 142 exists, err := sds.Exists(siaPath) 143 if !exists { 144 t.Fatal("No SiaDirSetEntry found") 145 } 146 if err != nil { 147 t.Fatal(err) 148 } 149 // Confirm there is 1 dir in memory 150 if len(sds.siaDirMap) != 1 { 151 t.Fatal("Expected 1 dir in memory, got:", len(sds.siaDirMap)) 152 } 153 // Close File 154 err = entry.Close() 155 if err != nil { 156 t.Fatal(err) 157 } 158 // Confirm therte are no files in memory 159 if len(sds.siaDirMap) != 0 { 160 t.Fatal("Expected 0 files in memory, got:", len(sds.siaDirMap)) 161 } 162 163 // Test accessing the same dir from two separate threads 164 // 165 // Open dir 166 entry1, err := sds.Open(siaPath) 167 if err != nil { 168 t.Fatal(err) 169 } 170 // Confirm there is 1 dir in memory 171 if len(sds.siaDirMap) != 1 { 172 t.Fatal("Expected 1 dir in memory, got:", len(sds.siaDirMap)) 173 } 174 // Access the dir again 175 entry2, err := sds.Open(siaPath) 176 if err != nil { 177 t.Fatal(err) 178 } 179 // Confirm there is still only has 1 dir in memory 180 if len(sds.siaDirMap) != 1 { 181 t.Fatal("Expected 1 dir in memory, got:", len(sds.siaDirMap)) 182 } 183 // Close one of the dir instances 184 err = entry1.Close() 185 if err != nil { 186 t.Fatal(err) 187 } 188 // Confirm there is still only has 1 dir in memory 189 if len(sds.siaDirMap) != 1 { 190 t.Fatal("Expected 1 dir in memory, got:", len(sds.siaDirMap)) 191 } 192 193 // Confirm closing out remaining files removes all files from memory 194 // 195 // Close last instance of the first dir 196 err = entry2.Close() 197 if err != nil { 198 t.Fatal(err) 199 } 200 // Confirm there are no files in memory 201 if len(sds.siaDirMap) != 0 { 202 t.Fatal("Expected 0 files in memory, got:", len(sds.siaDirMap)) 203 } 204 } 205 206 // TestUpdateSiaDirSetMetadata probes the UpdateMetadata method of the SiaDirSet 207 func TestUpdateSiaDirSetMetadata(t *testing.T) { 208 if testing.Short() { 209 t.SkipNow() 210 } 211 t.Parallel() 212 213 // Create SiaDirSet with SiaDir 214 entry, sds, err := newTestSiaDirSetWithDir() 215 if err != nil { 216 t.Fatal(err) 217 } 218 siaPath := entry.SiaPath() 219 exists, err := sds.Exists(siaPath) 220 if !exists { 221 t.Fatal("No SiaDirSetEntry found") 222 } 223 if err != nil { 224 t.Fatal(err) 225 } 226 227 // Confirm metadata is set properly 228 md := entry.metadata 229 if err = checkMetadataInit(md); err != nil { 230 t.Fatal(err) 231 } 232 233 // Update the metadata of the entry 234 checkTime := time.Now() 235 metadataUpdate := md 236 // Aggregate fields 237 metadataUpdate.AggregateHealth = 7 238 metadataUpdate.AggregateLastHealthCheckTime = checkTime 239 metadataUpdate.AggregateMinRedundancy = 2.2 240 metadataUpdate.AggregateModTime = checkTime 241 metadataUpdate.AggregateNumFiles = 11 242 metadataUpdate.AggregateNumStuckChunks = 15 243 metadataUpdate.AggregateNumSubDirs = 5 244 metadataUpdate.AggregateSize = 2432 245 metadataUpdate.AggregateStuckHealth = 5 246 // SiaDir fields 247 metadataUpdate.Health = 4 248 metadataUpdate.LastHealthCheckTime = checkTime 249 metadataUpdate.MinRedundancy = 2 250 metadataUpdate.ModTime = checkTime 251 metadataUpdate.NumFiles = 5 252 metadataUpdate.NumStuckChunks = 6 253 metadataUpdate.NumSubDirs = 4 254 metadataUpdate.Size = 223 255 metadataUpdate.StuckHealth = 2 256 257 err = sds.UpdateMetadata(siaPath, metadataUpdate) 258 if err != nil { 259 t.Fatal(err) 260 } 261 262 // Check if the metadata was updated properly in memory and on disk 263 md = entry.metadata 264 err = equalMetadatas(md, metadataUpdate) 265 if err != nil { 266 t.Fatal(err) 267 } 268 } 269 270 // TestSiaDirRename tests the Rename method of the siadirset. 271 func TestSiaDirRename(t *testing.T) { 272 if testing.Short() { 273 t.SkipNow() 274 } 275 // Prepare a siadirset 276 dir := filepath.Join(os.TempDir(), "siadirs", t.Name()) 277 os.RemoveAll(dir) 278 wal, _ := newTestWAL() 279 sds := NewSiaDirSet(dir, wal) 280 281 // Specify a directory structure for this test. 282 var dirStructure = []string{ 283 "dir1", 284 "dir1/subdir1", 285 "dir1/subdir1/subsubdir1", 286 "dir1/subdir1/subsubdir2", 287 "dir1/subdir1/subsubdir3", 288 "dir1/subdir2", 289 "dir1/subdir2/subsubdir1", 290 "dir1/subdir2/subsubdir2", 291 "dir1/subdir2/subsubdir3", 292 "dir1/subdir3", 293 "dir1/subdir3/subsubdir1", 294 "dir1/subdir3/subsubdir2", 295 "dir1/subdir3/subsubdir3", 296 } 297 // Specify a function that's executed in parallel which continuously saves dirs 298 // to disk. 299 stop := make(chan struct{}) 300 wg := new(sync.WaitGroup) 301 f := func(entry *SiaDirSetEntry) { 302 defer wg.Done() 303 defer entry.Close() 304 for { 305 select { 306 case <-stop: 307 return 308 default: 309 } 310 err := entry.UpdateMetadata(Metadata{}) 311 if err != nil { 312 t.Fatal(err) 313 } 314 time.Sleep(50 * time.Millisecond) 315 } 316 } 317 // Create the structure and spawn a goroutine that keeps saving the structure 318 // to disk for each directory. 319 for _, dir := range dirStructure { 320 sp, err := modules.NewSiaPath(dir) 321 if err != nil { 322 t.Fatal(err) 323 } 324 entry, err := sds.NewSiaDir(sp) 325 if err != nil { 326 t.Fatal(err) 327 } 328 // 50% chance to spawn goroutine. It's not realistic to assume that all dirs 329 // are loaded. 330 if fastrand.Intn(2) == 0 { 331 wg.Add(1) 332 go f(entry) 333 } else { 334 entry.Close() 335 } 336 } 337 // Wait a second for the goroutines to write to disk a few times. 338 time.Sleep(time.Second) 339 // Rename dir1 to dir2. 340 oldPath, err1 := modules.NewSiaPath(dirStructure[0]) 341 newPath, err2 := modules.NewSiaPath("dir2") 342 if err := errors.Compose(err1, err2); err != nil { 343 t.Fatal(err) 344 } 345 if err := sds.Rename(oldPath, newPath); err != nil { 346 t.Fatal(err) 347 } 348 // Wait another second for more writes to disk after renaming the dir before 349 // killing the goroutines. 350 time.Sleep(time.Second) 351 close(stop) 352 wg.Wait() 353 time.Sleep(time.Second) 354 // Make sure we can't open any of the old folders on disk but we can open the 355 // new ones. 356 for _, dir := range dirStructure { 357 oldDir, err1 := modules.NewSiaPath(dir) 358 newDir, err2 := oldDir.Rebase(oldPath, newPath) 359 if err := errors.Compose(err1, err2); err != nil { 360 t.Fatal(err) 361 } 362 // Open entry with old dir. Shouldn't work. 363 _, err := sds.Open(oldDir) 364 if err != ErrUnknownPath { 365 t.Fatal("shouldn't be able to open old path", oldDir.String(), err) 366 } 367 // Open entry with new dir. Should succeed. 368 entry, err := sds.Open(newDir) 369 if err != nil { 370 t.Fatal(err) 371 } 372 defer entry.Close() 373 // Check siapath of entry. 374 if entry.siaPath != newDir { 375 t.Fatalf("entry should have siapath '%v' but was '%v'", newDir, entry.siaPath) 376 } 377 } 378 } 379 380 // TestHealthPercentage checks the values returned from HealthPercentage 381 func TestHealthPercentage(t *testing.T) { 382 var tests = []struct { 383 health float64 384 healthPercentage float64 385 }{ 386 {1.5, 0}, 387 {1.25, 0}, 388 {1.0, 25}, 389 {0.75, 50}, 390 {0.5, 75}, 391 {0.25, 100}, 392 {0, 100}, 393 } 394 for _, test := range tests { 395 hp := HealthPercentage(test.health) 396 if hp != test.healthPercentage { 397 t.Fatalf("Expect %v got %v", test.healthPercentage, hp) 398 } 399 } 400 }