Skip to content

FITSChan Code Coverage Report #44

Description

@timj

This is a coverage report from Claude based on the work done in #41 -- it tried really hard to trigger some edge cases but came to the conclusion that some code is impossible to trigger.

fitschan.c Coverage Summary

Current Status (2026-04-26)

  • Lines: 92.6% (10793/11658)
  • Branches: 71.5% (7497/10485)
  • Tests: 348 total
  • Bugs fixed: 11
    1. FreePV leak in wcsmap.c (astGetNin→mapping.nin in destructor)
    2. TidyOffsets dead code in fitschan.c (IsASkyFrame→astIsASkyFrame)
    3. RESTFREQ GHz/MHz comment detection (re-find card before CardComm)
    4. CLASSFromStore rest frequency uninitialized for galactic coordinates
    5. SetAttrib AltAxes sscanf pattern (%d→%n for string values)
    6. NearestPix rounding: (int)(x+0.5)→round(x) for platform independence
    7. FitsAxisOrder leak/use-after-free in Copy/Delete
    8. LoadFitsChan FindString count 9→10 for KINT type
    9. ZPXMapping watstr leak on unsupported features (break before free)
    10. PCFromStore/FindLonLatSpecAxes heap buffer overflow on short CTYPE strings
    11. TabMapping heap-buffer-overflow in permutation loop when coordinate array
      dimension has no corresponding FITS-WCS axis (marray contains -1)
  • Latent bugs found: 2
    • RFVAL keywords never written by SpecTrans despite being read in
      WcsCelestial — only reachable via hand-crafted headers
    • MakeFitsFrameSet does not propagate Dut1/Dtai frame attributes when
      reconstructing the WCS frame, making the DUT1/DTAI write path at
      lines 2931/2935 unreachable (DUT1 can be read but not round-tripped)

Uncovered Lines Breakdown

  • Total uncovered: 865 executable lines

Remaining uncovered by function

