github.com/jrossiter/goscpwrap@v0.0.0-20160212105001-e15fae0c2306/src/goscp/goscp_test.go (about) 1 package goscp 2 3 import ( 4 "bufio" 5 "bytes" 6 "fmt" 7 "io" 8 "io/ioutil" 9 "log" 10 "os" 11 "path/filepath" 12 "reflect" 13 "regexp" 14 "testing" 15 "time" 16 ) 17 18 var ( 19 // Items created during testing 20 created []string 21 ) 22 23 func TestMain(m *testing.M) { 24 setUp() 25 26 code := m.Run() 27 28 tearDown() 29 30 os.Exit(code) 31 } 32 33 func setUp() { 34 err := os.Chdir(os.TempDir()) 35 if err != nil { 36 log.Fatal(err) 37 } 38 39 log.Println("Running tests in:", os.TempDir()) 40 } 41 42 func tearDown() { 43 for _, v := range created { 44 os.Remove(v) 45 } 46 } 47 48 func expectedError(t *testing.T, received, expected interface{}) { 49 t.Errorf("received: %q, expected: %q", received, expected) 50 } 51 52 func TestUpDirectory(t *testing.T) { 53 tests := []struct { 54 Input []string 55 Expected []string 56 }{ 57 { 58 // Up one directory 59 Input: []string{"one", "two", "three"}, 60 Expected: []string{"one", "two"}, 61 }, 62 { 63 // Up one directory 64 Input: []string{"one"}, 65 Expected: []string{}, 66 }, 67 { 68 // Current directory 69 Input: []string{}, 70 Expected: []string{}, 71 }, 72 } 73 74 c := Client{} 75 for _, v := range tests { 76 c.DestinationPath = v.Input 77 c.upDirectory() 78 79 // Check paths match 80 if !reflect.DeepEqual(c.DestinationPath, v.Expected) { 81 expectedError(t, c.DestinationPath, v.Expected) 82 } 83 } 84 } 85 86 func TestParseMessage(t *testing.T) { 87 tests := []struct { 88 Input string 89 Regex *regexp.Regexp 90 Expected map[string]string 91 ExpectedError string 92 }{ 93 { 94 // Create file message 95 Input: "C0644 25 helloworld.txt", 96 Regex: fileCopyRx, 97 Expected: map[string]string{ 98 "": "C0644 25 helloworld.txt", 99 "mode": "0644", 100 "length": "25", 101 "filename": "helloworld.txt", 102 }, 103 }, 104 { 105 // Create directory message 106 Input: "D0755 0 mydir", 107 Regex: dirCopyRx, 108 Expected: map[string]string{ 109 "": "D0755 0 mydir", 110 "mode": "0755", 111 "length": "0", 112 "dirname": "mydir", 113 }, 114 }, 115 { 116 // Timestamp message 117 Input: "T1234567890 0 9876543210 0", 118 Regex: timestampRx, 119 Expected: map[string]string{ 120 "": "T1234567890 0 9876543210 0", 121 "mtime": "1234567890", 122 "atime": "9876543210", 123 }, 124 }, 125 { 126 // Invalid message 127 Input: "Invalid msg", 128 Regex: fileCopyRx, 129 ExpectedError: "Could not parse protocol message: Invalid msg", 130 }, 131 } 132 133 c := Client{} 134 for _, v := range tests { 135 output, err := c.parseMessage(v.Input, v.Regex) 136 if err != nil { 137 if err.Error() != v.ExpectedError { 138 expectedError(t, err, v.ExpectedError) 139 } 140 continue 141 } 142 143 // Check parts match 144 if !reflect.DeepEqual(output, v.Expected) { 145 expectedError(t, output, v.Expected) 146 } 147 } 148 } 149 150 func TestDirectory(t *testing.T) { 151 uts := time.Now().Unix() 152 dirName := fmt.Sprintf("%s-%v", "goscp-mydir", uts) 153 154 tests := []struct { 155 StartPath string 156 InputPath string 157 ExpectedPath string 158 ExpectedDestinationPath []string 159 }{ 160 { 161 // Directory message 162 StartPath: ".", 163 InputPath: fmt.Sprintf("D0755 0 %s", dirName), 164 ExpectedPath: dirName, 165 ExpectedDestinationPath: []string{".", dirName}, 166 }, 167 } 168 169 for _, v := range tests { 170 c := Client{} 171 c.SetDestinationPath(v.StartPath) 172 c.directory(v.InputPath) 173 174 // Check dir was created 175 path := filepath.Join(c.DestinationPath...) 176 if _, err := os.Stat(path); os.IsNotExist(err) { 177 expectedError(t, err, path) 178 continue 179 } 180 created = append(created, path) 181 182 // Check destination paths match 183 if !reflect.DeepEqual(c.DestinationPath, v.ExpectedDestinationPath) { 184 expectedError(t, c.DestinationPath, v.ExpectedDestinationPath) 185 } 186 } 187 } 188 189 func TestFile(t *testing.T) { 190 uts := time.Now().Unix() 191 fileName := fmt.Sprintf("%s-%v", "goscp-test-file", uts) 192 fileContent := "hello world" 193 194 tests := []struct { 195 StartPath string 196 InputPath string 197 FileContent string 198 ExpectedPath string 199 }{ 200 { 201 // File message 202 StartPath: ".", 203 InputPath: fmt.Sprintf("C0755 %d %s", len(fileContent), fileName), 204 FileContent: fileContent, 205 ExpectedPath: fileName, 206 }, 207 { 208 // Empty file message 209 StartPath: ".", 210 InputPath: fmt.Sprintf("C0755 %d %s", 0, fileName), 211 FileContent: "", 212 ExpectedPath: fileName, 213 }, 214 } 215 216 for _, v := range tests { 217 c := Client{} 218 c.SetDestinationPath(v.StartPath) 219 220 dummy := bytes.NewBuffer([]byte(v.FileContent)) 221 rdr := &readCanceller{Reader: bufio.NewReader(dummy)} 222 c.scpStdoutPipe = rdr 223 224 c.file(v.InputPath) 225 226 // Check file was created 227 if _, err := os.Stat(v.ExpectedPath); os.IsNotExist(err) { 228 expectedError(t, err, v.ExpectedPath) 229 continue 230 } 231 232 // Check file content 233 bytes, _ := ioutil.ReadFile(v.ExpectedPath) 234 if string(bytes) != v.FileContent { 235 expectedError(t, string(bytes), v.FileContent) 236 } 237 238 os.Remove(v.ExpectedPath) 239 } 240 } 241 242 func TestHandleItem(t *testing.T) { 243 tests := []struct { 244 Type string 245 Name string 246 Content []byte 247 ExpectedMessages []string 248 DestinationPath []string 249 ExpectedDestinationPath []string 250 }{ 251 { 252 // File creation 253 Type: "file", 254 Name: "goscp-test-content.txt", 255 Content: []byte("hello-world test text\n"), 256 ExpectedMessages: []string{ 257 "C0644 22 goscp-test-content.txt\n", 258 "hello-world test text\n", 259 "\x00\n", 260 }, 261 }, 262 { 263 // Empty file creation 264 Type: "file", 265 Name: "goscp-test-content.txt", 266 Content: []byte(""), 267 ExpectedMessages: []string{ 268 "C0644 0 goscp-test-content.txt\n", 269 "\x00\n", 270 }, 271 }, 272 273 { 274 // Directory creation and traversing upward 275 Type: "directory", 276 Name: "goscp-test-dir/two", 277 ExpectedMessages: []string{ 278 "E\n", 279 "E\n", 280 "D0644 0 two\n", 281 }, 282 DestinationPath: []string{"goscp-test-dir", "hello", "one"}, 283 ExpectedDestinationPath: []string{"goscp-test-dir/two"}, 284 }, 285 { 286 // Directory creation in same level 287 Type: "directory", 288 Name: "goscp-test-dir/one", 289 ExpectedMessages: []string{ 290 "E\n", 291 "D0644 0 one\n", 292 }, 293 DestinationPath: []string{"goscp-test-dir", "two"}, 294 ExpectedDestinationPath: []string{"goscp-test-dir/one"}, 295 }, 296 { 297 // Directory creation in traversing downward 298 Type: "directory", 299 Name: "goscp-test-dir/one/two", 300 ExpectedMessages: []string{ 301 "D0644 0 two\n", 302 }, 303 DestinationPath: []string{"goscp-test-dir", "one"}, 304 ExpectedDestinationPath: []string{"goscp-test-dir/one/two"}, 305 }, 306 { 307 // Directory creation 308 Type: "directory", 309 Name: "goscp-test-dir", 310 ExpectedMessages: []string{ 311 "E\n", 312 "D0644 0 goscp-test-dir\n", 313 }, 314 DestinationPath: []string{"."}, 315 ExpectedDestinationPath: []string{"goscp-test-dir"}, 316 }, 317 } 318 319 for _, v := range tests { 320 r, w := io.Pipe() 321 c := Client{ 322 scpStdinPipe: w, 323 ShowProgressBar: false, 324 } 325 326 filePath := v.Name 327 var stats os.FileInfo 328 if v.Type == "file" { 329 f, err := os.Create(filePath) 330 if err != nil { 331 t.Error("Unexpected error:", err) 332 } 333 334 f.Write(v.Content) 335 f.Close() 336 } else if v.Type == "directory" { 337 err := os.MkdirAll(filePath, 0755) 338 if err != nil { 339 t.Error("Unexpected error:", err) 340 } 341 342 c.DestinationPath = v.DestinationPath 343 } 344 345 created = append(created, filePath) 346 stats, _ = os.Stat(filePath) 347 348 go func() { 349 br := bufio.NewReader(r) 350 351 msgCounter := 0 352 for { 353 msg, err := br.ReadString('\n') 354 if err != nil { 355 t.Error("Unexpected error:", err) 356 break 357 } 358 359 if msg != v.ExpectedMessages[msgCounter] { 360 expectedError(t, msg, v.ExpectedMessages[msgCounter]) 361 } 362 363 msgCounter++ 364 } 365 }() 366 367 err := c.handleItem(filePath, stats, nil) 368 if err != nil { 369 t.Error("Unexpected error:", err) 370 } 371 372 if v.Type == "file" { 373 // Output one more newline for convenience in reading from the pipe 374 fmt.Fprintf(c.scpStdinPipe, "\n") 375 } else if v.Type == "directory" { 376 if !reflect.DeepEqual(c.DestinationPath, v.ExpectedDestinationPath) { 377 expectedError(t, c.DestinationPath, v.ExpectedDestinationPath) 378 } 379 } 380 381 os.Remove(filePath) 382 } 383 } 384 385 func TestCancel(t *testing.T) { 386 // Send creation message 387 // Cancel 388 // Send another creation message 389 testsMessages := []string{ 390 "C0644 15 goscp-cancel.txt", 391 "Cancel incoming\x00", 392 "C0644 15 goscp-cancel.txt", 393 "Transfer cancelled", 394 "io: read/write on closed pipe", 395 } 396 397 r, w := io.Pipe() 398 c := Client{ 399 scpStdinPipe: w, 400 ShowProgressBar: false, 401 } 402 403 filePath := "goscp-cancel.txt" 404 f, err := os.Create(filePath) 405 if err != nil { 406 t.Error("Unexpected error:", err) 407 } 408 409 f.Write([]byte("Cancel incoming")) 410 f.Close() 411 412 created = append(created, filePath) 413 stats, _ := os.Stat(filePath) 414 msgCounter := 0 415 416 go func() { 417 c.scpStdoutPipe = &readCanceller{ 418 Reader: bufio.NewReader(r), 419 cancel: make(chan struct{}, 1), 420 } 421 422 scanner := bufio.NewScanner(c.scpStdoutPipe) 423 424 for scanner.Scan() { 425 txt := scanner.Text() 426 427 if txt != testsMessages[msgCounter] { 428 expectedError(t, txt, testsMessages[msgCounter]) 429 } 430 msgCounter++ 431 } 432 433 err := scanner.Err() 434 if err != nil { 435 if err.Error() != testsMessages[msgCounter] { 436 expectedError(t, err.Error(), testsMessages[msgCounter]) 437 } 438 msgCounter++ 439 } 440 c.scpStdinPipe.Close() 441 }() 442 443 err = c.handleItem(filePath, stats, nil) 444 if err != nil { 445 t.Error("Unexpected error:", err) 446 } 447 448 // Output one more newline for convenience in reading from the pipe 449 fmt.Fprintf(c.scpStdinPipe, "\n") 450 451 go c.Cancel() 452 453 time.Sleep(time.Millisecond * 100) 454 455 err = c.handleItem(filePath, stats, nil) 456 if err != nil { 457 if err.Error() != testsMessages[msgCounter] { 458 expectedError(t, err.Error(), testsMessages[msgCounter]) 459 } 460 msgCounter++ 461 } 462 463 // Output one more newline for convenience in reading from the pipe 464 fmt.Fprintf(c.scpStdinPipe, "\n") 465 }