This repository has no description
0

Configure Feed

Select the types of activity you want to include in your feed.

core / knot2 / crates / knot-maintenance / tests / geometric.rs
4.0 kB 129 lines
1use std::collections::BTreeSet; 2 3use knot_git::Repo; 4use knot_maintenance::{GeometricFactor, Options, PruneGrace, run_repo}; 5use knot_types::{ObjectFormat, Oid, RefName}; 6 7mod common; 8use common::{ 9 EMPTY_TREE_SHA1, chain, create_repo, delete_ref, empty_tree, fsck_clean, git_available, 10 has_cruft_pack, idx_stems, midx_verifies, now, options, reachable_objects, set_ref, 11}; 12 13fn opts(factor: u64, grace: PruneGrace) -> Options { 14 Options { 15 geometric_factor: GeometricFactor::new(factor), 16 prune_grace: grace, 17 commit_graph: false, 18 bitmap: false, 19 ..options() 20 } 21} 22 23fn main_ref(repo: &Repo) -> Option<Oid> { 24 repo.find_ref(&RefName::new("refs/heads/main").unwrap()) 25 .unwrap() 26} 27 28fn no_reachable_loss(format: ObjectFormat) { 29 if !git_available() { 30 eprintln!("skipping geometric differential: git unavailable"); 31 return; 32 } 33 let scan = tempfile::tempdir().unwrap(); 34 let empty = empty_tree(format); 35 let repo = create_repo(scan.path(), format, "did:plc:scallop"); 36 let opts = opts(2, PruneGrace::from_secs(86_400)); 37 38 (0..6).fold((None, BTreeSet::new()), |(tip, seen), round| { 39 let next = chain(&repo, empty, round * 2..round * 2 + 2, tip); 40 set_ref(&repo, "refs/heads/main", next); 41 run_repo(&repo, now(), &opts).unwrap(); 42 43 let r = Repo::open(repo.git().git_dir()).unwrap(); 44 assert!(fsck_clean(&r), "{format:?} r{round}: not fsck-clean"); 45 if let Some((ok, stderr)) = midx_verifies(&r) { 46 assert!(ok, "{format:?} r{round} midx: {stderr}"); 47 } 48 assert_eq!( 49 main_ref(&r), 50 Some(next), 51 "{format:?} r{round}: main lost its tip" 52 ); 53 let present = reachable_objects(&r); 54 assert!( 55 seen.is_subset(&present), 56 "{format:?} r{round}: a reachable object went missing" 57 ); 58 (Some(next), present) 59 }); 60} 61 62#[test] 63fn geometric_no_reachable_loss_sha1() { 64 no_reachable_loss(ObjectFormat::SHA1); 65} 66 67#[test] 68fn geometric_no_reachable_loss_sha256() { 69 no_reachable_loss(ObjectFormat::SHA256); 70} 71 72#[test] 73fn geometric_keeps_the_large_pack_while_rolling_up_then_crufting_small_packs() { 74 if !git_available() { 75 eprintln!("skipping geometric behavior test: git unavailable"); 76 return; 77 } 78 let scan = tempfile::tempdir().unwrap(); 79 let repo = create_repo(scan.path(), ObjectFormat::SHA1, "did:plc:conch"); 80 let opts = opts(2, PruneGrace::from_secs(86_400)); 81 82 let big = chain(&repo, EMPTY_TREE_SHA1, 0..10, None); 83 set_ref(&repo, "refs/heads/main", big); 84 run_repo(&repo, now(), &opts).unwrap(); 85 let large = idx_stems(&repo); 86 assert_eq!( 87 large.len(), 88 1, 89 "the initial repack settles to one large pack" 90 ); 91 92 let advanced = chain(&repo, EMPTY_TREE_SHA1, 100..102, Some(big)); 93 set_ref(&repo, "refs/heads/main", advanced); 94 run_repo(&repo, now(), &opts).unwrap(); 95 assert!( 96 large.is_subset(&idx_stems(&repo)), 97 "the large pack is kept verbatim" 98 ); 99 assert_eq!( 100 idx_stems(&repo).len(), 101 2, 102 "small additions roll into a second pack" 103 ); 104 105 let feature = chain(&repo, EMPTY_TREE_SHA1, 200..202, Some(advanced)); 106 set_ref(&repo, "refs/heads/feature", feature); 107 run_repo(&repo, now(), &opts).unwrap(); 108 delete_ref(&repo, "refs/heads/feature"); 109 110 let report = run_repo(&repo, now(), &opts).unwrap(); 111 assert!( 112 report.prune.crufted.get() >= 1, 113 "young rolled-up garbage is crufted" 114 ); 115 assert!( 116 large.is_subset(&idx_stems(&repo)) && has_cruft_pack(&repo), 117 "large pack untouched, cruft written" 118 ); 119 120 let reopened = Repo::open(repo.git().git_dir()).unwrap(); 121 assert!( 122 fsck_clean(&reopened), 123 "not fsck-clean after roll-up and cruft" 124 ); 125 assert!( 126 reopened.contains(advanced) && reopened.contains(feature), 127 "reachable tip and young orphan both survive" 128 ); 129}