gitlab.com/SiaPrime/SiaPrime@v1.4.1/modules/renter/directoryheap_test.go (about)

     1  package renter
     2  
     3  import (
     4  	"fmt"
     5  	"testing"
     6  
     7  	"gitlab.com/SiaPrime/SiaPrime/modules"
     8  	"gitlab.com/SiaPrime/SiaPrime/siatest/dependencies"
     9  )
    10  
    11  // updateSiaDirHealth is a helper method to update the health and the aggregate
    12  // health of a siadir
    13  func (r *Renter) updateSiaDirHealth(siaPath modules.SiaPath, health, aggregateHealth float64) error {
    14  	siaDir, err := r.staticDirSet.Open(siaPath)
    15  	if err != nil {
    16  		return err
    17  	}
    18  	defer siaDir.Close()
    19  	metadata := siaDir.Metadata()
    20  	metadata.Health = health
    21  	metadata.AggregateHealth = aggregateHealth
    22  	err = siaDir.UpdateMetadata(metadata)
    23  	if err != nil {
    24  		return err
    25  	}
    26  	return nil
    27  }
    28  
    29  // TestDirectoryHeap probes the directory heap implementation
    30  func TestDirectoryHeap(t *testing.T) {
    31  	if testing.Short() {
    32  		t.SkipNow()
    33  	}
    34  	t.Parallel()
    35  
    36  	// Create renter
    37  	rt, err := newRenterTesterWithDependency(t.Name(), &dependencies.DependencyDisableRepairAndHealthLoops{})
    38  	if err != nil {
    39  		t.Fatal(err)
    40  	}
    41  	defer rt.Close()
    42  
    43  	// Add directories to heap. Using these settings ensures that neither the
    44  	// first of the last element added remains at the top of the health. The
    45  	// heap should look like the following:
    46  	//
    47  	// &{5 1 false {5} {0 0}}
    48  	// &{2 4 true {2} {0 0}}
    49  	// &{3 3 false {3} {0 0}}
    50  	// &{4 2 true {4} {0 0}}
    51  	// &{1 5 false {1} {0 0}}
    52  	// &{6 0 true {6} {0 0}}
    53  
    54  	// Reset the directory heap because the root directory is added at init.
    55  	rt.renter.directoryHeap.managedReset()
    56  	heapLen := 6
    57  	for i := 1; i <= heapLen; i++ {
    58  		siaPath, err := modules.NewSiaPath(fmt.Sprint(i))
    59  		if err != nil {
    60  			t.Fatal(err)
    61  		}
    62  		d := &directory{
    63  			aggregateHealth: float64(i),
    64  			health:          float64(heapLen - i),
    65  			explored:        i%2 == 0,
    66  			siaPath:         siaPath,
    67  		}
    68  		rt.renter.directoryHeap.managedPush(d)
    69  	}
    70  
    71  	// Confirm all elements added.
    72  	if rt.renter.directoryHeap.managedLen() != heapLen {
    73  		t.Fatalf("heap should have length of %v but was %v", heapLen, rt.renter.directoryHeap.managedLen())
    74  	}
    75  
    76  	// Check health of heap
    77  	if rt.renter.directoryHeap.managedPeekHealth() != float64(5) {
    78  		t.Fatalf("Expected health of heap to be the value of the aggregate health of top chunk %v, got %v", 5, rt.renter.directoryHeap.managedPeekHealth())
    79  	}
    80  
    81  	// Pop off top element and check against expected values
    82  	d := rt.renter.directoryHeap.managedPop()
    83  	if d.health != float64(1) {
    84  		t.Fatal("Expected Health of 1, got", d.health)
    85  	}
    86  	if d.aggregateHealth != float64(5) {
    87  		t.Fatal("Expected AggregateHealth of 5, got", d.aggregateHealth)
    88  	}
    89  	if d.explored {
    90  		t.Fatal("Expected the directory to be unexplored")
    91  	}
    92  
    93  	// Check health of heap
    94  	if rt.renter.directoryHeap.managedPeekHealth() != float64(4) {
    95  		t.Fatalf("Expected health of heap to be the value of the health of top chunk %v, got %v", 4, rt.renter.directoryHeap.managedPeekHealth())
    96  	}
    97  
    98  	// Push directory back on.
    99  	rt.renter.directoryHeap.managedPush(d)
   100  	// A second push will be an update, which should change nothing, and
   101  	// therefore not impact the outcome of the test.
   102  	rt.renter.directoryHeap.managedPush(d)
   103  
   104  	// Now update directory and confirm it is not the top directory and the top
   105  	// element is as expected
   106  	d.aggregateHealth = 0
   107  	d.health = 0
   108  	d.explored = true
   109  	rt.renter.directoryHeap.managedPush(d)
   110  	topDir := rt.renter.directoryHeap.managedPop()
   111  	if topDir.health != float64(4) {
   112  		t.Fatal("Expected Health of 4, got", topDir.health)
   113  	}
   114  	if topDir.aggregateHealth != float64(2) {
   115  		t.Fatal("Expected AggregateHealth of 2, got", topDir.aggregateHealth)
   116  	}
   117  	if !topDir.explored {
   118  		t.Fatal("Expected the directory to be explored")
   119  	}
   120  	// Find Directory in heap and confirm that it was updated
   121  	found := false
   122  	for rt.renter.directoryHeap.managedLen() > 0 {
   123  		topDir = rt.renter.directoryHeap.managedPop()
   124  		if !topDir.siaPath.Equals(d.siaPath) {
   125  			continue
   126  		}
   127  		if found {
   128  			t.Fatal("Duplicate directory in heap")
   129  		}
   130  		found = true
   131  		if topDir.health != d.health {
   132  			t.Fatalf("Expected Health of %v, got %v", d.health, topDir.health)
   133  		}
   134  		if topDir.aggregateHealth != d.aggregateHealth {
   135  			t.Fatalf("Expected AggregateHealth of %v, got %v", d.aggregateHealth, topDir.aggregateHealth)
   136  		}
   137  		if !topDir.explored {
   138  			t.Fatal("Expected the directory to be explored")
   139  		}
   140  	}
   141  
   142  	// Reset Directory heap
   143  	rt.renter.directoryHeap.managedReset()
   144  
   145  	// Confirm that the heap is empty
   146  	if rt.renter.directoryHeap.managedLen() != 0 {
   147  		t.Fatal("heap should empty but has length of", rt.renter.directoryHeap.managedLen())
   148  	}
   149  
   150  	// Test initializing directory heap
   151  	err = rt.renter.managedPushUnexploredDirectory(modules.RootSiaPath())
   152  	if err != nil {
   153  		t.Fatal(err)
   154  	}
   155  	if rt.renter.directoryHeap.managedLen() != 1 {
   156  		t.Fatal("directory heap should have length of 1 but has length of", rt.renter.directoryHeap.managedLen())
   157  	}
   158  	d = rt.renter.directoryHeap.managedPop()
   159  	if d.explored {
   160  		t.Fatal("directory should be unexplored root")
   161  	}
   162  	if !d.siaPath.Equals(modules.RootSiaPath()) {
   163  		t.Fatal("Directory should be root directory but is", d.siaPath)
   164  	}
   165  }
   166  
   167  // TestPushSubDirectories probes the methods that add sub directories to the
   168  // heap. This in turn tests the methods for adding unexplored directories
   169  func TestPushSubDirectories(t *testing.T) {
   170  	if testing.Short() {
   171  		t.SkipNow()
   172  	}
   173  	t.Parallel()
   174  
   175  	// Create renter
   176  	rt, err := newRenterTesterWithDependency(t.Name(), &dependencies.DependencyDisableRepairAndHealthLoops{})
   177  	if err != nil {
   178  		t.Fatal(err)
   179  	}
   180  	defer rt.Close()
   181  
   182  	// Create a test directory with the following healths
   183  	//
   184  	// root/ 1
   185  	// root/SubDir1/ 2
   186  	// root/SubDir2/ 3
   187  
   188  	// Create directory tree
   189  	siaPath1, err := modules.NewSiaPath("SubDir1")
   190  	if err != nil {
   191  		t.Fatal(err)
   192  	}
   193  	siaPath2, err := modules.NewSiaPath("SubDir2")
   194  	if err != nil {
   195  		t.Fatal(err)
   196  	}
   197  	if err := rt.renter.CreateDir(siaPath1); err != nil {
   198  		t.Fatal(err)
   199  	}
   200  	if err := rt.renter.CreateDir(siaPath2); err != nil {
   201  		t.Fatal(err)
   202  	}
   203  
   204  	// Update metadata
   205  	err = rt.renter.updateSiaDirHealth(siaPath1, float64(2), float64(2))
   206  	if err != nil {
   207  		t.Fatal(err)
   208  	}
   209  
   210  	err = rt.renter.updateSiaDirHealth(siaPath2, float64(3), float64(3))
   211  	if err != nil {
   212  		t.Fatal(err)
   213  	}
   214  
   215  	// Make sure we are starting with an empty heap
   216  	rt.renter.directoryHeap.managedReset()
   217  
   218  	// Add root sub directories
   219  	d := &directory{
   220  		siaPath: modules.RootSiaPath(),
   221  	}
   222  	err = rt.renter.managedPushSubDirectories(d)
   223  	if err != nil {
   224  		t.Fatal(err)
   225  	}
   226  
   227  	// Heap should have a length of 2
   228  	if rt.renter.directoryHeap.managedLen() != 2 {
   229  		t.Fatal("Heap should have length of 2 but was", rt.renter.directoryHeap.managedLen())
   230  	}
   231  
   232  	// Pop off elements and confirm the are correct
   233  	d = rt.renter.directoryHeap.managedPop()
   234  	if !d.siaPath.Equals(siaPath2) {
   235  		t.Fatalf("Expected directory %v but found %v", siaPath2.String(), d.siaPath.String())
   236  	}
   237  	if d.aggregateHealth != float64(3) {
   238  		t.Fatal("Expected AggregateHealth to be 3 but was", d.aggregateHealth)
   239  	}
   240  	if d.health != float64(3) {
   241  		t.Fatal("Expected Health to be 3 but was", d.health)
   242  	}
   243  	if d.explored {
   244  		t.Fatal("Expected directory to be unexplored")
   245  	}
   246  	d = rt.renter.directoryHeap.managedPop()
   247  	if !d.siaPath.Equals(siaPath1) {
   248  		t.Fatalf("Expected directory %v but found %v", siaPath1.String(), d.siaPath.String())
   249  	}
   250  	if d.aggregateHealth != float64(2) {
   251  		t.Fatal("Expected AggregateHealth to be 2 but was", d.aggregateHealth)
   252  	}
   253  	if d.health != float64(2) {
   254  		t.Fatal("Expected Health to be 2 but was", d.health)
   255  	}
   256  	if d.explored {
   257  		t.Fatal("Expected directory to be unexplored")
   258  	}
   259  }
   260  
   261  // TestNextExploredDirectory probes managedNextExploredDirectory to ensure that
   262  // the directory traverses the filesystem as expected
   263  func TestNextExploredDirectory(t *testing.T) {
   264  	if testing.Short() {
   265  		t.SkipNow()
   266  	}
   267  	t.Parallel()
   268  
   269  	// Create renter
   270  	rt, err := newRenterTesterWithDependency(t.Name(), &dependencies.DependencyDisableRepairAndHealthLoops{})
   271  	if err != nil {
   272  		t.Fatal(err)
   273  	}
   274  	defer rt.Close()
   275  
   276  	// Create a test directory with the following healths/aggregateHealths
   277  	//
   278  	// root/ 0/3
   279  	// root/SubDir1/ 1/2
   280  	// root/SubDir1/SubDir1/ 1/1
   281  	// root/SubDir1/SubDir2/ 2/2
   282  	// root/SubDir2/ 1/3
   283  	// root/SubDir2/SubDir1/ 1/1
   284  	// root/SubDir2/SubDir2/ 3/3
   285  	//
   286  	// Overall we would expect to see root/SubDir2/SubDir2 popped first followed
   287  	// by root/SubDir1/SubDir2
   288  
   289  	// Create SiaPaths
   290  	siaPath1, err := modules.NewSiaPath("SubDir1")
   291  	if err != nil {
   292  		t.Fatal(err)
   293  	}
   294  	siaPath2, err := modules.NewSiaPath("SubDir2")
   295  	if err != nil {
   296  		t.Fatal(err)
   297  	}
   298  	siaPath1_1, err := siaPath1.Join("SubDir1")
   299  	if err != nil {
   300  		t.Fatal(err)
   301  	}
   302  	siaPath1_2, err := siaPath1.Join("SubDir2")
   303  	if err != nil {
   304  		t.Fatal(err)
   305  	}
   306  	siaPath2_1, err := siaPath2.Join("SubDir1")
   307  	if err != nil {
   308  		t.Fatal(err)
   309  	}
   310  	siaPath2_2, err := siaPath2.Join("SubDir2")
   311  	if err != nil {
   312  		t.Fatal(err)
   313  	}
   314  	// Create Directories
   315  	if err := rt.renter.CreateDir(siaPath1_1); err != nil {
   316  		t.Fatal(err)
   317  	}
   318  	if err := rt.renter.CreateDir(siaPath1_2); err != nil {
   319  		t.Fatal(err)
   320  	}
   321  	if err := rt.renter.CreateDir(siaPath2_1); err != nil {
   322  		t.Fatal(err)
   323  	}
   324  	if err := rt.renter.CreateDir(siaPath2_2); err != nil {
   325  		t.Fatal(err)
   326  	}
   327  
   328  	// Update metadata
   329  	err = rt.renter.updateSiaDirHealth(modules.RootSiaPath(), float64(0), float64(3))
   330  	if err != nil {
   331  		t.Fatal(err)
   332  	}
   333  	err = rt.renter.updateSiaDirHealth(siaPath1, float64(1), float64(2))
   334  	if err != nil {
   335  		t.Fatal(err)
   336  	}
   337  	err = rt.renter.updateSiaDirHealth(siaPath1_1, float64(1), float64(1))
   338  	if err != nil {
   339  		t.Fatal(err)
   340  	}
   341  	err = rt.renter.updateSiaDirHealth(siaPath1_2, float64(2), float64(2))
   342  	if err != nil {
   343  		t.Fatal(err)
   344  	}
   345  	err = rt.renter.updateSiaDirHealth(siaPath2, float64(1), float64(3))
   346  	if err != nil {
   347  		t.Fatal(err)
   348  	}
   349  	err = rt.renter.updateSiaDirHealth(siaPath2_1, float64(1), float64(1))
   350  	if err != nil {
   351  		t.Fatal(err)
   352  	}
   353  	err = rt.renter.updateSiaDirHealth(siaPath2_2, float64(3), float64(3))
   354  	if err != nil {
   355  		t.Fatal(err)
   356  	}
   357  
   358  	// Make sure we are starting with an empty heap, this helps with ndfs and
   359  	// tests proper handling of empty heaps
   360  	rt.renter.directoryHeap.managedReset()
   361  	err = rt.renter.managedPushUnexploredDirectory(modules.RootSiaPath())
   362  	if err != nil {
   363  		t.Fatal(err)
   364  	}
   365  
   366  	// Pop off next explored directory
   367  	d, err := rt.renter.managedNextExploredDirectory()
   368  	if err != nil {
   369  		t.Fatal(err)
   370  	}
   371  	if d == nil {
   372  		t.Fatal("No directory popped off heap")
   373  	}
   374  
   375  	// Directory should be root/SubDir2/SubDir2
   376  	if !d.siaPath.Equals(siaPath2_2) {
   377  		t.Fatalf("Expected directory %v but found %v", siaPath2_2.String(), d.siaPath.String())
   378  	}
   379  	if d.aggregateHealth != float64(3) {
   380  		t.Fatal("Expected AggregateHealth to be 3 but was", d.aggregateHealth)
   381  	}
   382  	if d.health != float64(3) {
   383  		t.Fatal("Expected Health to be 3 but was", d.health)
   384  	}
   385  	if !d.explored {
   386  		t.Fatal("Expected directory to be explored")
   387  	}
   388  
   389  	// Pop off next explored directory
   390  	d, err = rt.renter.managedNextExploredDirectory()
   391  	if err != nil {
   392  		t.Fatal(err)
   393  	}
   394  	if d == nil {
   395  		t.Fatal("No directory popped off heap")
   396  	}
   397  
   398  	// Directory should be root/SubDir1/SubDir2
   399  	if !d.siaPath.Equals(siaPath1_2) {
   400  		t.Fatalf("Expected directory %v but found %v", siaPath1_2.String(), d.siaPath.String())
   401  	}
   402  	if d.aggregateHealth != float64(2) {
   403  		t.Fatal("Expected AggregateHealth to be 2 but was", d.aggregateHealth)
   404  	}
   405  	if d.health != float64(2) {
   406  		t.Fatal("Expected Health to be 2 but was", d.health)
   407  	}
   408  	if !d.explored {
   409  		t.Fatal("Expected directory to be explored")
   410  	}
   411  }