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 }