Function Uncovered Notes
SpectralAxes 53 Grism write (39), LOG/grism detection overhead (10), non-linear unit mapping (4 — unreachable)
SetFits 42 Dead code: KINT/COMPLEXF/COMPLEXI/LOGICAL/UNDEF branches never called by SetValue
CLASSFromStore 29 Validation paths unreachable via normal write (AddEncodingFrame normalizes)
PCFromStore 27 Alternate version validation (19 — encoding can't represent different RADESYS), error paths (8)
CnvType 25 Internal error branches (AST__INTER) — unreachable via public API
AIPSFromStore 24 Validation: missing keywords, NCP-equator, kHz/MHz normalization (dead via AddEncodingFrame)
FitsGetCom 24 Protected method, no internal callers — unreachable from C tests
AIPSPPFromStore 23 Validation: missing keywords, NCP-equator, kHz/MHz normalization (dead via AddEncodingFrame)
SIPIntWorld 20 Reversed axis ordering (12), multi-mapping chains (6), MapSplit failure (2) — all unreachable from FITS headers
GetObjSize 19 Protected method — memory accounting for card types
SkySys 18 Unknown celestial system CTYPE fallback (14), isoff>0 dead code (4)
MakeIntWorld 17 Degenerate axis CRPIX adjustment (12 — needs non-sky degenerate axes, not produced by MakeFitsFrameSet), column-scaling fallbacks (5)
CelestialAxes 16 Scattered edge cases in celestial axis detection
EncodeValue 15 Encoding overflow for complex/integer/float — all unreachable with standard FITS card widths
WcsNatPole 15 Degenerate numerical cases (sin(theta0)=-1, indeterminate LATPOLE)
ChangePermSplit 14 Recursive PermMap decomposition in CmpMaps
MakeIndentedComment 14 Native Dump formatting (only used during AST serialization)
MatchFront 14 Dead code: digit widths 4-9 unused in keyword patterns
astFitsChanForId_ 13 Fortran wrapper, not callable from C tests
ExtractGrismMap 12 Grism detection in mapping chain
AddVersion 7 SkyRefIs IGNORED path (1 — dead), WorldAxes failure (6)
ScalePolyInputs 11 PolyMap input scaling for non-standard ranges
WorldAxes 11 Degenerate axis mappings not produced by well-formed FITS
EncodeFloat 10 Decimal-point insertion (8 — integer-like values filling width), E0 exponent (2 — dead: %G always produces E+/E-)
FindBasisVectors 10 Untriggerable from valid FITS (needs undefined IWC at grid centre)
IsMapTab1D 9 MapSplit fallback paths (8), LutMap table creation failure (1)
IsMapTab2D 10 2D -TAB table lookup
TabMapping 5 Multi-dim index CmpMap (5 — needs 3D+ coord array)

Classification of remaining uncovered lines

Dead code / unreachable (~199 lines):

  • SetFits type dispatch (42 lines) — KINT/COMPLEXF/COMPLEXI/LOGICAL/UNDEF
    branches never called; SetValue only passes FLOAT/STRING/INT types
  • FitsGetCom (24 lines) — protected virtual method with no internal callers
  • SIPIntWorld residual (20 lines) — axis ordering absorbed by IWC reordering
  • GetObjSize (19 lines) — protected method, not in public ast.h
  • PCFromStore alternate RADESYS/equinox/latpole/lonpole rejection (19 lines) —
    FITS-PC encoding requires all alternates to share primary values
  • EncodeValue overflow paths (15 lines) — INT/KINT/COMPLEXI/COMPLEXF/string all fit
    within FITS 80-char card limit; unknown-type is AST__INTER internal error
  • MatchFront digit-widths 4-9 (14 lines) — no keyword pattern uses these
  • astFitsChanForId_ (13 lines) — Fortran wrapper, not callable from C
  • MakeIntWorld degenerate CRPIX adjustment (12 lines) — needs non-sky degenerate
    axes (lin[i]=1 with jax >= nin), but MakeFitsFrameSet only adds SkyFrame axes
  • PutFits value parse errors (7 lines) — Split pre-validates with same sscanf
  • AIPSFromStore kHz/MHz/GHz unit branches (6 lines) — AddEncodingFrame normalizes to Hz
  • SplitMap nested WcsMap cleanup (6 lines) — FITSProj WcsMaps can't survive astSimplify
  • SkySys isoff>0 block (4 lines) — offset SkyFrames excluded by IsASkyFrame
  • SpecTrans LAMBDA→WAVE (4 lines) — strcmp against "LAMBDA " (space-padded)
    but AST strips trailing spaces from FITS string values, so match can never occur
  • ClassTrans "no velocity" warning (4 lines) — detection function requires VELO-EAR,
    which ClassTrans always matches before reaching the "not found" fallback
  • IRAFFromStore NCP-equator (3 lines) — NCP undefined at dec=0
  • WcsFromStore CNAME write (2 lines) — cname and ctype_com always equal
  • WcsIntWorld badmat Warn (2 lines) — pre-empted by WcsPCMatrix error
  • CheckFitsName long name (2 lines) — Split truncates to 8 chars before check
  • EncodeFloat E0 exponent (2 lines) — C %G format always produces E+ or E-, never bare E0
  • Platform-specific strerror branch (2 lines) — always has strerror on Linux
  • SkyRefIs IGNORED path in AddVersion (1 line) — IsSkyOff only returns 1 for
    POLE/ORIGIN, so the else at line 2797 is unreachable
  • DUT1/DTAI write (2 lines) — MakeFitsFrameSet doesn't propagate Dut1/Dtai
    attributes to the reconstructed WCS frame, so astTestDut1 always returns 0
  • AIPS/AIPS++ default epoch (4 lines) — AST SkyFrames always provide Equinox,
    so the epoch == AST__BAD fallback is never reached

Internal error paths (~34 lines):

  • CnvType AST__INTER paths (25 lines) — indicate bugs, not user-triggerable
  • FormatKey snprintf failure (6 lines) — cannot fail for ASCII keyword names
  • Other internal consistency checks

Validation/error paths (~200 lines):

  • *FromStore functions: unsupported projections, missing keywords, invalid configs
  • WcsCelestial/WcsNatPole/WorldAxes: degenerate numerical cases
  • AddVersion/FsetFromStore: non-invertable mappings, incomplete alternates
  • PutFits/Split: card insertion validation
  • GetValue: missing required keywords

Protected methods (~43 lines):

  • FitsGetCom (24 lines) — comment retrieval, not in public ast.h
  • GetObjSize (19 lines) — memory accounting, not in public ast.h

Specialized write paths (~150 lines):

  • SpectralAxes grism (39 lines) — needs non-linear GrismMap over pixel range
  • CLASSFromStore validation (29 lines) — AddEncodingFrame normalization
  • ComBlock CLASS patterns — unusual MAXIS/NAXIS
  • SkySys unknown celestial system (14 lines) — UNLN/UNLT fallback

Table/Dump/utility (~100 lines):

  • IsMapTab1D/2D, TabMapping — -TAB write-path MapSplit edge cases, multi-dim read
  • MakeIndentedComment, MakeIntoComment, Dump/Load edge cases
  • ChangePermSplit (14 lines) — recursive PermMap decomposition
  • ReadFromSource, WriteToSink — I/O callback handling

Scattered 1-2 line gaps (~110 lines):

  • Across 60+ functions, typically branch-not-taken in conditionals

Investigation notes

PutFits (7 lines): All uncovered lines are else branches after sscanf
parsing of keyword values. The Split function pre-validates values using the
same sscanf patterns before PutFits sees them, so these error paths are
unreachable through the public API.

SkySys isoff>0 (15 lines): The isoff > 0 block in SkySys (OFLN/OFLT
CTYPE and offset CTYPE comments) is unreachable because offset SkyFrames have
domain "SKY_ORIGIN"/"SKY_POLE", but SkySys requires domain "SKY" (via
IsASkyFrame check at entry). The OFLN/OFLT output for offset SkyFrames
actually comes from OtherAxes using the SkyFrame's axis symbols.

SpectralAxes grism write (39 lines): Requires ExtractGrismMap to find a
GrismMap in the simplified pixel→WCS mapping that produces non-linear output
exceeding IsMapLinear's 1e-7 tolerance. Realistic grism parameters linearise
over typical detector sizes. 1D WAVE-GRI headers fail to read at large
NAXIS1, and direct GrismMap construction fails because MakeFitsFrameSet needs
celestial reference position for FITS-WCS paper III.

FitsGetCom (24 lines): Protected virtual method (astFitsGetCom) that
retrieves keyword comments. Not exposed in the public ast.h header and has
no internal callers within fitschan.c or any other AST source file. Only
reachable by external applications that include fitschan.h directly.

SIPIntWorld residual (20 lines): The reversed inaxes[0] >= inaxes[1]
path (12 lines) requires the PolyMap's input axes to be in reverse order.
Verified empirically: even with DEC/RA axis swapping in the FITS header, the
pixiwcmap IWC reordering absorbs the permutation before MapSplit, so inaxes
is always {0,1} from FITS-derived FrameSets. Multi-mapping chain paths
(6 lines) need 3+ mappings in simplified series decomposition. MapSplit
failure paths (2 lines) need MapSplit to fail on a valid mapping.

SplitMap nested WcsMap (6 lines): Cleanup paths for when map1 or map3
contains a nested WcsMap with FITSProj set. Attempted to construct FrameSets
with two FITSProj WcsMaps from different FITS reads, but astSimplify
eliminates them. FITSProj is a protected attribute not settable via public API.

WcsIntWorld badmat Warn (2 lines): The Warn at line 37594 is dead code.
WcsPCMatrix catches non-invertible matrices with an AST__BDFTS error before
the Warn is reached. With error status set, the Warn function returns without
issuing. Verified: a singular PC matrix [[0,0],[0,1]] causes WcsPCMatrix to
error, but the error is annulled by the WCS read retry logic and astRead
succeeds with a non-celestial interpretation.

MakeIntWorld degenerate axis CRPIX adjustment (12 lines): Lines 22233-22254
adjust CRPIX to 1.0 for degenerate axes (WCSAXES > NAXIS) where the axis is
non-celestial (lin[i]=1, meaning a plain Frame, not SkyFrame). The condition
jax >= nin requires the contributing pixel axis to be beyond the actual
input count. MakeFitsFrameSet adds celestial SkyFrame axes for FITS-WCS paper
III compliance, but these have lin=0 (SkyFrame), so the CRPIX adjustment
code is never reached.

EncodeFloat decimal-point insertion (8 lines): Lines 9882-9884 (one leading
space, no decimal point) and 9891-9899 (no leading spaces) require
sprintf("%W.PG", value) to produce an integer-like string with exactly one
or zero leading spaces. This needs exact integer-valued doubles that occupy
almost the entire field width (19-20 digits). Normal FITS WCS values don't
reach this.

EncodeFloat E0 exponent (2 lines): Line 9824 checks for "E0" (exponent
without sign). The C %G format always produces E+ or E- before the
exponent, never bare E0. Dead code.

EncodeValue overflow paths (15 lines): Lines 10010 (float encode failure),
10050-10055 (string too long), 10064-10076 (integer/KINT overflow),
10101-10139 (complex float/int overflow), 10147 (unknown type). All require
values that exceed the FITS card width of 80 characters. The widest formatted
integer (INT64_MAX) is 19 characters, well within the 69-character field limit.
The unknown-type path is an AST__INTER internal error. All effectively
unreachable.

CheckFitsName long name (2 lines): The error at line 5928 for keyword
names > 8 characters is unreachable via astPutFits. The Split function
truncates the keyword name with strncpy(*name, card, FITSNAMLEN) before
CheckFitsName sees it.

AddEncodingFrame normalization: The AddEncodingFrame function normalizes
spectral axes to Hz and standard-of-rest to SOURCE before *FromStore
functions see the FitsStore. This makes many unit-specific branches in
AIPSFromStore and AIPSPPFromStore (kHz, MHz, GHz, km/s) unreachable through
the normal write path.

SetFits type dispatch (42 lines): The SetFits function handles 10 data
types (FLOAT, STRING, CONTINUE, COMMENT, INT, KINT, COMPLEXF, COMPLEXI,
LOGICAL, UNDEF) but is only called from SetValue, which only passes
AST__FLOAT, AST__STRING, and AST__INT types. The other 7 type branches
(42 lines) are dead code. PutFits bypasses SetFits entirely, calling
astSetFitsK/astSetFitsCF/etc. directly after Split determines the type.

PCFromStore alternate versions (19 lines): FITS-PC encoding requires all
alternate WCS versions to share the primary's PC matrix, RADESYS, EQUINOX,
LATPOLE, and LONPOLE. Any alternate celestial frame with a different
coordinate system (e.g. Galactic alternate on an FK5 primary) will have
different RADESYS, triggering rejection at the RADESYS comparison. Verified:
writing FK5 + Galactic FrameSet to FITS-PC with AltAxes=ALL produces only
the primary version — the Galactic alternate is silently rejected.

DUT1/DTAI write (2 lines): Lines 2931/2935 store Dut1/Dtai in the FitsStore
when astTestDut1(wcsfrm) is true. However, wcsfrm comes from the FrameSet
returned by MakeFitsFrameSet, which reconstructs the WCS frame without
propagating the Dut1/Dtai attributes. Even when Dut1 is explicitly set on the
original SkyFrame (via constructor or astSetD), MakeFitsFrameSet's frame
copying loses it. This makes lines 2931/2935 effectively unreachable through
the normal write path. Potential latent bug: MakeFitsFrameSet should propagate
frame-level time correction attributes.

AIPS/AIPS++ default epoch (4 lines): Lines 3482/3484 (AIPS) and 3986/3988
(AIPS++) set default Equinox when epoch == AST__BAD. The epoch variable
comes from the FitsStore's equinox item, populated from the SkyFrame's Equinox
attribute. AST SkyFrames always provide an Equinox (defaulting to B1950 for
FK4, J2000 for FK5), so epoch is never AST__BAD. Unreachable.

NonLinSpecWcs unknown system (line 23596): Now covered via the
error-unknown-spectral-algorithm test using CTYPE WAVE-Z2F. The 'Z'
character is not recognized as a valid spectral system (only F/W/V/A are
valid), triggering the ok=0 fallback which propagates as AST__BDFTS.

Source fixes

  • src/wcsmap.c — FreePV leak fix (astGetNin→mapping.nin in destructor)
  • src/fitschan.c — TidyOffsets bug fix (IsASkyFrame→astIsASkyFrame)
  • src/fitschan.c — RESTFREQ GHz/MHz comment fix (re-find card before CardComm)
  • src/fitschan.c — CLASSFromStore rest frequency fix (move rf check outside
    RADESYS conditional to avoid uninitialized variable for galactic coords)
  • src/fitschan.c — SetAttrib AltAxes fix (change sscanf from %d to
    %n%*[^\n]%n pattern — the %d stored the parsed integer in ival which
    was then reused as a string offset, making string values unreachable)
  • src/fitschan.c — NearestPix rounding fix (change (int)(x+0.5) to
    round(x) — the int cast truncates toward zero, giving platform-
    dependent results for pixel values near N.5 due to FP representation
    differences between Linux and macOS)
  • src/fitschan.c — FitsAxisOrder Copy/Delete fix (deep-copy in Copy
    constructor, free in destructor — was shared pointer / leak)
  • src/fitschan.c — LoadFitsChan FindString count fix (9→10 to include
    KINT "64 bit integer" type — KINT keywords could be Dumped but not Loaded)
  • src/fitschan.c — ZPXMapping watstr leak fix (free WAT string before
    breaking from loop when WATCoeffs returns unsupported features)
  • src/fitschan.c — PCFromStore/FindLonLatSpecAxes heap buffer overflow
    fix (strlen(cval) called before NULL check; astWcsPrjType(cval+4) and
    ctype[4] accessed without checking string length — triggers when writing
    a FrameSet with short CTYPE names to FITS-PC encoding)
  • src/fitschan.c — TabMapping heap-buffer-overflow fix: add astOK check
    to permutation loop to prevent permin[-1] access when marray contains -1
    from a missing FITS-WCS axis for a coordinate array dimension

New test headers (ast_tester/*.head)

ncp, velo-vel, vopt-wav, zopt-frq, cname, skyref, fits-pc-unit, gls,
iraf-longpole, multi-wcs-spec, freq-wav, wavn-frq, mercator, gappt,
atf, equinox-bj, timesys-tdb, zero-cdelt, alt-spectral, helioecl,
fits-pc-full, projp, fk5-noeq, timesys-tt, skyref-alt, axref,
unknown-cel, projp-pv, cd-old, fk4-noeq, unknown-cel2,
tidy-offsets, tidy-pole, date-short, date-frac, equinox-zero,
ssysobs, ener-vel, beta-frq, sip-alt, ukirt-dtai, rfval,
vrad-wav, projp-nopv0, projp-pv0only, restfreq-ghz, pc-sparse,
restfreq-mhz, unknown-spec, crota-3d, axes-swap, cd-cross,
class-noproj, ait-highlatpole, freq-ghz-bary, vopt-geo,
fk4-aips, sfl-origin, vrad-lsrd, class-write, class-gal,
offset-sky-pole, offset-sref, class-arc, class-ait, class-sin,
skyspec3d, skyspec3d-class, offset-sky, azel, vopt-f2w, wave-f2w,
fits-pc-gal, multi-wcs-dup, sip-3d, sip-3d-swap,
class-sfl, class-olddate, sin-plain, wave-1d, sfl-pc,
tan-rotated, tan-cdmatrix, pixel-2d, freq-pixel3d,
pixel-1d, pixel-rotated, pixel-shear,
galactic-tan, ecliptic-tan, date-z, fits-pc-altgal

testfitschan.c additions

  • Dump/Load round-trip with all keyword types: INT, FLOAT, STRING, KINT,
    LOGICAL, COMPLEXF, COMPLEXI, UNDEF; plus Encoding, AltAxes, FitsAxisOrder
    attributes (error 400-409)
  • Dump/Load with card flags (USED cards from astRead), UNDEF keyword
    round-trip verification, default Encoding/AltAxes load paths (error 410-416)
  • CnvType cross-type conversion tests (error 700-791)
  • LOGICAL(false)→STRING edge case (error 790-791)
  • CnvType failure paths: STRING→FLOAT/INT/KINT/LOGICAL/COMPLEXF/COMPLEXI
    with non-numeric string (error 795-800)
  • CnvType: UNDEF→FLOAT/INT (error 801-802)
  • CnvType: COMMENT→FLOAT (error 803)
  • CnvType: FLOAT(AST__BAD)→STRING and STRING("")→FLOAT (error 804-807)
  • All 9 TIMESYS variants loop including UTC (error 810-818)
  • Ill-conditioned SFL leak test (error 800)
  • TestAttrib for read-only attributes: Ncard, Nkey, CardType, AllWarnings
    (error 995-998)
  • GetAttrib parent delegation via Class, Full attributes (error 993-994)
  • Non-linear spectral VOPT-F2W, WAVE-F2W, WAVN-W2F, AWAV-F2A round-trips
    (error 606-655)
  • Non-linear spectral VELO-F2V, FREQ-A2F, FREQ-V2F (error 660-665)
  • Spectral axis Label + ObsGeo write (error 670-673)
  • LOG spectral algorithm via MathMap exponential mapping (error 680-684)
  • SplitMap linear-sky → CAR write test (error 850-857)
  • SplitMap constant-sky → TAN write test (error 860-862)
  • FitsAxisOrder write test (error 890-892)
  • PutCards bulk card insertion test (error 893)
  • DUT1/DTAI read test (error 900-901)
  • AddEncodingFrame CLASS VRAD/FK4 tests (error 910-915)
  • MakeFitsFrameSet 1D SpecFrame with RefRA/RefDec (error 920-923)
  • SkySys axis label write test (error 930-932)
  • SkySys helioecliptic write test (error 940-941)
  • Old DATE-OBS dd/mm/yy format write test (error 950-951)
  • FITS-PC write with alternate axes via 1D spectral FrameSet (error 960-963)
  • Offset SkyFrame write with SkyRef/SkyRefIs verification (error 971-973)
  • 3D SIP write with >2 axis PermMap handling (error 980-983)
  • 3D SIP write with swapped DEC/RA axis order (error 984-987)
  • EncodeFloat adaptive width via FitsDigits=-20 (error 988-989)
  • SpecTrans WAVE/angstrom CUNIT normalization with validation (error 990)
  • ClassTrans VELO-EAR geocentric velocity path with validation (error 991)
  • CLASSFromStore 1D→3D NAXIS expansion from 1D SpecFrame (error 1000-1006)
  • PCFromStore WCSAXES from 1D SpecFrame with RefRA/RefDec (error 1020-1022)
  • ReadFromSource via source callback function (error 830-832)
  • PCFromStore short CTYPE regression — generic Frames to FITS-PC (error 833-834)
  • SkySys Galactic and Supergalactic write paths (error 835-836)
  • WcsFromStore alternate version write — FK5 + Galactic with AltAxes=ALL (error 840-841)
  • SetFits overwrite paths via PutFits for KINT, COMPLEXI, COMPLEXF, LOGICAL,
    UNDEF, CONTINUE, COMMENT types (error 808-809)
  • -TAB non-unit index write/read via MathMap(x^2)→LutMap (error 1067-1069)
  • AIPS++ encoding auto-detection with CD matrix + AIPS spectral CTYPE (error 903)
  • Non-invertable PolyMap FrameSet write rejection — TabOK=0 (error 904)
  • TestAttrib parent delegation via Full attribute (error 992)

testfitschanerrors.c — table-driven warning/error tests

New test program using embedded malformed FITS headers to exercise warning
and error paths. Each test entry specifies: cards, warning category or
expected error code, expected text substring, and optional attributes.

Warning tests (Warnings attribute set, text verified via astWarnings KeyMap):

  • distortion-sip-on-axis3: -SIP on axis 3 (line 8819)
  • distortion-unknown: Unknown -XXX distortion suffix (line 8905)
  • badpv-tan-allzero: All-zero TAN PV latitude coefficients (line 13984)
  • badpv-lonaxis-allzero: All-zero TAN PV longitude coefficients (line 14029)
  • noctype-missing: Missing CTYPE for non-celestial axis (line 35633)
  • badpv-lonaxis: PV1_5 exceeding TAN longitude max (line 35922)
  • badkeyvalue-unparseable: Invalid keyword value, checked after PutFits (line 32552)
  • badpv-sao-allzero: SAO polynomial with all-zero coefficients (line 26715)
  • tnx-unsupported: TNX with unsupported cross-term type 1 (line 31917)
  • zpx-unsupported: ZPX with unsupported surface type 2 in WAT (line 41574)
  • badkeyname-nonprint: Non-printing control character in keyword name (line 5960)
  • badval-timesys-ut: TIMESYS=UT interpreted as UTC (line 34575)
  • badval-timesys-et: TIMESYS=ET interpreted as TT (line 34584)

Error tests (astStatus checked and cleared):

  • error-sourcefile-missing: Non-existent SourceFile → AST__RDERR
  • error-lat-without-lon: DEC--TAN without matching RA → AST__BDFTS
  • error-unknown-projection: RA---XXX → AST__BDFTS
  • error-duplicate-lon: Two RA---TAN axes → AST__BDFTS
  • error-mixed-celestial: RA with GLAT → AST__BDFTS
  • error-mixed-projections: TAN lon with ARC lat → AST__BDFTS
  • error-bad-encoding: Unknown Encoding value → AST__BADAT
  • error-bad-altaxes: Bad AltAxes value → AST__BADAT
  • error-duplicate-lat: Two DEC--TAN axes → AST__BDFTS
  • error-bad-radesys: Unknown RADESYS → AST__BDFTS
  • error-set-readonly: Set Ncard → AST__NOWRT
  • error-unknown-lat-projection: DEC--XXX → AST__BDFTS
  • error-unknown-spectral-algorithm: WAVE-Z2F unknown system → AST__BDFTS

Attribute error tests (manual in main):

  • clear-readonly-ncard: astClear Ncard → AST__NOWRT
  • clear-readonly-allwarnings: astClear AllWarnings → AST__NOWRT
  • unknown-warning-condition: astSetC Warnings "badxyz" → AST__ATTIN

-TAB error tests (manual in main, using astPutTable):

  • tab-missing-column: Table column not found → AST__BADTAB (line 33554)
  • tab-1d-column: Table column has 1D instead of ≥2D → AST__BADTAB (line 33566)
  • tab-duplicate-axis: Two axes map to same coord dimension → AST__BADTAB (line 33616)
  • tab-missing-axis: Coord array dimension has no FITS axis → AST__BADTAB (line 33635)

Write-rejection tests (astWrite returns 0, FITS-WCS fallback returns 1):

  • aips-reject-ait-nonorigin: AIT at non-origin
  • aips-reject-lonpole: Non-default LONPOLE
  • aips-reject-wave: WAVE spectral type
  • aips++-reject-wave: WAVE spectral type for AIPS++
  • aips-reject-topocent: TOPOCENT spectral frame
  • aips++-reject-topocent: TOPOCENT for AIPS++
  • aips-reject-pixel-shear: Asymmetric non-celestial CD matrix

Graceful-recovery tests (error annulled, read succeeds):

  • error-cdelt-zero: CDELT=0 → singular matrix → non-celestial fallback

What Would Move the Needle Next

Of the 865 remaining uncovered lines, approximately:

  • ~205 are dead code or unreachable via public API (including DUT1/DTAI write,
    AIPS default epoch, SetFits type dispatch, protected methods, Fortran wrapper,
    internal errors, pre-empted error paths, platform branches, AddEncodingFrame
    normalization, PCFromStore alternate constraints, LAMBDA→WAVE comparison,
    ClassTrans velocity warning, SkyRefIs IGNORED, EncodeFloat E0 exponent,
    EncodeValue overflow, MakeIntWorld degenerate CRPIX)
  • ~34 are internal error paths (AST__INTER — indicate AST bugs)
  • ~200 are validation/error paths in encoding and read functions
  • ~150 are specialized write paths (grism, CLASSFromStore, SkySys)
  • ~43 are protected methods (FitsGetCom, GetObjSize — not in public ast.h)
  • ~100 are table/dump/utility code (-TAB edge cases, Dump formatting)
  • ~110 are scattered small gaps (1-2 lines each across 60+ functions)

The highest-value remaining targets:

  1. -TAB table lookup (24 lines across IsMapTab1D/2D, TabMapping) —
    Write-path MapSplit fallback (8), IsMapTab2D edge cases (10), TabMapping
    multi-dim index CmpMap read (5 — needs 3D+ coord array), LutMap table
    creation failure (1).
  2. SpectralAxes grism write (53 lines) — see investigation notes.
    Fundamentally hard to trigger from the public API.
  3. MakeIntWorld (17 lines) — degenerate WCSAXES > NAXIS cases.
    CRPIX adjustment (12 lines) needs non-sky degenerate axes which
    MakeFitsFrameSet doesn't produce. Column-scaling fallbacks (5 lines).
  4. EncodeFloat (10 lines) — mostly dead code or extreme edge cases.
    Decimal-point insertion for integer-like values (8), E0 exponent dead
    code (2).

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions