@@ -233,6 +233,17 @@ impl Chunk {
233233}
234234
235235impl Chunking {
236+ /// Creates a reverse map from content IDs to checksums
237+ fn create_content_id_map (
238+ map : & IndexMap < String , ContentID > ,
239+ ) -> IndexMap < ContentID , Vec < & String > > {
240+ let mut rmap = IndexMap :: < ContentID , Vec < & String > > :: new ( ) ;
241+ for ( checksum, contentid) in map. iter ( ) {
242+ rmap. entry ( Rc :: clone ( contentid) ) . or_default ( ) . push ( checksum) ;
243+ }
244+ rmap
245+ }
246+
236247 /// Generate an initial single chunk.
237248 pub fn new ( repo : & ostree:: Repo , rev : & str ) -> Result < Self > {
238249 // Find the target commit
@@ -310,21 +321,17 @@ impl Chunking {
310321 return Ok ( ( ) ) ;
311322 }
312323
313- // Reverses `contentmeta.map` i.e. contentid -> Vec<checksum>
314- let mut rmap = IndexMap :: < ContentID , Vec < & String > > :: new ( ) ;
315- for ( checksum, contentid) in meta. map . iter ( ) {
316- rmap. entry ( Rc :: clone ( contentid) ) . or_default ( ) . push ( checksum) ;
317- }
318-
319324 // Create exclusive chunks first if specified
320325 let mut processed_specific_components = BTreeSet :: new ( ) ;
321326 if let Some ( specific_meta) = specific_contentmeta {
327+ let specific_map = Self :: create_content_id_map ( & specific_meta. map ) ;
328+
322329 for component in & specific_meta. sizes {
323330 let mut chunk = Chunk :: new ( & component. meta . name ) ;
324331 chunk. packages = vec ! [ component. meta. name. to_string( ) ] ;
325332
326333 // Move all objects belonging to this exclusive component
327- if let Some ( objects) = rmap . get ( & component. meta . identifier ) {
334+ if let Some ( objects) = specific_map . get ( & component. meta . identifier ) {
328335 for & obj in objects {
329336 self . remainder . move_obj ( & mut chunk, obj) ;
330337 }
@@ -353,6 +360,8 @@ impl Chunking {
353360 . cloned ( )
354361 . collect ( ) ;
355362
363+ let rmap = Self :: create_content_id_map ( & meta. map ) ;
364+
356365 // Process regular components with bin packing if we have remaining layers
357366 if let Some ( remaining) = NonZeroU32 :: new ( self . remaining ( ) ) {
358367 let start = Instant :: now ( ) ;
@@ -1221,4 +1230,183 @@ mod test {
12211230
12221231 Ok ( ( ) )
12231232 }
1233+
1234+ #[ test]
1235+ fn test_process_mapping_specific_components_contain_correct_objects ( ) -> Result < ( ) > {
1236+ // This test validates that specific components get their own dedicated layers
1237+ // and that their objects are properly isolated from regular package layers
1238+
1239+ // Setup: Create 5 packages
1240+ // - pkg1, pkg2: Will be marked as "specific components" (get their own layers)
1241+ // - pkg3, pkg4, pkg5: Regular packages (will be bin-packed together)
1242+ let packages = [
1243+ ( 1 , 100 , 50000 ) , // pkg1 - SPECIFIC COMPONENT
1244+ ( 2 , 200 , 40000 ) , // pkg2 - SPECIFIC COMPONENT
1245+ ( 3 , 300 , 30000 ) , // pkg3 - regular package
1246+ ( 4 , 400 , 20000 ) , // pkg4 - regular package
1247+ ( 5 , 500 , 10000 ) , // pkg5 - regular package
1248+ ] ;
1249+
1250+ let ( contentmeta, mut system_metadata, mut specific_components_meta, mut chunking) =
1251+ setup_exclusive_test ( & packages, 8 , None ) ?;
1252+
1253+ // Create object mappings
1254+ // - system_objects_map: Contains ALL objects in the system (both specific and regular)
1255+ // - specific_components_objects_map: Contains ONLY objects from specific components
1256+ let mut system_objects_map = IndexMap :: new ( ) ;
1257+ let mut specific_components_objects_map = IndexMap :: new ( ) ;
1258+
1259+ // SPECIFIC COMPONENT 1 (pkg1): owns 3 objects
1260+ let pkg1_objects = [ "checksum_1_a" , "checksum_1_b" , "checksum_1_c" ] ;
1261+ for obj in & pkg1_objects {
1262+ system_objects_map. insert ( obj. to_string ( ) , contentmeta[ 0 ] . meta . identifier . clone ( ) ) ;
1263+ specific_components_objects_map
1264+ . insert ( obj. to_string ( ) , contentmeta[ 0 ] . meta . identifier . clone ( ) ) ;
1265+ }
1266+
1267+ // SPECIFIC COMPONENT 2 (pkg2): owns 2 objects
1268+ let pkg2_objects = [ "checksum_2_a" , "checksum_2_b" ] ;
1269+ for obj in & pkg2_objects {
1270+ system_objects_map. insert ( obj. to_string ( ) , contentmeta[ 1 ] . meta . identifier . clone ( ) ) ;
1271+ specific_components_objects_map
1272+ . insert ( obj. to_string ( ) , contentmeta[ 1 ] . meta . identifier . clone ( ) ) ;
1273+ }
1274+
1275+ // REGULAR PACKAGE 1 (pkg3): owns 2 objects
1276+ let pkg3_objects = [ "checksum_3_a" , "checksum_3_b" ] ;
1277+ for obj in & pkg3_objects {
1278+ system_objects_map. insert ( obj. to_string ( ) , contentmeta[ 2 ] . meta . identifier . clone ( ) ) ;
1279+ }
1280+
1281+ // REGULAR PACKAGE 2 (pkg4): owns 1 object
1282+ let pkg4_objects = [ "checksum_4_a" ] ;
1283+ for obj in & pkg4_objects {
1284+ system_objects_map. insert ( obj. to_string ( ) , contentmeta[ 3 ] . meta . identifier . clone ( ) ) ;
1285+ }
1286+
1287+ // REGULAR PACKAGE 3 (pkg5): owns 3 objects
1288+ let pkg5_objects = [ "checksum_5_a" , "checksum_5_b" , "checksum_5_c" ] ;
1289+ for obj in & pkg5_objects {
1290+ system_objects_map. insert ( obj. to_string ( ) , contentmeta[ 4 ] . meta . identifier . clone ( ) ) ;
1291+ }
1292+
1293+ // Set up metadata
1294+ system_metadata. map = system_objects_map;
1295+ specific_components_meta. map = specific_components_objects_map;
1296+ specific_components_meta. sizes = vec ! [ contentmeta[ 0 ] . clone( ) , contentmeta[ 1 ] . clone( ) ] ;
1297+
1298+ // Initialize: Add ALL objects to the remainder chunk before processing
1299+ // This includes both specific component objects and regular package objects
1300+ // because process_mapping needs to move them from remainder to their final layers
1301+ for ( checksum, _) in & system_metadata. map {
1302+ chunking. remainder . content . insert (
1303+ RcStr :: from ( checksum. as_str ( ) ) ,
1304+ (
1305+ 1000 ,
1306+ vec ! [ Utf8PathBuf :: from( format!( "/path/to/{}" , checksum) ) ] ,
1307+ ) ,
1308+ ) ;
1309+ chunking. remainder . size += 1000 ;
1310+ }
1311+
1312+ // Process the mapping
1313+ // - system_metadata contains ALL objects in the system
1314+ // - specific_components_meta tells process_mapping which objects belong to specific components
1315+ chunking. process_mapping (
1316+ & system_metadata,
1317+ & Some ( NonZeroU32 :: new ( 8 ) . unwrap ( ) ) ,
1318+ None ,
1319+ Some ( & specific_components_meta) ,
1320+ ) ?;
1321+
1322+ // VALIDATION PART 1: Specific components get their own dedicated chunks
1323+ assert ! (
1324+ chunking. chunks. len( ) >= 2 ,
1325+ "Should have at least 2 chunks for specific components"
1326+ ) ;
1327+
1328+ // Specific Component Layer 1: pkg1 only
1329+ let specific_component_1_layer = & chunking. chunks [ 0 ] ;
1330+ assert_eq ! ( specific_component_1_layer. name, "pkg1" ) ;
1331+ assert_eq ! ( specific_component_1_layer. packages, vec![ "pkg1" ] ) ;
1332+ assert_eq ! ( specific_component_1_layer. content. len( ) , 3 ) ;
1333+ for obj in & pkg1_objects {
1334+ assert ! (
1335+ specific_component_1_layer. content. contains_key( * obj) ,
1336+ "Specific component 1 layer should contain {}" ,
1337+ obj
1338+ ) ;
1339+ }
1340+
1341+ // Specific Component Layer 2: pkg2 only
1342+ let specific_component_2_layer = & chunking. chunks [ 1 ] ;
1343+ assert_eq ! ( specific_component_2_layer. name, "pkg2" ) ;
1344+ assert_eq ! ( specific_component_2_layer. packages, vec![ "pkg2" ] ) ;
1345+ assert_eq ! ( specific_component_2_layer. content. len( ) , 2 ) ;
1346+ for obj in & pkg2_objects {
1347+ assert ! (
1348+ specific_component_2_layer. content. contains_key( * obj) ,
1349+ "Specific component 2 layer should contain {}" ,
1350+ obj
1351+ ) ;
1352+ }
1353+
1354+ // VALIDATION PART 2: Specific component layers contain NO regular package objects
1355+ for specific_layer in & chunking. chunks [ 0 ..2 ] {
1356+ for obj in pkg3_objects
1357+ . iter ( )
1358+ . chain ( & pkg4_objects)
1359+ . chain ( & pkg5_objects)
1360+ {
1361+ assert ! (
1362+ !specific_layer. content. contains_key( * obj) ,
1363+ "Specific component layer '{}' should NOT contain regular package object {}" ,
1364+ specific_layer. name,
1365+ obj
1366+ ) ;
1367+ }
1368+ }
1369+
1370+ // VALIDATION PART 3: Regular package layers contain NO specific component objects
1371+ let regular_package_layers = & chunking. chunks [ 2 ..] ;
1372+ for regular_layer in regular_package_layers {
1373+ for obj in pkg1_objects. iter ( ) . chain ( & pkg2_objects) {
1374+ assert ! (
1375+ !regular_layer. content. contains_key( * obj) ,
1376+ "Regular package layer should NOT contain specific component object {}" ,
1377+ obj
1378+ ) ;
1379+ }
1380+ }
1381+
1382+ // VALIDATION PART 4: All regular package objects are in some regular layer
1383+ let mut found_regular_objects = BTreeSet :: new ( ) ;
1384+ for regular_layer in regular_package_layers {
1385+ for ( obj, _) in & regular_layer. content {
1386+ found_regular_objects. insert ( obj. as_ref ( ) ) ;
1387+ }
1388+ }
1389+
1390+ for obj in pkg3_objects
1391+ . iter ( )
1392+ . chain ( & pkg4_objects)
1393+ . chain ( & pkg5_objects)
1394+ {
1395+ assert ! (
1396+ found_regular_objects. contains( * obj) ,
1397+ "Regular package object {} should be in some regular layer" ,
1398+ obj
1399+ ) ;
1400+ }
1401+
1402+ // VALIDATION PART 5: All objects moved from remainder
1403+ assert_eq ! (
1404+ chunking. remainder. content. len( ) ,
1405+ 0 ,
1406+ "All objects should be moved from remainder"
1407+ ) ;
1408+ assert_eq ! ( chunking. remainder. size, 0 , "Remainder size should be 0" ) ;
1409+
1410+ Ok ( ( ) )
1411+ }
12241412}
0 commit comments