acyclic-fs 0.1.5

Rust SDK for versioned workspaces and embedded native mounts
Documentation
use super::*;
use crate::foundation::Digest;
use crate::kernel::attribute_mutation::AttributeFormat;
use crate::kernel::{AttributeClass, AttributeEntry, AttributeName, CanonicalDecodeError};
use crate::performance::WorkError;
use crate::storage::{ObjectKind, ObjectStoreError};

fn name(byte: u8) -> Result<AttributeName, crate::kernel::AttributeError> {
    AttributeName::new(AttributeClass::PosixXattr, vec![byte], 1)
}

fn entry(byte: u8) -> Result<AttributeEntry, crate::kernel::AttributeError> {
    Ok(AttributeEntry {
        name: name(byte)?,
        value_bytes: u64::from(byte),
        value: ObjectId {
            kind: ObjectKind::Blob,
            digest: Digest::from_bytes([byte; 32]),
        },
    })
}

fn child(byte: u8) -> Result<Child<AttributeName>, crate::kernel::AttributeError> {
    Ok(Child {
        first: name(byte)?,
        page: ObjectId {
            kind: ObjectKind::AttributePage,
            digest: Digest::from_bytes([byte; 32]),
        },
    })
}

fn root(kind: ObjectKind) -> ObjectId {
    ObjectId {
        kind,
        digest: Digest::from_bytes([9; 32]),
    }
}

#[test]
fn machine_admission_frontier_height_and_alias_guards_are_exact()
-> Result<(), Box<dyn std::error::Error>> {
    let limits = DecodeLimits::default();
    let wrong = Machine::<AttributeFormat>::new(
        root(ObjectKind::TreePage),
        None,
        1,
        limits,
        WorkBudget::UNBOUNDED,
    )
    .err()
    .ok_or("wrong pagination root kind was admitted")?;
    assert!(matches!(wrong.error, Error::WrongRootKind));

    let zero = Machine::<AttributeFormat>::new(
        root(ObjectKind::AttributePage),
        None,
        0,
        limits,
        WorkBudget::UNBOUNDED,
    )
    .err()
    .ok_or("zero pagination limit was admitted")?;
    assert!(matches!(zero.error, Error::ZeroLimit));

    let mut invalid_limits = limits;
    invalid_limits.maximum_page_height = 0;
    let invalid = Machine::<AttributeFormat>::new(
        root(ObjectKind::AttributePage),
        None,
        1,
        invalid_limits,
        WorkBudget::UNBOUNDED,
    )
    .err()
    .ok_or("invalid pagination limits were admitted")?;
    assert!(matches!(invalid.error, Error::InvalidLimits));

    let budget = Machine::<AttributeFormat>::new(
        root(ObjectKind::AttributePage),
        None,
        1,
        limits,
        WorkBudget::default(),
    )
    .err()
    .ok_or("zero work budget admitted pagination output")?;
    assert!(matches!(
        budget.error,
        Error::Work(WorkError::BudgetExceeded { .. })
    ));

    let mut empty = Machine::<AttributeFormat>::new(
        root(ObjectKind::AttributePage),
        None,
        1,
        limits,
        WorkBudget::UNBOUNDED,
    )?;
    let mut pending = empty.pop_pending()?;
    let absent = empty
        .pop_pending()
        .err()
        .ok_or("empty pagination frontier produced a request")?;
    assert!(matches!(absent.error, Error::TraversalState));
    pending.height = limits.maximum_page_height.saturating_add(1);
    empty.push_pending(pending)?;
    let excessive = empty
        .pop_pending()
        .err()
        .ok_or("overheight pagination frontier was accepted")?;
    assert!(matches!(excessive.error, Error::HeightExceeded));

    let mut alias = Machine::<AttributeFormat>::new(
        root(ObjectKind::AttributePage),
        None,
        1,
        limits,
        WorkBudget::UNBOUNDED,
    )?;
    let page = root(ObjectKind::AttributePage);
    alias.visit(page)?;
    let repeated = alias
        .visit(page)
        .err()
        .ok_or("repeated pagination page was accepted")?;
    assert!(matches!(repeated.error, Error::CycleOrAlias));
    Ok(())
}

#[test]
fn cursor_search_and_page_bounds_cover_every_edge() -> Result<(), Box<dyn std::error::Error>> {
    let values = [entry(2)?, entry(4)?, entry(6)?];
    assert_eq!(
        upper_bound_values::<AttributeFormat>(&values, &name(1)?).0,
        0
    );
    assert_eq!(
        upper_bound_values::<AttributeFormat>(&values, &name(2)?).0,
        1
    );
    assert_eq!(
        upper_bound_values::<AttributeFormat>(&values, &name(5)?).0,
        2
    );
    assert_eq!(
        upper_bound_values::<AttributeFormat>(&values, &name(9)?).0,
        3
    );
    assert!(validate_values::<AttributeFormat>(&values, None, None).is_ok());
    assert!(validate_values::<AttributeFormat>(&values, Some(&name(2)?), Some(&name(7)?)).is_ok());
    assert!(matches!(
        validate_values::<AttributeFormat>(&values, Some(&name(3)?), None),
        Err(Error::ChildBoundsMismatch)
    ));
    assert!(matches!(
        validate_values::<AttributeFormat>(&values, None, Some(&name(6)?)),
        Err(Error::ChildBoundsMismatch)
    ));

    let children = [child(2)?, child(4)?, child(6)?];
    assert_eq!(upper_bound_children(&children, &name(1)?).0, 0);
    assert_eq!(upper_bound_children(&children, &name(2)?).0, 0);
    assert_eq!(upper_bound_children(&children, &name(5)?).0, 1);
    assert_eq!(upper_bound_children(&children, &name(9)?).0, 2);
    assert!(validate_children::<AttributeFormat>(&children, None, None).is_ok());
    assert!(
        validate_children::<AttributeFormat>(&children, Some(&name(2)?), Some(&name(7)?)).is_ok()
    );
    assert!(matches!(
        validate_children::<AttributeFormat>(&children, Some(&name(3)?), None),
        Err(Error::ChildBoundsMismatch)
    ));
    assert!(matches!(
        validate_children::<AttributeFormat>(&children, None, Some(&name(6)?)),
        Err(Error::ChildBoundsMismatch)
    ));
    Ok(())
}

