From 3826947bf1e99655a72cca0d614a35b149abd33c Mon Sep 17 00:00:00 2001 From: Graham Gilbert Date: Wed, 13 May 2026 22:35:32 -0700 Subject: [PATCH] Improve SoC power table tests --- tables/socpower/soc_power.go | 19 +++++- tables/socpower/soc_power_test.go | 104 ++++++++++++++++++++++++------ 2 files changed, 102 insertions(+), 21 deletions(-) diff --git a/tables/socpower/soc_power.go b/tables/socpower/soc_power.go index 861ab91..9b2560d 100644 --- a/tables/socpower/soc_power.go +++ b/tables/socpower/soc_power.go @@ -38,6 +38,12 @@ func SocPowerColumns() []table.ColumnDefinition { } func SocPowerGenerate(ctx context.Context, queryContext table.QueryContext) ([]map[string]string, error) { + r := utils.NewRunner() + fs := utils.OSFileSystem{} + return generateWithRunner(queryContext, r, fs) +} + +func parseInterval(queryContext table.QueryContext) int { interval := defaultInterval if constraintList, present := queryContext.Constraints["interval"]; present { for _, constraint := range constraintList.Constraints { @@ -49,8 +55,11 @@ func SocPowerGenerate(ctx context.Context, queryContext table.QueryContext) ([]m } } - r := utils.NewRunner() - fs := utils.OSFileSystem{} + return interval +} + +func generateWithRunner(queryContext table.QueryContext, r utils.Runner, fs utils.FileSystem) ([]map[string]string, error) { + interval := parseInterval(queryContext) result, err := runPowermetrics(r, fs, interval) if err != nil { fmt.Println(err) @@ -60,6 +69,10 @@ func SocPowerGenerate(ctx context.Context, queryContext table.QueryContext) ([]m return nil, nil } + return buildOutput(result, interval), nil +} + +func buildOutput(result *powermetricsOutput, interval int) []map[string]string { gpuActiveRatio := 1.0 - result.GPU.IdleRatio return []map[string]string{{ @@ -69,7 +82,7 @@ func SocPowerGenerate(ctx context.Context, queryContext table.QueryContext) ([]m "combined_power_mw": fmt.Sprintf("%.2f", result.Processor.CombinedPower), "gpu_active_ratio": fmt.Sprintf("%.4f", gpuActiveRatio), "interval": strconv.Itoa(interval), - }}, nil + }} } func runPowermetrics(r utils.Runner, fs utils.FileSystem, interval int) (*powermetricsOutput, error) { diff --git a/tables/socpower/soc_power_test.go b/tables/socpower/soc_power_test.go index 153d92d..14efac7 100644 --- a/tables/socpower/soc_power_test.go +++ b/tables/socpower/soc_power_test.go @@ -6,6 +6,7 @@ import ( "testing" "github.com/macadmins/osquery-extension/pkg/utils" + "github.com/osquery/osquery-go/plugin/table" "github.com/stretchr/testify/assert" ) @@ -13,29 +14,41 @@ import ( var testPlist string func TestSocPowerColumns(t *testing.T) { - columns := SocPowerColumns() - - assert.Len(t, columns, 6) - - columnNames := make(map[string]bool) - for _, col := range columns { - columnNames[col.Name] = true - } + assert.Equal(t, []table.ColumnDefinition{ + table.TextColumn("cpu_power_mw"), + table.TextColumn("gpu_power_mw"), + table.TextColumn("ane_power_mw"), + table.TextColumn("combined_power_mw"), + table.TextColumn("gpu_active_ratio"), + table.IntegerColumn("interval"), + }, SocPowerColumns()) +} - for _, name := range []string{"cpu_power_mw", "gpu_power_mw", "ane_power_mw", "combined_power_mw", "gpu_active_ratio", "interval"} { - assert.True(t, columnNames[name], "expected column %s not found", name) - } +func TestParseInterval(t *testing.T) { + assert.Equal(t, defaultInterval, parseInterval(table.QueryContext{})) + assert.Equal(t, 5000, parseInterval(table.QueryContext{Constraints: map[string]table.ConstraintList{ + "interval": {Constraints: []table.Constraint{{ + Operator: table.OperatorEquals, + Expression: "5000", + }}}, + }})) + assert.Equal(t, defaultInterval, parseInterval(table.QueryContext{Constraints: map[string]table.ConstraintList{ + "interval": {Constraints: []table.Constraint{{ + Operator: table.OperatorEquals, + Expression: "bad", + }}}, + }})) } func TestRunPowermetrics(t *testing.T) { tests := []struct { - name string - mockCmd utils.MockCmdRunner - fileExists bool - interval int - wantErr bool - wantNil bool - checkResult func(t *testing.T, result *powermetricsOutput) + name string + mockCmd utils.MockCmdRunner + fileExists bool + interval int + wantErr bool + wantNil bool + checkResult func(t *testing.T, result *powermetricsOutput) }{ { name: "Binary not present", @@ -76,6 +89,12 @@ func TestRunPowermetrics(t *testing.T) { interval: 3000, wantErr: true, }, + { + name: "Stat error", + fileExists: true, + interval: 3000, + wantErr: true, + }, { name: "Custom interval", mockCmd: utils.MockCmdRunner{ @@ -93,6 +112,9 @@ func TestRunPowermetrics(t *testing.T) { t.Run(tt.name, func(t *testing.T) { runner := utils.Runner{Runner: tt.mockCmd} fs := utils.MockFileSystem{FileExists: tt.fileExists} + if tt.name == "Stat error" { + fs.Err = errors.New("stat failed") + } result, err := runPowermetrics(runner, fs, tt.interval) @@ -115,3 +137,49 @@ func TestRunPowermetrics(t *testing.T) { }) } } + +func TestBuildOutputFormatsValues(t *testing.T) { + rows := buildOutput(&powermetricsOutput{ + Processor: struct { + CPUPower float64 `plist:"cpu_power"` + GPUPower float64 `plist:"gpu_power"` + ANEPower float64 `plist:"ane_power"` + CombinedPower float64 `plist:"combined_power"` + }{ + CPUPower: 9924.345, + GPUPower: 169.958, + ANEPower: 0, + CombinedPower: 10094.3, + }, + GPU: struct { + IdleRatio float64 `plist:"idle_ratio"` + }{ + IdleRatio: 0.901357, + }, + }, 5000) + + assert.Equal(t, []map[string]string{{ + "cpu_power_mw": "9924.34", + "gpu_power_mw": "169.96", + "ane_power_mw": "0.00", + "combined_power_mw": "10094.30", + "gpu_active_ratio": "0.0986", + "interval": "5000", + }}, rows) +} + +func TestGenerateWithRunner(t *testing.T) { + rows, err := generateWithRunner( + table.QueryContext{Constraints: map[string]table.ConstraintList{ + "interval": {Constraints: []table.Constraint{{ + Operator: table.OperatorEquals, + Expression: "5000", + }}}, + }}, + utils.Runner{Runner: utils.MockCmdRunner{Output: testPlist}}, + utils.MockFileSystem{FileExists: true}, + ) + assert.NoError(t, err) + assert.Len(t, rows, 1) + assert.Equal(t, "5000", rows[0]["interval"]) +}