fix: fallback to df on incompatible system (#880)

This commit is contained in:
lollipopkit🏳️‍⚧️
2025-09-01 23:32:20 +08:00
committed by GitHub
parent 4ec7f5895e
commit 8e4c2a7cde
3 changed files with 356 additions and 49 deletions

View File

@@ -81,6 +81,138 @@ void main() {
expect(usage.usedPercent, 50);
// This would use the "unknown" fallback for kname
});
test('parse df -k output (fallback mode)', () {
final disks = Disk.parse(_dfOutput);
expect(disks, isNotEmpty);
expect(disks.length, 3); // Should find 3 valid filesystems: udev, /dev/vda3, /dev/vda2
// Verify root filesystem
final rootFs = disks.firstWhere((disk) => disk.mount == '/');
expect(rootFs.path, '/dev/vda3');
expect(rootFs.usedPercent, 47);
expect(rootFs.size, BigInt.from(40910528 ~/ 1024)); // df -k output divided by 1024 = MB
expect(rootFs.used, BigInt.from(18067948 ~/ 1024));
expect(rootFs.avail, BigInt.from(20951380 ~/ 1024));
// Verify boot/efi filesystem
final efiFs = disks.firstWhere((disk) => disk.mount == '/boot/efi');
expect(efiFs.path, '/dev/vda2');
expect(efiFs.usedPercent, 7);
expect(efiFs.size, BigInt.from(192559 ~/ 1024));
// Verify udev filesystem is included (virtual filesystem)
final udevFs = disks.firstWhere((disk) => disk.path == 'udev');
expect(udevFs.mount, '/dev');
expect(udevFs.usedPercent, 0);
expect(udevFs.size, BigInt.from(864088 ~/ 1024));
});
test('handle empty input gracefully', () {
final disks = Disk.parse('');
expect(disks, isEmpty);
});
test('handle whitespace-only input', () {
final disks = Disk.parse(' \n\t \r\n ');
expect(disks, isEmpty);
});
test('handle JSON with null filesystem fields', () {
final disks = Disk.parse(_jsonWithNullFields);
expect(disks, isNotEmpty);
// Should handle null filesystem fields gracefully
final disk = disks.firstWhere((disk) => disk.mount == '/');
expect(disk.size, BigInt.zero);
expect(disk.used, BigInt.zero);
expect(disk.avail, BigInt.zero);
expect(disk.usedPercent, 0);
});
test('handle JSON with string "null" values', () {
final disks = Disk.parse(_jsonWithStringNulls);
expect(disks, isNotEmpty);
// Should handle string "null" filesystem fields gracefully
final disk = disks.firstWhere((disk) => disk.mount == '/');
expect(disk.size, BigInt.zero);
expect(disk.used, BigInt.zero);
expect(disk.avail, BigInt.zero);
expect(disk.usedPercent, 0);
});
test('handle JSON with empty string values', () {
final disks = Disk.parse(_jsonWithEmptyStrings);
expect(disks, isNotEmpty);
// Should handle empty string filesystem fields gracefully
final disk = disks.firstWhere((disk) => disk.mount == '/');
expect(disk.size, BigInt.zero);
expect(disk.used, BigInt.zero);
expect(disk.avail, BigInt.zero);
expect(disk.usedPercent, 0);
});
test('handle JSON with invalid percentage format', () {
final disks = Disk.parse(_jsonWithInvalidPercent);
expect(disks, isNotEmpty);
// Should handle invalid percentage gracefully
final disk = disks.firstWhere((disk) => disk.mount == '/');
expect(disk.usedPercent, 0);
});
test('handle JSON with malformed numbers', () {
final disks = Disk.parse(_jsonWithMalformedNumbers);
expect(disks, isNotEmpty);
// Should handle malformed numbers gracefully
final disk = disks.firstWhere((disk) => disk.mount == '/');
expect(disk.size, BigInt.zero);
expect(disk.used, BigInt.zero);
expect(disk.avail, BigInt.zero);
});
test('handle JSON parsing errors gracefully', () {
final disks = Disk.parse(_malformedJson);
expect(disks, isEmpty); // Should fallback to legacy method, which also fails
});
test('handle df output with missing fields', () {
final disks = Disk.parse(_dfWithMissingFields);
expect(disks, isNotEmpty);
// Should handle missing fields gracefully
final disk = disks.firstWhere((disk) => disk.mount == '/');
expect(disk.usedPercent, 47);
});
test('handle df output with inconsistent formatting', () {
final disks = Disk.parse(_dfWithInconsistentFormatting);
expect(disks, isNotEmpty);
// Should handle inconsistent formatting
expect(disks.length, greaterThan(0));
});
test('handle lsblk with success marker', () {
final disks = Disk.parse(_lsblkWithSuccessMarker);
expect(disks, isNotEmpty);
// Should parse JSON and ignore success marker
final rootFs = disks.firstWhere((disk) => disk.mount == '/');
expect(rootFs.fsTyp, 'ext4');
expect(rootFs.usedPercent, 56);
});
test('handle malformed lsblk output fallback', () {
final disks = Disk.parse(_malformedLsblkWithDfFallback);
expect(disks, isNotEmpty);
// Should fallback to df -k parsing when lsblk output is malformed
expect(disks.length, 3);
});
});
}
@@ -278,3 +410,151 @@ overlay 1907116416 5470
v2000pro/pve 1906694784 125440 1906569344 1% /mnt/v2000pro/pve
v2000pro/download 1906569472 128 1906569344 1% /mnt/v2000pro/download''',
];
const _dfOutput = '''
Filesystem 1K-blocks Used Available Use% Mounted on
udev 864088 0 864088 0% /dev
tmpfs 176724 688 176036 1% /run
/dev/vda3 40910528 18067948 20951380 47% /
tmpfs 883612 0 883612 0% /dev/shm
tmpfs 5120 0 5120 0% /run/lock
/dev/vda2 192559 11807 180752 7% /boot/efi
tmpfs 176720 104 176616 1% /run/user/1000
''';
// Test data for edge cases
const _jsonWithNullFields = '''
{
"blockdevices": [
{
"fstype": "ext4",
"mountpoint": "/",
"fssize": null,
"fsused": null,
"fsavail": null,
"fsuse%": null,
"path": "/dev/sda1"
}
]
}
''';
const _jsonWithStringNulls = '''
{
"blockdevices": [
{
"fstype": "ext4",
"mountpoint": "/",
"fssize": "null",
"fsused": "null",
"fsavail": "null",
"fsuse%": "null",
"path": "/dev/sda1"
}
]
}
''';
const _jsonWithEmptyStrings = '''
{
"blockdevices": [
{
"fstype": "ext4",
"mountpoint": "/",
"fssize": "",
"fsused": "",
"fsavail": "",
"fsuse%": "",
"path": "/dev/sda1"
}
]
}
''';
const _jsonWithInvalidPercent = '''
{
"blockdevices": [
{
"fstype": "ext4",
"mountpoint": "/",
"fssize": "1000000",
"fsused": "500000",
"fsavail": "500000",
"fsuse%": "invalid_percent",
"path": "/dev/sda1"
}
]
}
''';
const _jsonWithMalformedNumbers = '''
{
"blockdevices": [
{
"fstype": "ext4",
"mountpoint": "/",
"fssize": "not_a_number",
"fsused": "invalid",
"fsavail": "broken",
"fsuse%": "50%",
"path": "/dev/sda1"
}
]
}
''';
const _malformedJson = '''
{
"blockdevices": [
{
"fstype": "ext4",
"mountpoint": "/",
"fssize": "1000000",
"fsused": "500000",
"fsavail": "500000",
"fsuse%": "50%",
"path": "/dev/sda1"
}
]
// Missing closing brace and malformed structure
''';
const _dfWithMissingFields = '''
Filesystem 1K-blocks Used Available Use% Mounted on
/dev/vda3 40910528 18067948 20951380 47% /
''';
const _dfWithInconsistentFormatting = '''
Filesystem 1K-blocks Used Available Use% Mounted on
/dev/sda1 1000000 500000 500000 50% /
/dev/sda2 2000000 1000000 1000000 50% /home
udev 864088 0 864088 0% /dev
''';
const _lsblkWithSuccessMarker = '''
{
"blockdevices": [
{
"fstype": "ext4",
"mountpoint": "/",
"fssize": 982141468672,
"fsused": 552718364672,
"fsavail": 379457622016,
"fsuse%": "56%",
"path": "/dev/sda1"
}
]
}
LSBLK_SUCCESS
''';
const _malformedLsblkWithDfFallback = '''
Filesystem 1K-blocks Used Available Use% Mounted on
udev 864088 0 864088 0% /dev
tmpfs 176724 688 176036 1% /run
/dev/vda3 40910528 18067948 20951380 47% /
tmpfs 883612 0 883612 0% /dev/shm
tmpfs 5120 0 5120 0% /run/lock
/dev/vda2 192559 11807 180752 7% /boot/efi
tmpfs 176720 104 176616 1% /run/user/1000
''';