acyclic-fs 0.1.5

Rust SDK for versioned workspaces and embedded native mounts
Documentation
use super::*;

#[test]
fn portable_raw_posix_and_windows_names_remain_exact() -> Result<(), Box<dyn std::error::Error>> {
    let limits = VolumeLimits::default();
    let portable = PortablePath::parse("/workspace/src", limits)?;
    let converted = NamespacePath::from_portable(&portable, limits)?;
    assert_eq!(converted.depth(), 2);
    assert_eq!(converted.encoded_bytes(), 14);
    let parent = converted
        .parent()
        .ok_or_else(|| std::io::Error::other("workspace parent is missing"))?;
    assert_eq!(parent.depth(), 1);
    assert_eq!(parent.encoded_bytes(), 10);
    let root = parent
        .parent()
        .ok_or_else(|| std::io::Error::other("namespace root is missing"))?;
    assert!(root.is_root());
    assert_eq!(root.encoded_bytes(), 1);
    assert!(root.parent().is_none());

    let posix = LogicalName::new(NameEncoding::PosixBytes, vec![0xff, b'a'], 255)?;
    let windows = LogicalName::new(
        NameEncoding::WindowsUtf16Le,
        "file".encode_utf16().flat_map(u16::to_le_bytes).collect(),
        255,
    )?;
    let native = NamespacePath::new(vec![posix.clone(), windows], limits)?;
    assert_eq!(
        native.split_last().and_then(|(parent, _)| parent.first()),
        Some(&posix)
    );
    assert!(native.is_within(&NamespacePath::new(vec![posix], limits)?));
    Ok(())
}

#[test]
fn string_paths_use_the_volume_name_encoding() -> Result<(), Box<dyn std::error::Error>> {
    let limits = VolumeLimits::default();
    let portable = PortablePath::parse("/é/𝄞", limits)?;
    for (profile, encoding) in [
        (FilesystemProfile::Portable, NameEncoding::Utf8),
        (FilesystemProfile::Posix, NameEncoding::PosixBytes),
        (FilesystemProfile::Windows, NameEncoding::WindowsUtf16Le),
    ] {
        let path = NamespacePath::from_portable_in_profile(&portable, profile, limits)?;
        assert_eq!(
            path.components()
                .first()
                .and_then(LogicalName::unicode_text)
                .as_deref(),
            Some("é")
        );
        assert!(
            path.components()
                .iter()
                .all(|name| name.encoding() == encoding)
        );
        assert_eq!(
            path.components().first().map(LogicalName::as_bytes),
            Some(match profile {
                FilesystemProfile::Windows => &[0xe9, 0x00][..],
                _ => "é".as_bytes(),
            })
        );
        if profile == FilesystemProfile::Windows {
            assert_eq!(
                path.components().get(1).map(LogicalName::as_bytes),
                Some(&[0x34, 0xd8, 0x1e, 0xdd][..])
            );
        }
    }
    Ok(())
}

#[test]
fn bounds_are_checked_without_storage() -> Result<(), Box<dyn std::error::Error>> {
    let limits = VolumeLimits {
        maximum_path_bytes: 4,
        maximum_component_bytes: 2,
        maximum_path_depth: 1,
        ..VolumeLimits::default()
    };
    let name = LogicalName::new(NameEncoding::Utf8, b"ab".to_vec(), 2)?;
    assert!(NamespacePath::new(vec![name.clone()], limits).is_ok());
    assert_eq!(
        NamespacePath::new(vec![name.clone(), name], limits),
        Err(NamespacePathError::TooDeep)
    );

    let wide_limits = VolumeLimits {
        maximum_path_bytes: 64,
        maximum_component_bytes: 1,
        maximum_path_depth: 4,
        ..VolumeLimits::default()
    };
    let wide = LogicalName::new(NameEncoding::Utf8, b"ab".to_vec(), 2)?;
    assert_eq!(
        NamespacePath::new(vec![wide], wide_limits),
        Err(NamespacePathError::ComponentTooLong)
    );

    let short_path_limits = VolumeLimits {
        maximum_path_bytes: 2,
        maximum_component_bytes: 2,
        maximum_path_depth: 4,
        ..VolumeLimits::default()
    };
    let first = LogicalName::new(NameEncoding::Utf8, b"a".to_vec(), 2)?;
    let second = LogicalName::new(NameEncoding::Utf8, b"b".to_vec(), 2)?;
    assert_eq!(
        NamespacePath::new(vec![first, second], short_path_limits),
        Err(NamespacePathError::PathTooLong)
    );
    Ok(())
}