//go:build linux package system import ( "os" "path/filepath" "testing" ) // TestReadMemInfoUsesCgroupV2Limit asserts F1: when a cgroup v2 memory.max caps the container well // below the host /proc/meminfo total, readMemInfo reports the cgroup cap (the guest's real ceiling), // not the host RAM. On the pre-fix code this test fails because readMemInfo ignored cgroup entirely. func TestReadMemInfoUsesCgroupV2Limit(t *testing.T) { dir := t.TempDir() // 2 GiB limit, 512 MiB current usage. const twoGiB = uint64(2 * 1024 * 1024 * 1024) const halfGiB = uint64(512 * 1024 * 1024) mustWriteFile(t, filepath.Join(dir, "memory.max"), []byte(itoa(twoGiB))) mustWriteFile(t, filepath.Join(dir, "memory.current"), []byte(itoa(halfGiB))) old := cgroupRoot cgroupRoot = dir defer func() { cgroupRoot = old }() var info SystemInfo readMemInfo(&info) // Host /proc/meminfo total is whatever the test machine has; the cgroup cap must win when smaller. if info.TotalMemMB != 2048 { t.Fatalf("TotalMemMB = %d, want 2048 (cgroup cap), not host RAM", info.TotalMemMB) } if info.UsedMemMB != 512 { t.Fatalf("UsedMemMB = %d, want 512 (memory.current)", info.UsedMemMB) } if info.AvailMemMB != 1536 { t.Fatalf("AvailMemMB = %d, want 1536", info.AvailMemMB) } if info.MemPercent < 24 || info.MemPercent > 26 { t.Fatalf("MemPercent = %.1f, want ~25", info.MemPercent) } } // TestReadMemInfoCgroupMaxIsUnlimited asserts that a v2 "max" sentinel does NOT override /proc/meminfo // (an uncapped container keeps the host view rather than a bogus 0). func TestReadMemInfoCgroupMaxIsUnlimited(t *testing.T) { dir := t.TempDir() mustWriteFile(t, filepath.Join(dir, "memory.max"), []byte("max")) old := cgroupRoot cgroupRoot = dir defer func() { cgroupRoot = old }() var info SystemInfo readMemInfo(&info) if info.TotalMemMB == 0 { t.Fatalf("TotalMemMB = 0 with an unlimited cgroup; expected the /proc/meminfo host total") } } // TestReadCgroupMemLimitV1Unlimited asserts the v1 near-uint64-max sentinel is treated as unlimited. func TestReadCgroupMemLimitV1Unlimited(t *testing.T) { dir := t.TempDir() memDir := filepath.Join(dir, "memory") if err := os.MkdirAll(memDir, 0o755); err != nil { t.Fatal(err) } // Typical v1 "unlimited" value. mustWriteFile(t, filepath.Join(memDir, "memory.limit_in_bytes"), []byte("9223372036854771712")) if _, ok := readCgroupMemLimitMB(dir); ok { t.Fatalf("readCgroupMemLimitMB treated the v1 unlimited sentinel as a real limit") } } // TestReadCgroupMemLimitV1Real asserts a finite v1 limit is read. func TestReadCgroupMemLimitV1Real(t *testing.T) { dir := t.TempDir() memDir := filepath.Join(dir, "memory") if err := os.MkdirAll(memDir, 0o755); err != nil { t.Fatal(err) } const oneGiB = uint64(1024 * 1024 * 1024) mustWriteFile(t, filepath.Join(memDir, "memory.limit_in_bytes"), []byte(itoa(oneGiB))) mb, ok := readCgroupMemLimitMB(dir) if !ok || mb != 1024 { t.Fatalf("readCgroupMemLimitMB = (%d, %v), want (1024, true)", mb, ok) } } func mustWriteFile(t *testing.T, path string, data []byte) { t.Helper() if err := os.WriteFile(path, data, 0o644); err != nil { t.Fatal(err) } } func itoa(v uint64) string { if v == 0 { return "0" } var buf [20]byte i := len(buf) for v > 0 { i-- buf[i] = byte('0' + v%10) v /= 10 } return string(buf[i:]) }