use mount_fstab::spec::Spec;
use mount_fstab::types::Fstab;
use std::path::PathBuf;
#[test]
fn parse_minimal_entry() {
let input = "UUID=abc / ext4 defaults 0 1\n";
let fstab = Fstab::parse_str(input).unwrap();
assert_eq!(fstab.entries.len(), 1);
let e = &fstab.entries[0];
assert_eq!(e.spec, Spec::Uuid("abc".into()));
assert_eq!(e.file.as_path(), PathBuf::from("/"));
assert_eq!(e.vfstype.as_str(), "ext4");
assert!(e.options.has("defaults"));
assert_eq!(e.freq, 0);
assert_eq!(e.passno, 1);
}
#[test]
fn parse_entry_without_freq_passno() {
let input = "proc /proc proc defaults\n";
let fstab = Fstab::parse_str(input).unwrap();
assert_eq!(fstab.entries.len(), 1);
assert_eq!(fstab.entries[0].freq, 0);
assert_eq!(fstab.entries[0].passno, 0);
}
#[test]
fn parse_entry_without_options() {
let input = "proc /proc proc\n";
let fstab = Fstab::parse_str(input).unwrap();
assert!(fstab.entries[0].options.is_empty());
}
#[test]
fn parse_entry_with_escaped_space() {
let input = r"LABEL=My\040Drive /mnt/data ext4 defaults 0 0
";
let fstab = Fstab::parse_str(input).unwrap();
let e = &fstab.entries[0];
assert_eq!(e.spec, Spec::Label("My Drive".into()));
}
#[test]
fn parse_multiple_entries() {
let input = "\
UUID=root / ext4 defaults 0 1
UUID=boot /boot ext4 defaults 0 2
";
let fstab = Fstab::parse_str(input).unwrap();
assert_eq!(fstab.entries.len(), 2);
}
#[test]
fn parse_with_intro_comment() {
let input = "\
# This is my fstab
# Generated by systemd
UUID=root / ext4 defaults 0 1
";
let fstab = Fstab::parse_str(input).unwrap();
assert!(fstab.intro_comment.is_some());
let intro = fstab.intro_comment.unwrap();
assert!(intro.contains("This is my fstab"));
assert!(intro.contains("Generated by systemd"));
}
#[test]
fn parse_with_per_entry_comment() {
let input = "\
# Root filesystem
UUID=root / ext4 defaults 0 1
# Boot partition
UUID=boot /boot ext4 defaults 0 2
";
let fstab = Fstab::parse_str(input).unwrap();
assert_eq!(fstab.entries.len(), 2);
assert!(fstab.entries[0].comment.is_some());
assert!(
fstab.entries[0]
.comment
.as_ref()
.unwrap()
.contains("Root filesystem")
);
assert!(fstab.entries[1].comment.is_some());
assert!(
fstab.entries[1]
.comment
.as_ref()
.unwrap()
.contains("Boot partition")
);
}
#[test]
fn parse_with_trailing_comment() {
let input = "\
UUID=root / ext4 defaults 0 1
# End of fstab
";
let fstab = Fstab::parse_str(input).unwrap();
assert!(fstab.trailing_comment.is_some());
assert!(fstab.trailing_comment.unwrap().contains("End of fstab"));
}
#[test]
fn parse_blank_line_separates_intro() {
let input = "\
# Intro comment
# This becomes per-entry comment for first entry
UUID=root / ext4 defaults 0 1
";
let fstab = Fstab::parse_str(input).unwrap();
assert!(fstab.intro_comment.is_some());
assert_eq!(fstab.entries.len(), 1);
assert!(fstab.entries[0].comment.is_some());
}
#[test]
fn parse_empty_file() {
let fstab = Fstab::parse_str("").unwrap();
assert!(fstab.entries.is_empty());
}
#[test]
fn parse_comments_only() {
let fstab = Fstab::parse_str("# Just a comment\n# And another\n").unwrap();
assert!(fstab.entries.is_empty());
assert!(fstab.intro_comment.is_some());
}
#[test]
fn parse_carriage_return_stripped() {
let input = "UUID=root / ext4 defaults 0 1\r\n";
let fstab = Fstab::parse_str(input).unwrap();
assert_eq!(fstab.entries.len(), 1);
}
#[test]
fn parse_nfs_entry() {
let input = "server.example.com:/exports /mnt/nfs nfs defaults 0 0\n";
let fstab = Fstab::parse_str(input).unwrap();
let e = &fstab.entries[0];
assert_eq!(
e.spec,
Spec::NetworkMount {
host: "server.example.com".into(),
path: PathBuf::from("/exports"),
}
);
}
#[test]
fn parse_swap_entry() {
let input = "/swap.img none swap sw 0 0\n";
let fstab = Fstab::parse_str(input).unwrap();
let e = &fstab.entries[0];
assert!(e.is_swap());
assert!(e.file.is_swap());
}
#[test]
fn parse_tab_separated_fields() {
let input = "UUID=abc\t/\text4\tdefaults\t0\t1\n";
let fstab = Fstab::parse_str(input).unwrap();
assert_eq!(fstab.entries.len(), 1);
assert_eq!(fstab.entries[0].vfstype.as_str(), "ext4");
}
#[test]
fn parse_mixed_tabs_and_spaces() {
let input = "UUID=abc \t/\t ext4 \t defaults\t0\t1\n";
let fstab = Fstab::parse_str(input).unwrap();
assert_eq!(fstab.entries.len(), 1);
}
#[test]
fn parse_leading_whitespace_on_data_line() {
let input = " UUID=abc / ext4 defaults 0 1\n";
let fstab = Fstab::parse_str(input).unwrap();
assert_eq!(fstab.entries.len(), 1);
}
#[test]
fn parse_three_field_entry() {
let input = "proc /proc proc\n";
let fstab = Fstab::parse_str(input).unwrap();
let e = &fstab.entries[0];
assert!(e.options.is_empty());
assert_eq!(e.freq, 0);
assert_eq!(e.passno, 0);
}
#[test]
fn parse_five_field_entry() {
let input = "UUID=abc / ext4 defaults 1\n"; let fstab = Fstab::parse_str(input).unwrap();
assert_eq!(fstab.entries[0].freq, 1);
assert_eq!(fstab.entries[0].passno, 0);
}
#[test]
fn parse_comment_with_leading_whitespace() {
let input = " # Indented comment\nUUID=abc / ext4 defaults 0 1\n";
let fstab = Fstab::parse_str(input).unwrap();
assert!(fstab.entries[0].comment.is_some());
assert!(
fstab.entries[0]
.comment
.as_ref()
.unwrap()
.contains("Indented comment")
);
}
#[test]
fn parse_blank_lines_between_entries_preserved_as_spacing() {
let input = "UUID=a /mnt1 ext4 defaults 0 0\n\n\nUUID=b /mnt2 ext4 defaults 0 0\n";
let fstab = Fstab::parse_str(input).unwrap();
assert_eq!(fstab.entries.len(), 2);
}
#[test]
fn parse_multiple_comment_lines_before_entry() {
let input =
"# First comment line\n# Second comment line\n# Third\nUUID=abc / ext4 defaults 0 1\n";
let fstab = Fstab::parse_str(input).unwrap();
let comment = fstab.entries[0].comment.as_ref().unwrap();
assert!(comment.contains("First comment line"));
assert!(comment.contains("Second comment line"));
assert!(comment.contains("Third"));
}
#[test]
fn parse_file_without_trailing_newline() {
let input = "UUID=abc / ext4 defaults 0 1"; let fstab = Fstab::parse_str(input).unwrap();
assert_eq!(fstab.entries.len(), 1);
}
#[test]
fn parse_double_escaped_backslash() {
let input = r"LABEL=test\\\\label /mnt ext4 defaults 0 0
";
let fstab = Fstab::parse_str(input).unwrap();
assert_eq!(fstab.entries[0].spec, Spec::Label(r"test\\label".into()));
}
#[test]
fn parse_swap_with_priority_option() {
let input = "/swap.img none swap sw,pri=10 0 0\n";
let fstab = Fstab::parse_str(input).unwrap();
let e = &fstab.entries[0];
assert!(e.is_swap());
assert_eq!(e.options.get("pri"), Some("10"));
}
#[test]
fn parse_tmpfs_with_size_option() {
let input = "tmpfs /tmp tmpfs size=10G,mode=1777 0 0\n";
let fstab = Fstab::parse_str(input).unwrap();
let e = &fstab.entries[0];
assert_eq!(e.options.get("size"), Some("10G"));
assert_eq!(e.options.get("mode"), Some("1777"));
}
#[test]
fn parse_bind_mount() {
let input = "/mnt/data /srv/data none bind 0 0\n";
let fstab = Fstab::parse_str(input).unwrap();
let e = &fstab.entries[0];
assert!(e.is_bind_mount());
}
#[test]
fn parse_nfs_with_multiple_options() {
let input = "server:/path /mnt nfs rw,hard,intr,timeo=100,retrans=3 0 0\n";
let fstab = Fstab::parse_str(input).unwrap();
assert_eq!(fstab.entries[0].options.get("timeo"), Some("100"));
assert_eq!(fstab.entries[0].options.get("retrans"), Some("3"));
}
#[test]
fn parse_cifs_with_credentials() {
let input = r"//server/share /mnt cifs credentials=/etc/samba/creds,uid=1000,gid=1000 0 0
";
let fstab = Fstab::parse_str(input).unwrap();
assert_eq!(
fstab.entries[0].options.get("credentials"),
Some("/etc/samba/creds")
);
}
#[test]
fn parse_entries_with_different_delimiters() {
let input = "\
UUID=a / ext4 defaults 0 1
UUID=b\t/boot\text4\tdefaults\t0\t2
UUID=c /data xfs defaults 0 0
";
let fstab = Fstab::parse_str(input).unwrap();
assert_eq!(fstab.entries.len(), 3);
}
#[test]
fn parse_error_reports_line_number() {
let input = "good /mnt ext4 defaults 0 0\nbadline\n";
let err = Fstab::parse_str(input).unwrap_err();
let msg = err.to_string();
assert!(
msg.contains("line 2"),
"expected line 2 in error, got: {msg}"
);
}