diff --git a/AGENTS.md b/AGENTS.md index 1399558..5d1de56 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -319,7 +319,7 @@ boş alandan düşmedi"* — 20 GB'lik disk 39 GB kullanıyor gibi görünüyord | Yer | Kural | |---|---| | `has_symbolic_remaining(parts, disk)` | **TEK KAYNAK.** `!delete && size_mb == 0 && orig_device.is_empty()` | -| tablo (`build_table_rows`) | sembolik varsa boş alan satırı **çizilmez** — o alan o tanıma ait | +| tablo (`build_table_rows`) | sembolik varsa boş alan satırı **çizilmez** — o alan o tanıma ait. Koşul **`show_free`** adıyla TEK yerde tanımlı ve **üç basış yerinin de** ortak koşulu (aşağıdaki REGRESYON 3'e bak) | | çubuk (`manual_dual_bars`) | sembolik varsa serbest segment **eklenmez** | | `manual_capacity_error` | sembolik varsa `fixed_mb >= cap_mb` reddedilir | @@ -331,6 +331,28 @@ kalanın tamamını alır, ikincisine hiç yer kalmaz. `orig_device` dolu (taranmış) 0 boyutlu bölüm sembolik **DEĞİLDİR**: sıfır boyut "kalanın tamamı" anlamına gelmez. +#### ⭐ Koşul YER YER yazılırsa bu sınıf hata tekrarlar + +Bu tabloda bir hata **üç kez** yaşandı ve üçü de aynı sebepten: +`has_symbolic` kontrolü satırın **yalnızca bir** basış yerine +eklenmişti. + +1. `else if` dalı vardı, diskte tanım yokken çalışıyordu → + tanım varsa satır hiç basılmıyordu (`sda1` + 15 GB boş) +2. `else if` düzeltildi, **döngü içindeki** basış unutuldu → + `19.8 GB` bölüm + `19.8 GB` boş alan +3. döngü içi de düzeltildi ama **koşul üç yere ayrı ayrı yazıldı** + +**Ders:** aynı koşulu birden çok yerde yazma. Tek bir +`show_free = free_total_mb > 0 && !has_symbolic` değişkeni hesaplanıp +**üç basış yerinin de** koşulu olarak kullanılıyor. Kural tek +yerde; onu atlamak ya da çoğaltmak imkânsız. + +> Bu, "sabit bir sayıyı 3 yere yazma" değil — "bir *koşulu* 3 +> yere yazma". Sabit sayıyı çoğaltmak hataya açıktır ama en azından +> yanlışsa sayı yanlış görünür; koşulu çoğaltmak bazı basış +> yerlerinde **sessizce yanlış** davranış üretir. + ### ⭐ Bağlanmayan dosya sistemleri — mountpoint isteğe bağlı `mountpoint_required(editing, fstype)` üç dosya sisteminde `false` @@ -669,11 +691,11 @@ nedeni yorumla sabitlendi. Log bile "MiB" derken argüman "MB" gönderiyordu. ``` cargo check --all-targets : 0 hata, 0 uyarı -cargo test : 143/143 (test modülü sayısı arttıkça güncel tut) +cargo test : 145/145 (test modülü sayısı arttıkça güncel tut) cargo clippy : ~77 uyarı, tamamı stil (collapsed_if, replace zinciri) ``` -Bu oturumda 1 → 143 test. Test modülü olmayan dosyalar (`users.rs`, +Bu oturumda 1 → 145 test. Test modülü olmayan dosyalar (`users.rs`, `location.rs`, `rescue.rs`, `funct.rs`, `autoinstall/mod.rs`, `main.rs`) içine modüller eklendi. Çoklu test modülü paylaşılan yardımcıları `steps/partition.rs::testutil` altındadır (`disk()`, `part()`). diff --git a/src/steps/partition.rs b/src/steps/partition.rs index d0b3584..e3cce2a 100644 --- a/src/steps/partition.rs +++ b/src/steps/partition.rs @@ -762,8 +762,12 @@ fn build_table_rows<'a>( .map(|p| p.size_mb) .sum(); let free_total_mb = disk_mb.saturating_sub(used_total); - // "Kalan Alan" tanımı kalanın tamamını alır → boş alan satırı çizilmez. + // "Kalan Alan" tanımı kalanın tamamını alır → boş alan satırı + // çizilmez. ⚠️ Bu koşul ÜÇ basış yerinin de ORTAK koşuludur. + // Sadece sondakine uygulanırsa diğer ikisi sembolik tanımı + // görmez (aşağıdaki REGRESYON 3). let has_symbolic = has_symbolic_remaining(parts, active_disk); + let show_free = free_total_mb > 0 && !has_symbolic; let mut fs_first: Option = None; // Boş alan satırı disk başına TEK'tir (GParted tarzı: silinenler @@ -779,7 +783,7 @@ fn build_table_rows<'a>( } // İşaret bloğunun bittiği yerde boş-alan satırını bas if let Some(first) = fs_first.take() { - if free_total_mb > 0 { + if show_free { visible_rows.push(TableRow::FreeSpace { disk: active_disk.to_string(), total_mb: free_total_mb, @@ -794,14 +798,14 @@ fn build_table_rows<'a>( if let Some(first) = fs_first.take() { // Son ifade olduğu için `free_row_shown` burada güncellenmez; // zaten sonrasında okunacak bir dal yok. - if free_total_mb > 0 { + if show_free { visible_rows.push(TableRow::FreeSpace { disk: active_disk.to_string(), total_mb: free_total_mb, first_idx: Some(first), }); } - } else if free_total_mb > 0 && !free_row_shown && !has_symbolic { + } else if show_free && !free_row_shown { // Disk SONUNDAKİ boş alan — diskte tanım olsun ya da olmasın. // // REGRESYON: koşul `all(|p| p.disk != active_disk)` idi, yani @@ -809,14 +813,27 @@ fn build_table_rows<'a>( // Diskte tanım var ama hiçbiri silinmek işaretli değilse // (`sda1` + 15 GB boş) satır hiç basılmıyordu. // - // `has_symbolic` kuralı: "Kalan Alan" tanımı varsa boş alan o - // tanıma aittir. REGRESYON 2: sembolik tanım (`size_mb == 0`) - // hiçbir hesaba katılmadığı için hem bölüm satırı hem boş alan - // satırı kalanın tamamını gösteriyordu — 20 GB'lik disk - // 39 GB kullanıyormuş gibi görünüyordu. Kullanıcı raporu: - // "eklenen bölümün 19.8 gb demesine rağmen bunu boş alandan + // REGRESYON 2: sembolik tanım (`size_mb == 0`) hiçbir hesaba + // katılmadığı için hem bölüm satırı hem boş alan satırı + // kalanın tamamını gösteriyordu — 20 GB'lik disk 39 GB + // kullanıyor gibi görünüyordu. Kullanıcı raporu: // düşmedi". // + // düşmedi". + // + // REGRESYON 3 (kullanıcı: "bir alanı sildim boş alan olarak + // doğru okudu, sonrasında o boş alanı yeni bölüm diyerek / + // dizin yaptım… ama boş alan duruyor yerinde, yeni oluşturduğumda + // duruyor"): REGRESYON 2'nin düzeltmesi YALNIZCA bu `else if` + // dalına uygulanmıştı. Silinen bölümün yerine **döngü içinde** + // basılan satır `has_symbolic`'e BAKMIYORDU. `/` "Kalan Alan" + // olduğu için semboliktir ve alanın tamamını alır; buna rağmen + // satır diskin tamamını "boş" gösteriyordu. + // + // Çözüm: `show_free` TEK koşul ve üç basış yerinin de ortak + // koşulu. Koşulu yer yer yazmak bu sınıf hatayı kaçınılmaz + // kılıyordu — üçüncü kez düzeltmek zorunda kaldık. + // // `first_idx: None` burada "seçilemez" DEĞİL demektir; yalnızca // temsil ettiği silinmiş bir tanım yoktur. Tıklanabilirdir // (`global_idx == None` dalı `selected_free` tutar ve @@ -4027,6 +4044,61 @@ mod free_space_tests { // ── "Kalan Alan" (sembolik) tanımlar ─────────────────────── + /// ⭐ Kullanıcının senaryosu: bir alanı sildi → boş alan DOĞRU + /// göründü → o boş alandan "Yeni Bölüm" ile `/` yaptı → boş alan + /// **yerinde durdu**, hem de yeni bölümün yanında. + /// + /// REGRESYON: sembolik tanım kuralı (`has_symbolic`) yalnızca + /// sondaki `else if` dalına uygulanmıştı. Silinen bölümün yerine + /// **döngü içinde** basılan satır kuralı görmüyordu. `/` "Kalan + /// Alan" olduğu için semboliktir (alanın tamamını alır) ama satır + /// yine de diskin tamamını "boş" gösteriyordu. + #[test] + fn creating_root_in_a_freed_area_consumes_the_free_row() { + let mut dead = part("/dev/sdb2", 100_000, FsType::Ext4, "/"); + dead.delete = true; // kullanıcı sildi + let mut root = part("/dev/sdb3", 0, FsType::Ext4, "/"); + root.orig_device = String::new(); // "Kalan Alan" → sembolik + let parts = vec![ + part("/dev/sdb1", 1_024, FsType::Fat32, "/boot/efi"), + dead, + root, + ]; + let rows = build_table_rows(&parts, "/dev/sdb", 500_000); + + assert!( + free_mb_of(&rows).is_empty(), + "sembolik / alanın tamamını alıyor, boş alan satırı kalmamalı: {:?}", + free_mb_of(&rows) + ); + assert_eq!( + rows.len(), + 2, + "sdb1 + yeni / olmalı, boş alan satırı olmamalı: {}", + rows.len() + ); + } + + /// Aynı senaryo ama yerine SABİT boyutlu bölüm konursa boş alan + /// **kalır** ve miktar doğru düşer — sembolik değil, gerçek yer + /// vardır. + #[test] + fn fixed_size_partition_in_a_freed_area_keeps_a_correct_free_row() { + let mut dead = part("/dev/sdb2", 100_000, FsType::Ext4, "/"); + dead.delete = true; + let mut data = part("/dev/sdb3", 50_000, FsType::Ntfs, ""); + data.orig_device = String::new(); + let parts = vec![dead, data]; + let rows = build_table_rows(&parts, "/dev/sdb", 500_000); + + // 500_000 − 50_000 = 450_000 boş kalmalı + assert_eq!( + free_mb_of(&rows), + vec![450_000], + "sabit boyutlu bölüm boş alanı düşürmeli, satır kalmalı" + ); + } + /// Kullanıcı raporu: "eklenen bölümün 19.8 gb demesine rağmen /// bunu boş alandan düşmedi". ///