#[test]
fn bounded_leaf_and_internal_frontiers_stop_exactly_and_reject_corrupt_accounting()
-> Result<(), Box<dyn std::error::Error>> {
    let values = [entry(2)?, entry(4)?, entry(6)?];
    let mut leaf = Machine::<AttributeFormat>::new(
        root(ObjectKind::AttributePage),
        None,
        1,
        DecodeLimits::default(),
        WorkBudget::UNBOUNDED,
    )?;
    let pending = leaf.pop_pending()?;
    leaf.accept_shared_leaf(&values, &pending)?;
    assert_eq!(leaf.values, values[..2]);

    let children = [child(2)?, child(4)?, child(6)?, child(8)?];
    let mut internal = Machine::<AttributeFormat>::new(
        root(ObjectKind::AttributePage),
        None,
        1,
        DecodeLimits::default(),
        WorkBudget::UNBOUNDED,
    )?;
    let mut pending = internal.pop_pending()?;
    internal.accept_shared_internal(&children, &mut pending)?;
    assert_eq!(internal.successor_page, Some(children[3].page));

    let shallow_limits = DecodeLimits {
        maximum_page_height: 1,
        ..DecodeLimits::default()
    };
    let mut shallow = Machine::<AttributeFormat>::new(
        root(ObjectKind::AttributePage),
        None,
        1,
        shallow_limits,
        WorkBudget::UNBOUNDED,
    )?;
    let mut pending = shallow.pop_pending()?;
    assert!(matches!(
        shallow.accept_shared_internal(&children, &mut pending),
        Err(OperationFailure {
            error: Error::HeightExceeded,
            ..
        })
    ));

    let corrupt_pending = || -> Result<Pending<AttributeName>, crate::kernel::AttributeError> {
        Ok(Pending {
            page: root(ObjectKind::AttributePage),
            lower: Some(name(2)?),
            upper: Some(name(9)?),
            height: 1,
            nested_bytes: 0,
        })
    };
    let bounded_children = [child(2)?, child(4)?];
    let mut shared = Machine::<AttributeFormat>::new(
        root(ObjectKind::AttributePage),
        None,
        1,
        DecodeLimits::default(),
        WorkBudget::UNBOUNDED,
    )?;
    let mut pending = corrupt_pending()?;
    assert!(matches!(
        shared.accept_shared_internal(&bounded_children, &mut pending),
        Err(OperationFailure {
            error: Error::TraversalState,
            ..
        })
    ));

    let mut owned = Machine::<AttributeFormat>::new(
        root(ObjectKind::AttributePage),
        None,
        1,
        DecodeLimits::default(),
        WorkBudget::UNBOUNDED,
    )?;
    let mut pending = corrupt_pending()?;
    assert!(matches!(
        owned.accept_internal(bounded_children.to_vec(), 2, &mut pending),
        Err(OperationFailure {
            error: Error::TraversalState,
            ..
        })
    ));
    Ok(())
}

#[test]
fn item_charging_and_lower_layer_error_translation_are_total()
-> Result<(), Box<dyn std::error::Error>> {
    let mut work = WorkCounters::default();
    charge_items(&mut work, 2, WorkBudget::UNBOUNDED)?;
    assert_eq!(work.items_examined, 2);
    let exact_budget = work;
    let rejected = charge_items(&mut work, 1, exact_budget)
        .err()
        .ok_or("over-budget item charge succeeded")?;
    assert!(matches!(
        rejected.error,
        Error::Work(WorkError::BudgetExceeded { .. })
    ));

    for allocation in [
        AllocationError::Overflow,
        AllocationError::ReleaseInvariant,
        AllocationError::InvalidCapacity,
        AllocationError::CapacityExceeded,
        AllocationError::AllocationFailed,
    ] {
        assert!(matches!(
            map_allocation(allocation),
            Error::AllocationFailed
        ));
    }
    assert!(matches!(
        map_allocation(AllocationError::Work(WorkError::Overflow)),
        Error::Work(WorkError::Overflow)
    ));
    assert!(matches!(
        map_io(persistent_io::Error::AllocationFailed),
        Error::AllocationFailed
    ));
    assert!(matches!(
        map_io(persistent_io::Error::Allocation(
            AllocationError::CapacityExceeded
        )),
        Error::AllocationFailed
    ));
    assert!(matches!(
        map_io(persistent_io::Error::Storage(ObjectStoreError::Missing)),
        Error::Storage(ObjectStoreError::Missing)
    ));
    assert!(matches!(
        map_io(persistent_io::Error::Decode(
            CanonicalDecodeError::Truncated
        )),
        Error::Decode(CanonicalDecodeError::Truncated)
    ));
    assert!(matches!(
        map_io(persistent_io::Error::Work(WorkError::Overflow)),
        Error::Work(WorkError::Overflow)
    ));
    Ok(())
}