test.rs (24237B)
1 /* This Source Code Form is subject to the terms of the Mozilla Public 2 * License, v. 2.0. If a copy of the MPL was not distributed with this 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 5 // Currently the mozdevice API is not safe for multiple requests at the same 6 // time. It is recommended to run each of the unit tests on its own. Also adb 7 // specific tests cannot be run in CI yet. To check those locally, also run 8 // the ignored tests. 9 // 10 // Use the following command to accomplish that: 11 // 12 // $ cargo test -- --ignored --test-threads=1 13 14 use crate::*; 15 16 use std::collections::BTreeSet; 17 use std::panic; 18 use std::path::PathBuf; 19 use tempfile::{tempdir, TempDir}; 20 21 #[test] 22 fn read_length_from_valid_string() { 23 fn test(message: &str) -> Result<usize> { 24 read_length(&mut io::BufReader::new(message.as_bytes())) 25 } 26 27 assert_eq!(test("0000").unwrap(), 0); 28 assert_eq!(test("0001").unwrap(), 1); 29 assert_eq!(test("000F").unwrap(), 15); 30 assert_eq!(test("00FF").unwrap(), 255); 31 assert_eq!(test("0FFF").unwrap(), 4095); 32 assert_eq!(test("FFFF").unwrap(), 65535); 33 34 assert_eq!(test("FFFF0").unwrap(), 65535); 35 } 36 37 #[test] 38 fn read_length_from_invalid_string() { 39 fn test(message: &str) -> Result<usize> { 40 read_length(&mut io::BufReader::new(message.as_bytes())) 41 } 42 43 test("").expect_err("empty string"); 44 test("G").expect_err("invalid hex character"); 45 test("-1").expect_err("negative number"); 46 test("000").expect_err("shorter than 4 bytes"); 47 } 48 49 #[test] 50 fn encode_message_with_valid_string() { 51 assert_eq!(encode_message("").unwrap(), "0000".to_string()); 52 assert_eq!(encode_message("a").unwrap(), "0001a".to_string()); 53 assert_eq!( 54 encode_message(&"a".repeat(15)).unwrap(), 55 format!("000F{}", "a".repeat(15)) 56 ); 57 assert_eq!( 58 encode_message(&"a".repeat(255)).unwrap(), 59 format!("00FF{}", "a".repeat(255)) 60 ); 61 assert_eq!( 62 encode_message(&"a".repeat(4095)).unwrap(), 63 format!("0FFF{}", "a".repeat(4095)) 64 ); 65 assert_eq!( 66 encode_message(&"a".repeat(65535)).unwrap(), 67 format!("FFFF{}", "a".repeat(65535)) 68 ); 69 } 70 71 #[test] 72 fn encode_message_with_invalid_string() { 73 encode_message(&"a".repeat(65536)).expect_err("string lengths exceeds 4 bytes"); 74 } 75 76 fn run_device_test<F>(test: F) 77 where 78 F: FnOnce(&Device, &TempDir, &UnixPath) + panic::UnwindSafe, 79 { 80 let host = Host { 81 ..Default::default() 82 }; 83 let device = host 84 .device_or_default::<String>(None, AndroidStorageInput::Auto) 85 .expect("device_or_default"); 86 87 let tmp_dir = tempdir().expect("create temp dir"); 88 let response = device 89 .execute_host_shell_command("echo $EXTERNAL_STORAGE") 90 .unwrap(); 91 let mut test_root = UnixPathBuf::from(response.trim_end_matches('\n')); 92 test_root.push("mozdevice"); 93 94 let _ = device.remove(&test_root); 95 96 let result = panic::catch_unwind(|| test(&device, &tmp_dir, &test_root)); 97 98 let _ = device.kill_forward_all_ports(); 99 // let _ = device.kill_reverse_all_ports(); 100 101 assert!(result.is_ok()) 102 } 103 104 #[test] 105 #[ignore] 106 fn host_features() { 107 let host = Host { 108 ..Default::default() 109 }; 110 111 let set = host.features::<BTreeSet<_>>().expect("to query features"); 112 assert!(set.contains("cmd")); 113 assert!(set.contains("shell_v2")); 114 } 115 116 #[test] 117 #[ignore] 118 fn host_devices() { 119 let host = Host { 120 ..Default::default() 121 }; 122 123 let set: BTreeSet<_> = host.devices().expect("to query devices"); 124 assert_eq!(1, set.len()); 125 } 126 127 #[test] 128 #[ignore] 129 fn host_device_or_default() { 130 let host = Host { 131 ..Default::default() 132 }; 133 134 let devices: Vec<_> = host.devices().expect("to query devices"); 135 let expected_device = devices.first().expect("found a device"); 136 137 let device = host 138 .device_or_default::<String>(Some(&expected_device.serial), AndroidStorageInput::App) 139 .expect("connected device with serial"); 140 assert_eq!(device.run_as_package, None); 141 assert_eq!(device.serial, expected_device.serial); 142 assert!(device.tempfile.starts_with("/data/local/tmp")); 143 } 144 145 #[test] 146 #[ignore] 147 fn host_device_or_default_invalid_serial() { 148 let host = Host { 149 ..Default::default() 150 }; 151 152 host.device_or_default::<String>(Some(&"foobar".to_owned()), AndroidStorageInput::Auto) 153 .expect_err("invalid serial"); 154 } 155 156 #[test] 157 #[ignore] 158 fn host_device_or_default_no_serial() { 159 let host = Host { 160 ..Default::default() 161 }; 162 163 let devices: Vec<_> = host.devices().expect("to query devices"); 164 let expected_device = devices.first().expect("found a device"); 165 166 let device = host 167 .device_or_default::<String>(None, AndroidStorageInput::Auto) 168 .expect("connected device with serial"); 169 assert_eq!(device.serial, expected_device.serial); 170 } 171 172 #[test] 173 #[ignore] 174 fn host_device_or_default_storage_as_app() { 175 let host = Host { 176 ..Default::default() 177 }; 178 179 let device = host 180 .device_or_default::<String>(None, AndroidStorageInput::App) 181 .expect("connected device"); 182 assert_eq!(device.storage, AndroidStorage::App); 183 } 184 185 #[test] 186 #[ignore] 187 fn host_device_or_default_storage_as_auto() { 188 let host = Host { 189 ..Default::default() 190 }; 191 192 let device = host 193 .device_or_default::<String>(None, AndroidStorageInput::Auto) 194 .expect("connected device"); 195 assert_eq!(device.storage, AndroidStorage::Sdcard); 196 } 197 198 #[test] 199 #[ignore] 200 fn host_device_or_default_storage_as_internal() { 201 let host = Host { 202 ..Default::default() 203 }; 204 205 let device = host 206 .device_or_default::<String>(None, AndroidStorageInput::Internal) 207 .expect("connected device"); 208 assert_eq!(device.storage, AndroidStorage::Internal); 209 } 210 211 #[test] 212 #[ignore] 213 fn host_device_or_default_storage_as_sdcard() { 214 let host = Host { 215 ..Default::default() 216 }; 217 218 let device = host 219 .device_or_default::<String>(None, AndroidStorageInput::Sdcard) 220 .expect("connected device"); 221 assert_eq!(device.storage, AndroidStorage::Sdcard); 222 } 223 224 #[test] 225 #[ignore] 226 fn device_shell_command() { 227 run_device_test(|device: &Device, _: &TempDir, _: &UnixPath| { 228 assert_eq!( 229 "Linux\n", 230 device 231 .execute_host_shell_command("uname") 232 .expect("to have shell output") 233 ); 234 }); 235 } 236 237 #[test] 238 #[ignore] 239 fn device_forward_port_hardcoded() { 240 run_device_test(|device: &Device, _: &TempDir, _: &UnixPath| { 241 assert_eq!( 242 3035, 243 device 244 .forward_port(3035, 3036) 245 .expect("forwarded local port") 246 ); 247 // TODO: check with forward --list 248 }); 249 } 250 251 // #[test] 252 // #[ignore] 253 // TODO: "adb server response to `forward tcp:0 ...` was not a u16: \"000559464\"") 254 // fn device_forward_port_system_allocated() { 255 // run_device_test(|device: &Device, _: &TempDir, _: &UnixPath| { 256 // let local_port = device.forward_port(0, 3037).expect("local_port"); 257 // assert_ne!(local_port, 0); 258 // // TODO: check with forward --list 259 // }); 260 // } 261 262 #[test] 263 #[ignore] 264 fn device_kill_forward_port_no_forwarded_port() { 265 run_device_test(|device: &Device, _: &TempDir, _: &UnixPath| { 266 device 267 .kill_forward_port(3038) 268 .expect_err("adb error: listener 'tcp:3038' "); 269 }); 270 } 271 272 #[test] 273 #[ignore] 274 fn device_kill_forward_port_twice() { 275 run_device_test(|device: &Device, _: &TempDir, _: &UnixPath| { 276 let local_port = device 277 .forward_port(3039, 3040) 278 .expect("forwarded local port"); 279 assert_eq!(local_port, 3039); 280 // TODO: check with forward --list 281 device 282 .kill_forward_port(local_port) 283 .expect("to remove forwarded port"); 284 device 285 .kill_forward_port(local_port) 286 .expect_err("adb error: listener 'tcp:3039' "); 287 }); 288 } 289 290 #[test] 291 #[ignore] 292 fn device_kill_forward_all_ports_no_forwarded_port() { 293 run_device_test(|device: &Device, _: &TempDir, _: &UnixPath| { 294 device 295 .kill_forward_all_ports() 296 .expect("to not fail for no forwarded ports"); 297 }); 298 } 299 300 #[test] 301 #[ignore] 302 fn device_kill_forward_all_ports_twice() { 303 run_device_test(|device: &Device, _: &TempDir, _: &UnixPath| { 304 let local_port1 = device 305 .forward_port(3039, 3040) 306 .expect("forwarded local port"); 307 assert_eq!(local_port1, 3039); 308 let local_port2 = device 309 .forward_port(3041, 3042) 310 .expect("forwarded local port"); 311 assert_eq!(local_port2, 3041); 312 // TODO: check with forward --list 313 device 314 .kill_forward_all_ports() 315 .expect("to remove all forwarded ports"); 316 device 317 .kill_forward_all_ports() 318 .expect("to not fail for no forwarded ports"); 319 }); 320 } 321 322 #[test] 323 #[ignore] 324 fn device_reverse_port_hardcoded() { 325 run_device_test(|device: &Device, _: &TempDir, _: &UnixPath| { 326 assert_eq!(4035, device.reverse_port(4035, 4036).expect("remote_port")); 327 // TODO: check with reverse --list 328 }); 329 } 330 331 // #[test] 332 // #[ignore] 333 // TODO: No adb response: ParseInt(ParseIntError { kind: Empty }) 334 // fn device_reverse_port_system_allocated() { 335 // run_device_test(|device: &Device, _: &TempDir, _: &UnixPath| { 336 // let reverse_port = device.reverse_port(0, 4037).expect("remote port"); 337 // assert_ne!(reverse_port, 0); 338 // // TODO: check with reverse --list 339 // }); 340 // } 341 342 #[test] 343 #[ignore] 344 fn device_kill_reverse_port_no_reverse_port() { 345 run_device_test(|device: &Device, _: &TempDir, _: &UnixPath| { 346 device 347 .kill_reverse_port(4038) 348 .expect_err("listener 'tcp:4038' not found"); 349 }); 350 } 351 352 // #[test] 353 // #[ignore] 354 // TODO: "adb error: adb server response did not contain expected hexstring length: \"\"" 355 // fn device_kill_reverse_port_twice() { 356 // run_device_test(|device: &Device, _: &TempDir, _: &UnixPath| { 357 // let remote_port = device 358 // .reverse_port(4039, 4040) 359 // .expect("reversed local port"); 360 // assert_eq!(remote_port, 4039); 361 // // TODO: check with reverse --list 362 // device 363 // .kill_reverse_port(remote_port) 364 // .expect("to remove reverse port"); 365 // device 366 // .kill_reverse_port(remote_port) 367 // .expect_err("listener 'tcp:4039' not found"); 368 // }); 369 // } 370 371 #[test] 372 #[ignore] 373 fn device_kill_reverse_all_ports_no_reversed_port() { 374 run_device_test(|device: &Device, _: &TempDir, _: &UnixPath| { 375 device 376 .kill_reverse_all_ports() 377 .expect("to not fail for no reversed ports"); 378 }); 379 } 380 381 #[test] 382 #[ignore] 383 fn device_kill_reverse_all_ports_twice() { 384 run_device_test(|device: &Device, _: &TempDir, _: &UnixPath| { 385 let local_port1 = device 386 .forward_port(4039, 4040) 387 .expect("forwarded local port"); 388 assert_eq!(local_port1, 4039); 389 let local_port2 = device 390 .forward_port(4041, 4042) 391 .expect("forwarded local port"); 392 assert_eq!(local_port2, 4041); 393 // TODO: check with reverse --list 394 device 395 .kill_reverse_all_ports() 396 .expect("to remove all reversed ports"); 397 device 398 .kill_reverse_all_ports() 399 .expect("to not fail for no reversed ports"); 400 }); 401 } 402 403 #[test] 404 #[ignore] 405 fn device_push_pull_text_file() { 406 run_device_test( 407 |device: &Device, _: &TempDir, remote_root_path: &UnixPath| { 408 let content = "test"; 409 let remote_path = remote_root_path.join("foo.txt"); 410 411 device 412 .push( 413 &mut io::BufReader::new(content.as_bytes()), 414 &remote_path, 415 0o777, 416 ) 417 .expect("file has been pushed"); 418 419 let file_content = device 420 .execute_host_shell_command(&format!("cat {}", remote_path.display())) 421 .expect("host shell command for 'cat' to succeed"); 422 423 assert_eq!(file_content, content); 424 425 // And as second step pull it off the device. 426 let mut buffer = Vec::new(); 427 device 428 .pull(&remote_path, &mut buffer) 429 .expect("file has been pulled"); 430 assert_eq!(buffer, content.as_bytes()); 431 }, 432 ); 433 } 434 435 #[test] 436 #[ignore] 437 fn device_push_pull_large_binary_file() { 438 run_device_test( 439 |device: &Device, _: &TempDir, remote_root_path: &UnixPath| { 440 let remote_path = remote_root_path.join("foo.binary"); 441 442 let mut content = Vec::new(); 443 444 // Needs to be larger than 64kB to test multiple chunks. 445 for i in 0..100000u32 { 446 content.push('0' as u8 + (i % 10) as u8); 447 } 448 449 device 450 .push( 451 &mut std::io::Cursor::new(content.clone()), 452 &remote_path, 453 0o777, 454 ) 455 .expect("large file has been pushed"); 456 457 let output = device 458 .execute_host_shell_command(&format!("ls -l {}", remote_path.display())) 459 .expect("host shell command for 'ls' to succeed"); 460 461 assert!(output.contains(remote_path.to_str().unwrap())); 462 463 let mut buffer = Vec::new(); 464 465 device 466 .pull(&remote_path, &mut buffer) 467 .expect("large binary file has been pulled"); 468 assert_eq!(buffer, content); 469 }, 470 ); 471 } 472 473 #[test] 474 #[ignore] 475 fn device_push_permission() { 476 run_device_test( 477 |device: &Device, _: &TempDir, remote_root_path: &UnixPath| { 478 fn adjust_mode(mode: u32) -> u32 { 479 // Adjust the mode by copying the user permissions to 480 // group and other as indicated in 481 // [send_impl](https://android.googlesource.com/platform/system/core/+/master/adb/daemon/file_sync_service.cpp#516). 482 // This ensures that group and other can both access a 483 // file if the user can access it. 484 let mut m = mode & 0o777; 485 m |= (m >> 3) & 0o070; 486 m |= (m >> 3) & 0o007; 487 m 488 } 489 490 fn get_permissions(mode: u32) -> String { 491 // Convert the mode integer into the string representation 492 // of the mode returned by `ls`. This assumes the object is 493 // a file and not a directory. 494 let mut perms = vec!["-", "r", "w", "x", "r", "w", "x", "r", "w", "x"]; 495 let mut bit_pos = 0; 496 while bit_pos < 9 { 497 if (1 << bit_pos) & mode == 0 { 498 perms[9 - bit_pos] = "-" 499 } 500 bit_pos += 1; 501 } 502 perms.concat() 503 } 504 let content = "test"; 505 let remote_path = remote_root_path.join("foo.bar"); 506 507 // First push the file to the device 508 let modes = vec![0o421, 0o644, 0o666, 0o777]; 509 for mode in modes { 510 let adjusted_mode = adjust_mode(mode); 511 let adjusted_perms = get_permissions(adjusted_mode); 512 device 513 .push( 514 &mut io::BufReader::new(content.as_bytes()), 515 &remote_path, 516 mode, 517 ) 518 .expect("file has been pushed"); 519 520 let output = device 521 .execute_host_shell_command(&format!("ls -l {}", remote_path.display())) 522 .expect("host shell command for 'ls' to succeed"); 523 524 assert!(output.contains(remote_path.to_str().unwrap())); 525 assert!(output.starts_with(&adjusted_perms)); 526 } 527 528 let output = device 529 .execute_host_shell_command(&format!("ls -ld {}", remote_root_path.display())) 530 .expect("host shell command for 'ls parent' to succeed"); 531 532 assert!(output.contains(remote_root_path.to_str().unwrap())); 533 assert!(output.starts_with("drwxrwxrwx")); 534 }, 535 ); 536 } 537 538 #[test] 539 #[ignore] 540 fn device_pull_fails_for_missing_file() { 541 run_device_test( 542 |device: &Device, _: &TempDir, remote_root_path: &UnixPath| { 543 let mut buffer = Vec::new(); 544 545 device 546 .pull(&remote_root_path.join("missing"), &mut buffer) 547 .expect_err("missing file should not be pulled"); 548 }, 549 ); 550 } 551 552 #[test] 553 #[ignore] 554 fn device_push_and_list_dir() { 555 run_device_test( 556 |device: &Device, tmp_dir: &TempDir, remote_root_path: &UnixPath| { 557 let files = ["foo1.bar", "foo2.bar", "bar/foo3.bar", "bar/more/foo3.bar"]; 558 559 for file in files.iter() { 560 let path = tmp_dir.path().join(Path::new(file)); 561 let _ = std::fs::create_dir_all(path.parent().unwrap()); 562 563 let f = File::create(path).expect("to create file"); 564 let mut f = io::BufWriter::new(f); 565 f.write_all(file.as_bytes()).expect("to write data"); 566 } 567 568 device 569 .push_dir(tmp_dir.path(), &remote_root_path, 0o777) 570 .expect("to push_dir"); 571 572 for file in files.iter() { 573 let path = append_components(remote_root_path, Path::new(file)).unwrap(); 574 let output = device 575 .execute_host_shell_command(&format!("ls {}", path.display())) 576 .expect("host shell command for 'ls' to succeed"); 577 578 assert!(output.contains(path.to_str().unwrap())); 579 } 580 581 let mut listings = device.list_dir(&remote_root_path).expect("to list_dir"); 582 listings.sort(); 583 assert_eq!( 584 listings, 585 vec![ 586 RemoteDirEntry { 587 depth: 0, 588 name: "foo1.bar".to_string(), 589 metadata: RemoteMetadata::RemoteFile(RemoteFileMetadata { 590 mode: 0b110110000, 591 size: 8 592 }) 593 }, 594 RemoteDirEntry { 595 depth: 0, 596 name: "foo2.bar".to_string(), 597 metadata: RemoteMetadata::RemoteFile(RemoteFileMetadata { 598 mode: 0b110110000, 599 size: 8 600 }) 601 }, 602 RemoteDirEntry { 603 depth: 0, 604 name: "bar".to_string(), 605 metadata: RemoteMetadata::RemoteDir 606 }, 607 RemoteDirEntry { 608 depth: 1, 609 name: "bar/foo3.bar".to_string(), 610 metadata: RemoteMetadata::RemoteFile(RemoteFileMetadata { 611 mode: 0b110110000, 612 size: 12 613 }) 614 }, 615 RemoteDirEntry { 616 depth: 1, 617 name: "bar/more".to_string(), 618 metadata: RemoteMetadata::RemoteDir 619 }, 620 RemoteDirEntry { 621 depth: 2, 622 name: "bar/more/foo3.bar".to_string(), 623 metadata: RemoteMetadata::RemoteFile(RemoteFileMetadata { 624 mode: 0b110110000, 625 size: 17 626 }) 627 } 628 ] 629 ); 630 }, 631 ); 632 } 633 634 #[test] 635 #[ignore] 636 fn device_push_and_pull_dir() { 637 run_device_test( 638 |device: &Device, tmp_dir: &TempDir, remote_root_path: &UnixPath| { 639 let files = ["foo1.bar", "foo2.bar", "bar/foo3.bar", "bar/more/foo3.bar"]; 640 641 let src_dir = tmp_dir.path().join(Path::new("src")); 642 let dest_dir = tmp_dir.path().join(Path::new("src")); 643 644 for file in files.iter() { 645 let path = src_dir.join(Path::new(file)); 646 let _ = std::fs::create_dir_all(path.parent().unwrap()); 647 648 let f = File::create(path).expect("to create file"); 649 let mut f = io::BufWriter::new(f); 650 f.write_all(file.as_bytes()).expect("to write data"); 651 } 652 653 device 654 .push_dir(&src_dir, &remote_root_path, 0o777) 655 .expect("to push_dir"); 656 657 device 658 .pull_dir(remote_root_path, &dest_dir) 659 .expect("to pull_dir"); 660 661 for file in files.iter() { 662 let path = dest_dir.join(Path::new(file)); 663 let mut f = File::open(path).expect("to open file"); 664 let mut buf = String::new(); 665 f.read_to_string(&mut buf).expect("to read content"); 666 assert_eq!(buf, *file); 667 } 668 }, 669 ) 670 } 671 672 #[test] 673 #[ignore] 674 fn device_push_and_list_dir_flat() { 675 run_device_test( 676 |device: &Device, tmp_dir: &TempDir, remote_root_path: &UnixPath| { 677 let content = "test"; 678 679 let files = [ 680 PathBuf::from("foo1.bar"), 681 PathBuf::from("foo2.bar"), 682 PathBuf::from("bar").join("foo3.bar"), 683 ]; 684 685 for file in files.iter() { 686 let path = tmp_dir.path().join(&file); 687 let _ = std::fs::create_dir_all(path.parent().unwrap()); 688 689 let f = File::create(path).expect("to create file"); 690 let mut f = io::BufWriter::new(f); 691 f.write_all(content.as_bytes()).expect("to write data"); 692 } 693 694 device 695 .push_dir(tmp_dir.path(), &remote_root_path, 0o777) 696 .expect("to push_dir"); 697 698 for file in files.iter() { 699 let path = append_components(remote_root_path, file).unwrap(); 700 let output = device 701 .execute_host_shell_command(&format!("ls {}", path.display())) 702 .expect("host shell command for 'ls' to succeed"); 703 704 assert!(output.contains(path.to_str().unwrap())); 705 } 706 707 let mut listings = device 708 .list_dir_flat(&remote_root_path, 7, "prefix".to_string()) 709 .expect("to list_dir_flat"); 710 listings.sort(); 711 assert_eq!( 712 listings, 713 vec![ 714 RemoteDirEntry { 715 depth: 7, 716 metadata: RemoteMetadata::RemoteFile(RemoteFileMetadata { 717 mode: 0b110110000, 718 size: 4 719 }), 720 name: "prefix/foo1.bar".to_string(), 721 }, 722 RemoteDirEntry { 723 depth: 7, 724 metadata: RemoteMetadata::RemoteFile(RemoteFileMetadata { 725 mode: 0b110110000, 726 size: 4 727 }), 728 name: "prefix/foo2.bar".to_string(), 729 }, 730 RemoteDirEntry { 731 depth: 7, 732 metadata: RemoteMetadata::RemoteDir, 733 name: "prefix/bar".to_string(), 734 }, 735 ] 736 ); 737 }, 738 ); 739 } 740 741 #[test] 742 fn format_own_device_error_types() { 743 assert_eq!( 744 format!("{}", DeviceError::InvalidStorage), 745 "Invalid storage".to_string() 746 ); 747 assert_eq!( 748 format!("{}", DeviceError::MissingPackage), 749 "Missing package".to_string() 750 ); 751 assert_eq!( 752 format!("{}", DeviceError::MultipleDevices), 753 "Multiple Android devices online".to_string() 754 ); 755 756 assert_eq!( 757 format!("{}", DeviceError::Adb("foo".to_string())), 758 "foo".to_string() 759 ); 760 }