github.com/johnathanhowell/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  }