1
0
mirror of https://github.com/tw93/Mole.git synced 2026-03-22 20:15:07 +00:00

fix: harden CI test stability and status collector resilience

This commit is contained in:
tw93
2026-03-04 16:09:13 +08:00
parent c88691c2c8
commit ff69504f89
10 changed files with 158 additions and 19 deletions

View File

@@ -230,6 +230,9 @@ func (c *Collector) Collect() (MetricsSnapshot, error) {
// Host info is cached by gopsutil; fetch once.
hostInfo, _ := host.Info()
if hostInfo == nil {
hostInfo = &host.InfoStat{}
}
var (
wg sync.WaitGroup
@@ -255,6 +258,18 @@ func (c *Collector) Collect() (MetricsSnapshot, error) {
wg.Add(1)
go func() {
defer wg.Done()
defer func() {
if r := recover(); r != nil {
errMu.Lock()
panicErr := fmt.Errorf("collector panic: %v", r)
if mergeErr == nil {
mergeErr = panicErr
} else {
mergeErr = fmt.Errorf("%v; %w", mergeErr, panicErr)
}
errMu.Unlock()
}
}()
if err := fn(); err != nil {
errMu.Lock()
if mergeErr == nil {

View File

@@ -17,6 +17,9 @@ func collectMemory() (MemoryStatus, error) {
}
swap, _ := mem.SwapMemory()
if swap == nil {
swap = &mem.SwapMemoryStat{}
}
pressure := getMemoryPressure()
// On macOS, vm.Cached is 0, so we calculate from file-backed pages.

View File

@@ -2,6 +2,7 @@ package main
import (
"context"
"fmt"
"net/url"
"os"
"runtime"
@@ -13,10 +14,25 @@ import (
"github.com/shirou/gopsutil/v4/net"
)
var ioCountersFunc = net.IOCounters
func collectIOCountersSafely(pernic bool) (stats []net.IOCountersStat, err error) {
defer func() {
if r := recover(); r != nil {
err = fmt.Errorf("panic collecting network counters: %v", r)
}
}()
return ioCountersFunc(pernic)
}
func (c *Collector) collectNetwork(now time.Time) ([]NetworkStatus, error) {
stats, err := net.IOCounters(true)
stats, err := collectIOCountersSafely(true)
if err != nil {
return nil, err
// Some restricted environments can break netstat-backed collectors.
// Degrade gracefully to keep status output available.
c.rxHistoryBuf.Add(0)
c.txHistoryBuf.Add(0)
return nil, nil
}
// Map interface IPs.

View File

@@ -1,6 +1,11 @@
package main
import "testing"
import (
"strings"
"testing"
gopsutilnet "github.com/shirou/gopsutil/v4/net"
)
func TestCollectProxyFromEnvSupportsAllProxy(t *testing.T) {
env := map[string]string{
@@ -58,3 +63,41 @@ func TestCollectProxyFromScutilOutputHTTPHostPort(t *testing.T) {
t.Fatalf("unexpected host: %s", got.Host)
}
}
func TestCollectIOCountersSafelyRecoversPanic(t *testing.T) {
original := ioCountersFunc
ioCountersFunc = func(bool) ([]gopsutilnet.IOCountersStat, error) {
panic("boom")
}
t.Cleanup(func() { ioCountersFunc = original })
stats, err := collectIOCountersSafely(true)
if err == nil {
t.Fatalf("expected error from panic recovery")
}
if !strings.Contains(err.Error(), "panic collecting network counters") {
t.Fatalf("unexpected error: %v", err)
}
if len(stats) != 0 {
t.Fatalf("expected empty stats when panic recovered")
}
}
func TestCollectIOCountersSafelyReturnsData(t *testing.T) {
original := ioCountersFunc
want := []gopsutilnet.IOCountersStat{
{Name: "en0", BytesRecv: 1, BytesSent: 2},
}
ioCountersFunc = func(bool) ([]gopsutilnet.IOCountersStat, error) {
return want, nil
}
t.Cleanup(func() { ioCountersFunc = original })
got, err := collectIOCountersSafely(true)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if len(got) != 1 || got[0].Name != "en0" {
t.Fatalf("unexpected stats: %+v", got)
}
}