Fix multi-copy car handling: track all configs per guid and add copy count
- Change owned_configs from HashMap<String, String> to HashMap<String, Vec<String>> to capture all configs across copies (previously last-write-wins lost data) - Add copy_count field to MyCarsData and CarCounts for wishlist copy-coverage logic - Fix improve.csv to exclude already-owned final configs as targets (e.g., Fiat Campagnola 1974 now targets 332 instead of already-owned 323) - Fix wishlist to check all owned configs for exact-match and final-build detection - Fix trash to check all configs for final-build-not-best detection - Update all tests for new Vec type and add multi-copy improve test
This commit is contained in:
parent
b24b274d25
commit
62688926c3
7 changed files with 494 additions and 83 deletions
|
|
@ -3,6 +3,7 @@
|
|||
//! These tests exercise the full analysis pipeline with fixture data,
|
||||
//! verifying end-to-end correctness from parsing through CSV output.
|
||||
|
||||
use std::collections::HashMap;
|
||||
use std::fs;
|
||||
use std::io::Write;
|
||||
|
||||
|
|
@ -20,7 +21,7 @@ use top_drives::{
|
|||
fn test_full_pipeline_empty_input() {
|
||||
let ctx = AnalysisContext::new();
|
||||
let car_list: Vec<Car> = vec![];
|
||||
let counts = compute_car_counts(&ctx, car_list, empty_my_data());
|
||||
let counts = compute_car_counts(&ctx, car_list, empty_my_data(), HashMap::new());
|
||||
|
||||
assert!(counts.by_rid.is_empty());
|
||||
assert!(counts.guid_to_rid.is_empty());
|
||||
|
|
@ -38,7 +39,7 @@ fn test_full_pipeline_single_car_multiple_tracks() {
|
|||
}
|
||||
|
||||
let car = make_car("Ferrari_F40", 95, "guid_f40");
|
||||
let counts = compute_car_counts(&ctx, vec![car], empty_my_data());
|
||||
let counts = compute_car_counts(&ctx, vec![car], empty_my_data(), HashMap::new());
|
||||
|
||||
assert_eq!(counts.by_rid.len(), 1);
|
||||
let (rec, count) = counts.by_rid.get(&("Ferrari_F40".to_string(), "332".to_string())).unwrap();
|
||||
|
|
@ -82,7 +83,7 @@ fn test_full_pipeline_multiple_cars_different_rq() {
|
|||
make_car("Car_C", 100, "guid_c"),
|
||||
];
|
||||
|
||||
let counts = compute_car_counts(&ctx, car_list, empty_my_data());
|
||||
let counts = compute_car_counts(&ctx, car_list, empty_my_data(), HashMap::new());
|
||||
|
||||
assert_eq!(counts.by_rid.len(), 3);
|
||||
|
||||
|
|
@ -213,7 +214,7 @@ fn test_ownership_filtering_in_counts() {
|
|||
let car_list =
|
||||
vec![make_car("Owned_Car", 85, "guid_owned"), make_car("Unowned_Car", 90, "guid_unowned")];
|
||||
|
||||
let counts = compute_car_counts(&ctx, car_list, empty_my_data());
|
||||
let counts = compute_car_counts(&ctx, car_list, empty_my_data(), HashMap::new());
|
||||
|
||||
// Both should be in by_rid
|
||||
assert_eq!(counts.by_rid.len(), 2);
|
||||
|
|
@ -258,8 +259,8 @@ fn test_load_my_cars_data_with_fixture() {
|
|||
|
||||
// owned_configs only for locked cards with complete tune data
|
||||
assert_eq!(data.owned_configs.len(), 2);
|
||||
assert_eq!(data.owned_configs.get("guid_a"), Some(&"323".to_string()));
|
||||
assert_eq!(data.owned_configs.get("guid_c"), Some(&"111".to_string()));
|
||||
assert_eq!(data.owned_configs.get("guid_a"), Some(&vec!["323".to_string()]));
|
||||
assert_eq!(data.owned_configs.get("guid_c"), Some(&vec!["111".to_string()]));
|
||||
// Unlocked card should not be in owned_configs
|
||||
assert!(!data.owned_configs.contains_key("guid_b"));
|
||||
|
||||
|
|
@ -290,7 +291,7 @@ fn test_load_my_cars_data_incomplete_tune_data() {
|
|||
|
||||
// Only the card with complete tune data has a config entry
|
||||
assert_eq!(data.owned_configs.len(), 1);
|
||||
assert_eq!(data.owned_configs.get("guid_full"), Some(&"233".to_string()));
|
||||
assert_eq!(data.owned_configs.get("guid_full"), Some(&vec!["233".to_string()]));
|
||||
assert!(!data.owned_configs.contains_key("guid_partial"));
|
||||
assert!(!data.owned_configs.contains_key("guid_empty"));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
//! Property-based tests using proptest for sorting and comparison functions.
|
||||
|
||||
use std::collections::HashMap;
|
||||
use proptest::prelude::*;
|
||||
|
||||
use top_drives::test_utils::{make_car, make_record};
|
||||
|
|
@ -177,7 +178,7 @@ proptest! {
|
|||
cars in proptest::collection::vec(car_strategy(), 0..20)
|
||||
) {
|
||||
let ctx = AnalysisContext::default();
|
||||
let counts = compute_car_counts(&ctx, cars.clone(), empty_my_data());
|
||||
let counts = compute_car_counts(&ctx, cars.clone(), empty_my_data(), HashMap::new());
|
||||
// No best_cars entries → every car has 0 wins → by_rid is empty
|
||||
assert_eq!(counts.by_rid.len(), 0);
|
||||
// guid_to_rid is also empty since no car won any track
|
||||
|
|
@ -212,7 +213,7 @@ proptest! {
|
|||
}
|
||||
|
||||
let car_list = vec![make_car(&rid, rq, &guid)];
|
||||
let counts = compute_car_counts(&ctx, car_list, empty_my_data());
|
||||
let counts = compute_car_counts(&ctx, car_list, empty_my_data(), HashMap::new());
|
||||
|
||||
let entry = counts.by_rid.get(&(rid.clone(), "332".to_string()));
|
||||
assert!(entry.is_some(), "car should appear in by_rid");
|
||||
|
|
@ -240,7 +241,7 @@ proptest! {
|
|||
);
|
||||
|
||||
let car_list = vec![make_car(&rid, rq, &guid)];
|
||||
let counts = compute_car_counts(&ctx, car_list, empty_my_data());
|
||||
let counts = compute_car_counts(&ctx, car_list, empty_my_data(), HashMap::new());
|
||||
|
||||
let (record, _) = counts.by_rid.get(&(rid, "332".to_string())).unwrap();
|
||||
assert_eq!(record.owned, owned, "owned flag should propagate");
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue