mirror of
https://github.com/tw93/Mole.git
synced 2026-03-22 17:55:08 +00:00
fix(status): improve disk card display refs #551
This commit is contained in:
@@ -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
|
||||
|
||||
60
cmd/status/metrics_disk_test.go
Normal file
60
cmd/status/metrics_disk_test.go
Normal 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)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -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 {
|
||||
|
||||
@@ -749,29 +749,52 @@ func TestMiniBar(t *testing.T) {
|
||||
|
||||
func TestFormatDiskLine(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
label string
|
||||
disk DiskStatus
|
||||
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},
|
||||
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},
|
||||
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},
|
||||
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},
|
||||
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
|
||||
|
||||
Reference in New Issue
Block a user