1
0
mirror of https://github.com/tw93/Mole.git synced 2026-03-22 18:30:08 +00:00

fix(status): improve disk card display refs #551

This commit is contained in:
tw93
2026-03-08 23:46:46 +08:00
parent 24da1e2ac1
commit 8c53923ce8
4 changed files with 203 additions and 28 deletions

View File

@@ -22,6 +22,23 @@ var skipDiskMounts = map[string]bool{
"/dev": true,
}
var skipDiskFSTypes = map[string]bool{
"afpfs": true,
"autofs": true,
"cifs": true,
"devfs": true,
"fuse": true,
"fuseblk": true,
"fusefs": true,
"macfuse": true,
"nfs": true,
"osxfuse": true,
"procfs": true,
"smbfs": true,
"tmpfs": true,
"webdav": true,
}
func collectDisks() ([]DiskStatus, error) {
partitions, err := disk.Partitions(false)
if err != nil {
@@ -34,17 +51,7 @@ func collectDisks() ([]DiskStatus, error) {
seenVolume = make(map[string]bool)
)
for _, part := range partitions {
if strings.HasPrefix(part.Device, "/dev/loop") {
continue
}
if skipDiskMounts[part.Mountpoint] {
continue
}
if strings.HasPrefix(part.Mountpoint, "/System/Volumes/") {
continue
}
// Skip /private mounts.
if strings.HasPrefix(part.Mountpoint, "/private/") {
if shouldSkipDiskPartition(part) {
continue
}
baseDevice := baseDeviceName(part.Device)
@@ -97,6 +104,34 @@ func collectDisks() ([]DiskStatus, error) {
return disks, nil
}
func shouldSkipDiskPartition(part disk.PartitionStat) bool {
if strings.HasPrefix(part.Device, "/dev/loop") {
return true
}
if skipDiskMounts[part.Mountpoint] {
return true
}
if strings.HasPrefix(part.Mountpoint, "/System/Volumes/") {
return true
}
if strings.HasPrefix(part.Mountpoint, "/private/") {
return true
}
fstype := strings.ToLower(part.Fstype)
if skipDiskFSTypes[fstype] || strings.Contains(fstype, "fuse") {
return true
}
// On macOS, local disks should come from /dev. This filters sshfs/macFUSE-style
// mounts that can mirror the root volume and show up as duplicate internal disks.
if runtime.GOOS == "darwin" && part.Device != "" && !strings.HasPrefix(part.Device, "/dev/") {
return true
}
return false
}
var (
// External disk cache.
lastDiskCacheAt time.Time

View File

@@ -0,0 +1,60 @@
package main
import (
"testing"
"github.com/shirou/gopsutil/v4/disk"
)
func TestShouldSkipDiskPartition(t *testing.T) {
tests := []struct {
name string
part disk.PartitionStat
want bool
}{
{
name: "keep local apfs root volume",
part: disk.PartitionStat{
Device: "/dev/disk3s1s1",
Mountpoint: "/",
Fstype: "apfs",
},
want: false,
},
{
name: "skip macfuse mirror mount",
part: disk.PartitionStat{
Device: "kaku-local:/",
Mountpoint: "/Users/tw93/Library/Caches/dev.kaku/sshfs/kaku-local",
Fstype: "macfuse",
},
want: true,
},
{
name: "skip smb share",
part: disk.PartitionStat{
Device: "//server/share",
Mountpoint: "/Volumes/share",
Fstype: "smbfs",
},
want: true,
},
{
name: "skip system volume",
part: disk.PartitionStat{
Device: "/dev/disk3s5",
Mountpoint: "/System/Volumes/Data",
Fstype: "apfs",
},
want: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := shouldSkipDiskPartition(tt.part); got != tt.want {
t.Fatalf("shouldSkipDiskPartition(%+v) = %v, want %v", tt.part, got, tt.want)
}
})
}
}

View File

@@ -365,6 +365,8 @@ func renderDiskCard(disks []DiskStatus, io DiskIOStatus) cardData {
addGroup("EXTR", external)
if len(lines) == 0 {
lines = append(lines, subtleStyle.Render("No disks detected"))
} else if len(disks) == 1 {
lines = append(lines, formatDiskMetaLine(disks[0]))
}
}
readBar := ioBar(io.ReadRate)
@@ -398,8 +400,19 @@ func formatDiskLine(label string, d DiskStatus) string {
}
bar := progressBar(d.UsedPercent)
used := humanBytesShort(d.Used)
total := humanBytesShort(d.Total)
return fmt.Sprintf("%-6s %s %5.1f%%, %s/%s", label, bar, d.UsedPercent, used, total)
free := uint64(0)
if d.Total > d.Used {
free = d.Total - d.Used
}
return fmt.Sprintf("%-6s %s %s used, %s free", label, bar, used, humanBytesShort(free))
}
func formatDiskMetaLine(d DiskStatus) string {
parts := []string{humanBytesShort(d.Total)}
if d.Fstype != "" {
parts = append(parts, strings.ToUpper(d.Fstype))
}
return fmt.Sprintf("Total %s", strings.Join(parts, " · "))
}
func ioBar(rate float64) string {

View File

@@ -752,26 +752,49 @@ func TestFormatDiskLine(t *testing.T) {
name string
label string
disk DiskStatus
wantUsed string
wantFree string
wantNoSubstr string
}{
{
name: "empty label defaults to DISK",
label: "",
disk: DiskStatus{UsedPercent: 50.5, Used: 100 << 30, Total: 200 << 30},
wantUsed: "100G used",
wantFree: "100G free",
wantNoSubstr: "%",
},
{
name: "internal disk",
label: "INTR",
disk: DiskStatus{UsedPercent: 67.2, Used: 336 << 30, Total: 500 << 30},
wantUsed: "336G used",
wantFree: "164G free",
wantNoSubstr: "%",
},
{
name: "external disk",
label: "EXTR1",
disk: DiskStatus{UsedPercent: 85.0, Used: 850 << 30, Total: 1000 << 30},
wantUsed: "850G used",
wantFree: "150G free",
wantNoSubstr: "%",
},
{
name: "low usage",
label: "INTR",
disk: DiskStatus{UsedPercent: 15.3, Used: 15 << 30, Total: 100 << 30},
wantUsed: "15G used",
wantFree: "85G free",
wantNoSubstr: "%",
},
{
name: "used exceeds total clamps free to zero",
label: "INTR",
disk: DiskStatus{UsedPercent: 110.0, Used: 110 << 30, Total: 100 << 30},
wantUsed: "110G used",
wantFree: "0 free",
wantNoSubstr: "%",
},
}
@@ -789,10 +812,54 @@ func TestFormatDiskLine(t *testing.T) {
if !contains(got, expectedLabel) {
t.Errorf("formatDiskLine(%q, ...) = %q, should contain label %q", tt.label, got, expectedLabel)
}
if !contains(got, tt.wantUsed) {
t.Errorf("formatDiskLine(%q, ...) = %q, should contain used value %q", tt.label, got, tt.wantUsed)
}
if !contains(got, tt.wantFree) {
t.Errorf("formatDiskLine(%q, ...) = %q, should contain free value %q", tt.label, got, tt.wantFree)
}
if tt.wantNoSubstr != "" && contains(got, tt.wantNoSubstr) {
t.Errorf("formatDiskLine(%q, ...) = %q, should not contain %q", tt.label, got, tt.wantNoSubstr)
}
})
}
}
func TestRenderDiskCardAddsMetaLineForSingleDisk(t *testing.T) {
card := renderDiskCard([]DiskStatus{{
UsedPercent: 28.4,
Used: 263 << 30,
Total: 926 << 30,
Fstype: "apfs",
}}, DiskIOStatus{ReadRate: 0, WriteRate: 0.1})
if len(card.lines) != 4 {
t.Fatalf("renderDiskCard() single disk expected 4 lines, got %d", len(card.lines))
}
meta := stripANSI(card.lines[1])
if meta != "Total 926G · APFS" {
t.Fatalf("renderDiskCard() single disk meta line = %q, want %q", meta, "Total 926G · APFS")
}
}
func TestRenderDiskCardDoesNotAddMetaLineForMultipleDisks(t *testing.T) {
card := renderDiskCard([]DiskStatus{
{UsedPercent: 28.4, Used: 263 << 30, Total: 926 << 30, Fstype: "apfs"},
{UsedPercent: 50.0, Used: 500 << 30, Total: 1000 << 30, Fstype: "apfs"},
}, DiskIOStatus{})
if len(card.lines) != 4 {
t.Fatalf("renderDiskCard() multiple disks expected 4 lines, got %d", len(card.lines))
}
for _, line := range card.lines {
if stripANSI(line) == "Total 926G · APFS" || stripANSI(line) == "Total 1000G · APFS" {
t.Fatalf("renderDiskCard() multiple disks should not add meta line, got %q", line)
}
}
}
func TestGetScoreStyle(t *testing.T) {
tests := []struct {
name string