fits-well 0.1.4

A blazing-fast reader and writer for FITS (Flexible Image Transport System) files, targeting the full FITS 4.0 standard.
Documentation
use super::*;
use crate::header::from_card_lines as header;

#[test]
fn classifies_by_mandatory_keywords() {
    assert_eq!(
        HduKind::classify(&header(&["SIMPLE  = T"])),
        HduKind::Primary
    );
    assert_eq!(
        HduKind::classify(&header(&["XTENSION= 'IMAGE   '"])),
        HduKind::Image
    );
    assert_eq!(
        HduKind::classify(&header(&["XTENSION= 'TABLE   '"])),
        HduKind::AsciiTable
    );
    assert_eq!(
        HduKind::classify(&header(&["XTENSION= 'BINTABLE'"])),
        HduKind::BinTable
    );
    // A BINTABLE flagged ZIMAGE/ZTABLE is classified by its payload, not its container.
    assert_eq!(
        HduKind::classify(&header(&["XTENSION= 'BINTABLE'", "ZIMAGE  = T"])),
        HduKind::CompressedImage
    );
    assert_eq!(
        HduKind::classify(&header(&["XTENSION= 'BINTABLE'", "ZTABLE  = T"])),
        HduKind::CompressedTable
    );
    assert_eq!(
        HduKind::classify(&header(&["SIMPLE  = T", "GROUPS  = T"])),
        HduKind::RandomGroups
    );
    assert_eq!(
        HduKind::classify(&header(&["XTENSION= 'FOO     '"])),
        HduKind::Other
    );
}

#[test]
fn image_data_extent_matches_hand_computed_size() {
    // 512×512 signed 16-bit image: 2 × 512 × 512 = 524288 bytes,
    // rounded up to 183 blocks = 527040 bytes.
    let h = header(&[
        "BITPIX  = 16",
        "NAXIS   = 2",
        "NAXIS1  = 512",
        "NAXIS2  = 512",
    ]);
    let e = data_extent(&h).unwrap();
    assert_eq!(e.data_bytes, 524_288);
    assert_eq!(e.padded_bytes, 527_040);
}

#[test]
fn dataless_primary_has_no_data_unit() {
    let e = data_extent(&header(&["BITPIX  = 8", "NAXIS   = 0"])).unwrap();
    assert_eq!(e.data_bytes, 0);
    assert_eq!(e.padded_bytes, 0);
}

#[test]
fn rejects_malformed_pcount_and_gcount_instead_of_clamping() {
    let neg_pcount = header(&[
        "XTENSION= 'BINTABLE'",
        "BITPIX  = 8",
        "NAXIS   = 2",
        "NAXIS1  = 4",
        "NAXIS2  = 3",
        "PCOUNT  = -1",
    ]);
    assert!(matches!(
        data_extent(&neg_pcount),
        Err(FitsError::KeywordOutOfRange { name: "PCOUNT" })
    ));

    let zero_gcount = header(&[
        "XTENSION= 'IMAGE   '",
        "BITPIX  = 8",
        "NAXIS   = 1",
        "NAXIS1  = 4",
        "GCOUNT  = 0",
    ]);
    assert!(matches!(
        data_extent(&zero_gcount),
        Err(FitsError::KeywordOutOfRange { name: "GCOUNT" })
    ));
}

#[test]
fn axis_product_overflow_is_an_error_not_a_wrap() {
    // Three axes near 2^32 overflow u64 when multiplied — must not wrap into
    // a small, plausible-but-wrong byte count.
    let h = header(&[
        "BITPIX  = 8",
        "NAXIS   = 3",
        "NAXIS1  = 4294967296",
        "NAXIS2  = 4294967296",
        "NAXIS3  = 4294967296",
    ]);
    assert!(matches!(data_extent(&h), Err(FitsError::DataUnitOverflow)));
}

#[test]
fn random_groups_extent_skips_the_zero_first_axis() {
    // BITPIX=-32 (4 bytes), per-group array = 3×4×1×1×1 = 12 elems,
    // plus PCOUNT=6 params, over GCOUNT=7956 groups:
    // 4 × 7956 × (6 + 12) = 572832 bytes → 199 blocks = 573120.
    let h = header(&[
        "BITPIX  = -32",
        "NAXIS   = 6",
        "NAXIS1  = 0",
        "NAXIS2  = 3",
        "NAXIS3  = 4",
        "NAXIS4  = 1",
        "NAXIS5  = 1",
        "NAXIS6  = 1",
        "GROUPS  = T",
        "PCOUNT  = 6",
        "GCOUNT  = 7956",
    ]);
    let e = data_extent(&h).unwrap();
    assert_eq!(e.data_bytes, 572_832);
    assert_eq!(e.padded_bytes, 573_120);
}