Skip to content

Commit bddf975

Browse files
committed
test: cover the advance_rg_plan_to guard; make it a testable free function
Per @adriangb, the internal_err guard on advance_rg_plan_to had no coverage (cargo-mutants: mutating == to != survived). Make advance_rg_plan_to a free function over &mut VecDeque<RgPlanEntry> so its pop/guard logic is unit-testable without a full stream state, and add two tests: pop up to target, and the guard erroring when the target is absent. Both fail under the ==/!= mutation.
1 parent c4f5633 commit bddf975

1 file changed

Lines changed: 42 additions & 12 deletions

File tree

‎datafusion/datasource-parquet/src/push_decoder.rs‎

Lines changed: 42 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -441,30 +441,32 @@ impl PushDecoderStreamState {
441441
.peek_next_row_group()
442442
.map_err(DataFusionError::from)?
443443
{
444-
Some(actual) => self.advance_rg_plan_to(actual)?,
444+
Some(actual) => Self::advance_rg_plan_to(&mut self.rg_plan, actual)?,
445445
// Decoder has nothing left to emit — drain our plan so the stream
446446
// finishes cleanly.
447447
None => self.rg_plan.clear(),
448448
}
449449
Ok(())
450450
}
451451

452-
/// Pop `rg_plan` entries until its front is `target`.
452+
/// Pop entries off `rg_plan` until its front is `target`.
453453
///
454-
/// `target` is the RG the decoder will emit next and must still be in our
455-
/// plan. A missing `target` means the decoder's frontier and `rg_plan`
456-
/// have diverged; we surface that as an internal error rather than
457-
/// silently draining the plan, which would truncate the scan.
458-
fn advance_rg_plan_to(&mut self, target: usize) -> Result<()> {
459-
while let Some(front) = self.rg_plan.front() {
454+
/// `target` is the RG the decoder will emit next and must still be in the
455+
/// plan. A missing `target` means the decoder's frontier and `rg_plan` have
456+
/// diverged; we surface that as an internal error rather than silently
457+
/// draining the plan, which would truncate the scan. Kept free-standing on
458+
/// `rg_plan` (rather than `&mut self`) so the pop/guard logic is
459+
/// unit-testable without constructing a full stream state.
460+
fn advance_rg_plan_to(
461+
rg_plan: &mut VecDeque<RgPlanEntry>,
462+
target: usize,
463+
) -> Result<()> {
464+
while let Some(front) = rg_plan.front() {
460465
if front.rg_index == target {
461466
return Ok(());
462467
}
463-
self.rg_plan.pop_front();
468+
rg_plan.pop_front();
464469
}
465-
// Drained without finding `target`: the decoder frontier names an RG
466-
// our plan does not know, so decoder and plan have diverged. Surface it
467-
// rather than continuing on with a truncated (now empty) plan.
468470
internal_err!(
469471
"push decoder frontier RG {target} is not in rg_plan; \
470472
decoder and plan have diverged"
@@ -664,4 +666,32 @@ mod tests {
664666
assert!(!pruner.should_prune(&[1]));
665667
assert!(!pruner.should_prune(&[2]));
666668
}
669+
670+
#[test]
671+
fn advance_rg_plan_to_pops_up_to_target() {
672+
let mut plan: VecDeque<RgPlanEntry> = [0usize, 1, 2, 3]
673+
.into_iter()
674+
.map(|rg_index| RgPlanEntry { rg_index })
675+
.collect();
676+
PushDecoderStreamState::advance_rg_plan_to(&mut plan, 2).unwrap();
677+
assert_eq!(
678+
plan.iter().map(|e| e.rg_index).collect::<Vec<_>>(),
679+
vec![2, 3],
680+
"must pop the entries before `target` and stop at it",
681+
);
682+
}
683+
684+
#[test]
685+
fn advance_rg_plan_to_errors_when_target_absent() {
686+
let mut plan: VecDeque<RgPlanEntry> = [0usize, 1, 2]
687+
.into_iter()
688+
.map(|rg_index| RgPlanEntry { rg_index })
689+
.collect();
690+
let err = PushDecoderStreamState::advance_rg_plan_to(&mut plan, 5)
691+
.expect_err("a target absent from the plan must be an internal error");
692+
assert!(
693+
err.to_string().contains("diverged"),
694+
"expected a divergence internal error, got: {err}",
695+
);
696+
}
667697
}

0 commit comments

Comments
 (0)