github.com/avahowell/sia@v0.5.1-beta.0.20160524050156-83dcc3d37c94/modules/renter/files_test.go (about)

     1  package renter
     2  
     3  import (
     4  	"os"
     5  	"path/filepath"
     6  	"testing"
     7  
     8  	"github.com/NebulousLabs/Sia/types"
     9  )
    10  
    11  // TestFileNumChunks checks the numChunks method of the file type.
    12  func TestFileNumChunks(t *testing.T) {
    13  	tests := []struct {
    14  		size           uint64
    15  		pieceSize      uint64
    16  		piecesPerChunk int
    17  		expNumChunks   uint64
    18  	}{
    19  		{100, 10, 1, 10}, // evenly divides
    20  		{100, 10, 2, 5},  // evenly divides
    21  
    22  		{101, 10, 1, 11}, // padded
    23  		{101, 10, 2, 6},  // padded
    24  
    25  		{10, 100, 1, 1}, // larger piece than file
    26  		{0, 10, 1, 1},   // 0-length
    27  	}
    28  
    29  	for _, test := range tests {
    30  		rsc, _ := NewRSCode(test.piecesPerChunk, 1) // can't use 0
    31  		f := &file{size: test.size, erasureCode: rsc, pieceSize: test.pieceSize}
    32  		if f.numChunks() != test.expNumChunks {
    33  			t.Errorf("Test %v: expected %v, got %v", test, test.expNumChunks, f.numChunks())
    34  		}
    35  	}
    36  }
    37  
    38  // TestFileAvailable probes the available method of the file type.
    39  func TestFileAvailable(t *testing.T) {
    40  	rsc, _ := NewRSCode(1, 10)
    41  	f := &file{
    42  		size:        1000,
    43  		erasureCode: rsc,
    44  		pieceSize:   100,
    45  	}
    46  
    47  	if f.available() {
    48  		t.Error("file should not be available")
    49  	}
    50  
    51  	var fc fileContract
    52  	for i := uint64(0); i < f.numChunks(); i++ {
    53  		fc.Pieces = append(fc.Pieces, pieceData{Chunk: i, Piece: 0})
    54  	}
    55  	f.contracts = map[types.FileContractID]fileContract{types.FileContractID{}: fc}
    56  
    57  	if !f.available() {
    58  		t.Error("file should be available")
    59  	}
    60  }
    61  
    62  // TestFileRedundancy tests that redundancy is correctly calculated for files
    63  // with varying number of filecontracts and erasure code settings.
    64  func TestFileRedundancy(t *testing.T) {
    65  	nDatas := []int{1, 2, 10}
    66  	for _, nData := range nDatas {
    67  		rsc, _ := NewRSCode(nData, 10)
    68  		f := &file{
    69  			size:        1000,
    70  			pieceSize:   100,
    71  			contracts:   make(map[types.FileContractID]fileContract),
    72  			erasureCode: rsc,
    73  		}
    74  
    75  		// Test that an empty file has 0 redundancy.
    76  		if r := f.redundancy(); r != 0 {
    77  			t.Error("expected 0 redundancy, got", r)
    78  		}
    79  		// Test that a file with 1 filecontract that has a piece for every chunk but
    80  		// one chunk still has a redundancy of 0.
    81  		fc := fileContract{
    82  			ID: types.FileContractID{0},
    83  		}
    84  		for i := uint64(0); i < f.numChunks()-1; i++ {
    85  			pd := pieceData{
    86  				Chunk: i,
    87  				Piece: 0,
    88  			}
    89  			fc.Pieces = append(fc.Pieces, pd)
    90  		}
    91  		f.contracts[fc.ID] = fc
    92  		if r := f.redundancy(); r != 0 {
    93  			t.Error("expected 0 redundancy, got", r)
    94  		}
    95  		// Test that adding another filecontract with a piece for every chunk but one
    96  		// chunk still results in a file with redundancy 0.
    97  		fc = fileContract{
    98  			ID: types.FileContractID{1},
    99  		}
   100  		for i := uint64(0); i < f.numChunks()-1; i++ {
   101  			pd := pieceData{
   102  				Chunk: i,
   103  				Piece: 1,
   104  			}
   105  			fc.Pieces = append(fc.Pieces, pd)
   106  		}
   107  		f.contracts[fc.ID] = fc
   108  		if r := f.redundancy(); r != 0 {
   109  			t.Error("expected 0 redundancy, got", r)
   110  		}
   111  		// Test that adding a file contract with a piece for the missing chunk
   112  		// results in a file with redundancy > 0 && <= 1.
   113  		fc = fileContract{
   114  			ID: types.FileContractID{2},
   115  		}
   116  		pd := pieceData{
   117  			Chunk: f.numChunks() - 1,
   118  			Piece: 0,
   119  		}
   120  		fc.Pieces = append(fc.Pieces, pd)
   121  		f.contracts[fc.ID] = fc
   122  		// 1.0 / MinPieces because the chunk with the least number of pieces has 1 piece.
   123  		expectedR := 1.0 / float64(f.erasureCode.MinPieces())
   124  		if r := f.redundancy(); r == 0 || r > 1 || r != expectedR {
   125  			t.Errorf("expected %f redundancy, got %f", expectedR, r)
   126  		}
   127  		// Test that adding a file contract that has erasureCode.MinPieces() pieces
   128  		// per chunk for all chunks results in a file with redundancy > 1.
   129  		fc = fileContract{
   130  			ID: types.FileContractID{3},
   131  		}
   132  		for iChunk := uint64(0); iChunk < f.numChunks(); iChunk++ {
   133  			for iPiece := 0; iPiece < f.erasureCode.MinPieces(); iPiece++ {
   134  				pd := pieceData{
   135  					Chunk: iChunk,
   136  					Piece: uint64(iPiece),
   137  				}
   138  				fc.Pieces = append(fc.Pieces, pd)
   139  			}
   140  		}
   141  		f.contracts[fc.ID] = fc
   142  		// 1+MinPieces / MinPieces because the chunk with the least number of pieces has 1+MinPieces pieces.
   143  		expectedR = float64(1+f.erasureCode.MinPieces()) / float64(f.erasureCode.MinPieces())
   144  		if r := f.redundancy(); r <= 1 || r != expectedR {
   145  			t.Errorf("expected a redundancy >1 and equal to %f, got %f", expectedR, r)
   146  		}
   147  	}
   148  }
   149  
   150  // TestFileExpiration probes the expiration method of the file type.
   151  func TestFileExpiration(t *testing.T) {
   152  	f := &file{
   153  		contracts: make(map[types.FileContractID]fileContract),
   154  	}
   155  
   156  	if f.expiration() != 0 {
   157  		t.Error("file with no pieces should report as having no time remaining")
   158  	}
   159  
   160  	// Add a contract.
   161  	fc := fileContract{}
   162  	fc.WindowStart = 100
   163  	f.contracts[types.FileContractID{0}] = fc
   164  	if f.expiration() != 100 {
   165  		t.Error("file did not report lowest WindowStart")
   166  	}
   167  
   168  	// Add a contract with a lower WindowStart.
   169  	fc.WindowStart = 50
   170  	f.contracts[types.FileContractID{1}] = fc
   171  	if f.expiration() != 50 {
   172  		t.Error("file did not report lowest WindowStart")
   173  	}
   174  
   175  	// Add a contract with a higher WindowStart.
   176  	fc.WindowStart = 75
   177  	f.contracts[types.FileContractID{2}] = fc
   178  	if f.expiration() != 50 {
   179  		t.Error("file did not report lowest WindowStart")
   180  	}
   181  }
   182  
   183  // TestRenterDeleteFile probes the DeleteFile method of the renter type.
   184  func TestRenterDeleteFile(t *testing.T) {
   185  	if testing.Short() {
   186  		t.SkipNow()
   187  	}
   188  	rt, err := newRenterTester("TestRenterDeleteFile")
   189  	if err != nil {
   190  		t.Fatal(err)
   191  	}
   192  	defer rt.Close()
   193  
   194  	// Delete a file from an empty renter.
   195  	err = rt.renter.DeleteFile("dne")
   196  	if err != ErrUnknownPath {
   197  		t.Error("Expected ErrUnknownPath:", err)
   198  	}
   199  
   200  	// Put a file in the renter.
   201  	rt.renter.files["1"] = &file{
   202  		name: "one",
   203  	}
   204  	// Delete a different file.
   205  	err = rt.renter.DeleteFile("one")
   206  	if err != ErrUnknownPath {
   207  		t.Error("Expected ErrUnknownPath, got", err)
   208  	}
   209  	// Delete the file.
   210  	err = rt.renter.DeleteFile("1")
   211  	if err != nil {
   212  		t.Error(err)
   213  	}
   214  	if len(rt.renter.FileList()) != 0 {
   215  		t.Error("file was deleted, but is still reported in FileList")
   216  	}
   217  
   218  	// Put a file in the renter, then rename it.
   219  	f := newTestingFile()
   220  	f.name = "1"
   221  	rt.renter.files[f.name] = f
   222  	rt.renter.RenameFile(f.name, "one")
   223  	// Call delete on the previous name.
   224  	err = rt.renter.DeleteFile("1")
   225  	if err != ErrUnknownPath {
   226  		t.Error("Expected ErrUnknownPath, got", err)
   227  	}
   228  	// Call delete on the new name.
   229  	err = rt.renter.DeleteFile("one")
   230  	if err != nil {
   231  		t.Error(err)
   232  	}
   233  
   234  	// Check that all .sia files have been deleted.
   235  	var walkStr string
   236  	filepath.Walk(rt.renter.persistDir, func(path string, _ os.FileInfo, _ error) error {
   237  		// capture only .sia files
   238  		if filepath.Ext(path) == ".sia" {
   239  			rel, _ := filepath.Rel(rt.renter.persistDir, path) // strip testdir prefix
   240  			walkStr += rel
   241  		}
   242  		return nil
   243  	})
   244  	expWalkStr := ""
   245  	if walkStr != expWalkStr {
   246  		t.Fatalf("Bad walk string: expected %q, got %q", expWalkStr, walkStr)
   247  	}
   248  }
   249  
   250  // TestRenterFileList probes the FileList method of the renter type.
   251  func TestRenterFileList(t *testing.T) {
   252  	if testing.Short() {
   253  		t.SkipNow()
   254  	}
   255  	rt, err := newRenterTester("TestRenterFileList")
   256  	if err != nil {
   257  		t.Fatal(err)
   258  	}
   259  	defer rt.Close()
   260  
   261  	// Get the file list of an empty renter.
   262  	if len(rt.renter.FileList()) != 0 {
   263  		t.Error("FileList has non-zero length for empty renter?")
   264  	}
   265  
   266  	// Put a file in the renter.
   267  	rsc, _ := NewRSCode(1, 1)
   268  	rt.renter.files["1"] = &file{
   269  		name:        "one",
   270  		erasureCode: rsc,
   271  		pieceSize:   1,
   272  	}
   273  	if len(rt.renter.FileList()) != 1 {
   274  		t.Error("FileList is not returning the only file in the renter")
   275  	}
   276  	if rt.renter.FileList()[0].SiaPath != "one" {
   277  		t.Error("FileList is not returning the correct filename for the only file")
   278  	}
   279  
   280  	// Put multiple files in the renter.
   281  	rt.renter.files["2"] = &file{
   282  		name:        "two",
   283  		erasureCode: rsc,
   284  		pieceSize:   1,
   285  	}
   286  	if len(rt.renter.FileList()) != 2 {
   287  		t.Error("FileList is not returning both files in the renter")
   288  	}
   289  	files := rt.renter.FileList()
   290  	if !((files[0].SiaPath == "one" || files[0].SiaPath == "two") &&
   291  		(files[1].SiaPath == "one" || files[1].SiaPath == "two") &&
   292  		(files[0].SiaPath != files[1].SiaPath)) {
   293  		t.Error("FileList is returning wrong names for the files:", files[0].SiaPath, files[1].SiaPath)
   294  	}
   295  }
   296  
   297  // TestRenterRenameFile probes the rename method of the renter.
   298  func TestRenterRenameFile(t *testing.T) {
   299  	rt, err := newRenterTester("TestRenterRenameFile")
   300  	if err != nil {
   301  		t.Fatal(err)
   302  	}
   303  	defer rt.Close()
   304  
   305  	// Rename a file that doesn't exist.
   306  	err = rt.renter.RenameFile("1", "1a")
   307  	if err != ErrUnknownPath {
   308  		t.Error("Expecting ErrUnknownPath:", err)
   309  	}
   310  
   311  	// Rename a file that does exist.
   312  	f := newTestingFile()
   313  	f.name = "1"
   314  	rt.renter.files["1"] = f
   315  	err = rt.renter.RenameFile("1", "1a")
   316  	if err != nil {
   317  		t.Fatal(err)
   318  	}
   319  	files := rt.renter.FileList()
   320  	if len(files) != 1 {
   321  		t.Fatal("FileList has unexpected number of files:", len(files))
   322  	}
   323  	if files[0].SiaPath != "1a" {
   324  		t.Errorf("RenameFile failed: expected 1a, got %v", files[0].SiaPath)
   325  	}
   326  
   327  	// Rename a file to an existing name.
   328  	f2 := newTestingFile()
   329  	f2.name = "1"
   330  	rt.renter.files["1"] = f2
   331  	err = rt.renter.RenameFile("1", "1a")
   332  	if err != ErrPathOverload {
   333  		t.Error("Expecting ErrPathOverload, got", err)
   334  	}
   335  
   336  	// Rename a file to the same name.
   337  	err = rt.renter.RenameFile("1", "1")
   338  	if err != ErrPathOverload {
   339  		t.Error("Expecting ErrPathOverload, got", err)
   340  	}
   341  }