diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..eeb5d1b --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +coverage/ +build/ diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..3ef0be6 --- /dev/null +++ b/Makefile @@ -0,0 +1,16 @@ +build: clean coverage + if [ ! -d build ]; then mkdir build; fi + find ./cmd/ -mindepth 1 -maxdepth 1 -type d -exec go build -o ./build/ {} \; + +test: + if [ ! -d coverage ]; then mkdir coverage; fi + go test -race -coverprofile coverage/coverage.out ./... + +clean: + rm -rf coverage/* + rm -rf build/* + +coverage: coverage/index.html + +coverage/index.html: test + go tool cover -html="coverage/coverage.out" -o "coverage/index.html" diff --git a/go.mod b/go.mod index 575d450..0efdf50 100644 --- a/go.mod +++ b/go.mod @@ -8,6 +8,7 @@ require ( github.com/gosimple/slug v1.9.0 github.com/prometheus/client_golang v1.4.1 github.com/prometheus/procfs v0.0.10 // indirect + github.com/stretchr/testify v1.5.1 github.com/urfave/cli/v2 v2.1.1 golang.org/x/sys v0.0.0-20200219091948-cb0a6d8edb6c // indirect ) diff --git a/go.sum b/go.sum index 31ec47b..989a270 100644 --- a/go.sum +++ b/go.sum @@ -14,6 +14,7 @@ github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:ma github.com/cpuguy83/go-md2man/v2 v2.0.0 h1:EoUDS0afbrsXAZ9YQ9jdu/mZ2sXgT1/2yyNng4PGlyM= github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= @@ -81,7 +82,10 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+ github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4= +github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/urfave/cli/v2 v2.1.1 h1:Qt8FeAtxE/vfdrLmR3rxR6JRE0RoVmbXu8+6kZtYU4k= github.com/urfave/cli/v2 v2.1.1/go.mod h1:SE9GqnLQmjVa0iPEY0f1w3ygNIYcIJ0OKPMoW2caLfQ= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= @@ -107,4 +111,5 @@ gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8 gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.5 h1:ymVxjfMaHvXD8RqPRmzHHsB3VvucivSkIAvJFDI5O3c= gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= diff --git a/lib/app/app.go b/lib/app/app.go index 38d3f59..a23e151 100644 --- a/lib/app/app.go +++ b/lib/app/app.go @@ -7,6 +7,7 @@ import ( "net/http" "net/url" "os" + "time" "git.buddy.wtf/buddy/open-hardware-monitor-client/lib/client" "git.buddy.wtf/buddy/open-hardware-monitor-client/lib/metrics" @@ -111,6 +112,7 @@ func (a *App) Run(args []string) error { func (a *App) Before(ctx *cli.Context) error { host := fmt.Sprintf("%s:%d", ctx.String("host"), ctx.Int("port")) a.Client = &client.Client{ + Timeout: time.Second, URL: url.URL{ Scheme: ctx.String("scheme"), Host: host, diff --git a/lib/client/client.go b/lib/client/client.go index 3f52fe6..6ce0039 100644 --- a/lib/client/client.go +++ b/lib/client/client.go @@ -27,6 +27,11 @@ func (c *Client) Fetch() (*Node, error) { // Decode json func (c *Client) Decode(r io.Reader) (*Node, error) { + return Decode(r) +} + +// Decode json +func Decode(r io.Reader) (*Node, error) { node := &Node{} decoder := json.NewDecoder(r) if err := decoder.Decode(node); err != nil { diff --git a/lib/client/hardware_test.go b/lib/client/hardware_test.go new file mode 100644 index 0000000..c692dda --- /dev/null +++ b/lib/client/hardware_test.go @@ -0,0 +1,35 @@ +package client + +// func TestSensorString(t *testing.T) { +// a := assert.New(t) +// a.Equal("Clock", Clock.String()) +// a.Equal("Control", Control.String()) +// a.Equal("Data", Data.String()) +// a.Equal("Factor", Factor.String()) +// a.Equal("Fan", Fan.String()) +// a.Equal("Flow", Flow.String()) +// a.Equal("Level", Level.String()) +// a.Equal("Load", Load.String()) +// a.Equal("Power", Power.String()) +// a.Equal("SmallData", SmallData.String()) +// a.Equal("Temperature", Temperature.String()) +// a.Equal("Voltage", Voltage.String()) +// a.Equal("Unknown", Sensor(123456).String()) +// } + +// func TestHardwareTypeString(t *testing.T) { +// a := assert.New(t) +// a.Equal("Unknown", UnknownHardware.String()) +// a.Equal("Mainboard", Mainboard.String()) +// a.Equal("SuperIO", SuperIO.String()) +// a.Equal("CPU", CPU.String()) +// a.Equal("RAM", RAM.String()) +// a.Equal("GpuNvidia", GpuNvidia.String()) +// a.Equal("GpuAti", GpuAti.String()) +// a.Equal("TBalancer", TBalancer.String()) +// a.Equal("Heatmaster", Heatmaster.String()) +// a.Equal("HDD", HDD.String()) +// a.Equal("Controller", Controller.String()) +// a.Equal("Computer", Computer.String()) +// a.Equal("Unknown", HardwareType(12345).String()) +// } diff --git a/lib/client/node.go b/lib/client/node.go index adfcc03..8afdc37 100644 --- a/lib/client/node.go +++ b/lib/client/node.go @@ -23,7 +23,7 @@ type Visitor func(v Value) error // IsValue true if node has children func (n *Node) IsValue() bool { if n.Children == nil { - return false + return n.Value != "" } return len(n.Children) == 0 } diff --git a/lib/client/node_test.go b/lib/client/node_test.go new file mode 100644 index 0000000..b44b6c7 --- /dev/null +++ b/lib/client/node_test.go @@ -0,0 +1,252 @@ +package client + +import ( + "fmt" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestNodeIsValue(t *testing.T) { + a := assert.New(t) + tt := []struct { + node *Node + expected bool + }{ + {&Node{}, false}, + {&Node{Children: []Node{{}}}, false}, + {&Node{Children: []Node{}}, true}, + } + for i, tc := range tt { + msg := fmt.Sprintf("test case %d", i) + a.Equal(tc.expected, tc.node.IsValue(), msg) + } +} + +func TestNodeIsRoot(t *testing.T) { + a := assert.New(t) + tt := []struct { + node *Node + expected bool + }{ + {&Node{}, true}, + {&Node{ID: 123}, false}, + } + for i, tc := range tt { + msg := fmt.Sprintf("test case %d", i) + a.Equal(tc.expected, tc.node.IsRoot(), msg) + } +} + +func TestNodeSensor(t *testing.T) { + a := assert.New(t) + tt := []struct { + node *Node + sensor Sensor + str string + isSensor bool + }{ + {&Node{}, UnknownSensor, "Unknown", false}, + {&Node{Text: "any unknown sensor"}, UnknownSensor, "Unknown", false}, + {&Node{ImageURL: "x"}, UnknownSensor, "Unknown", false}, + {&Node{Text: "Example"}, UnknownSensor, "Unknown", false}, + {&Node{ImageURL: "x", Text: "Example"}, UnknownSensor, "Unknown", false}, + {&Node{ImageURL: "x", Text: "Clocks"}, Clock, "Clock", true}, + {&Node{ImageURL: "x", Text: "Controls"}, Control, "Control", true}, + {&Node{ImageURL: "x", Text: "Data"}, Data, "Data", true}, + {&Node{ImageURL: "x", Text: "Data", Children: []Node{{Value: "123.4 MB"}}}, SmallData, "SmallData", true}, + {&Node{ImageURL: "x", Text: "Factors"}, Factor, "Factor", true}, + {&Node{ImageURL: "x", Text: "Fans"}, Fan, "Fan", true}, + {&Node{ImageURL: "x", Text: "Flows"}, Flow, "Flow", true}, + {&Node{ImageURL: "x", Text: "Levels"}, Level, "Level", true}, + {&Node{ImageURL: "x", Text: "Load"}, Load, "Load", true}, + {&Node{ImageURL: "x", Text: "Powers"}, Power, "Power", true}, + {&Node{ImageURL: "x", Text: "Temperatures"}, Temperature, "Temperature", true}, + {&Node{ImageURL: "x", Text: "Voltages"}, Voltage, "Voltage", true}, + } + for _, tc := range tt { + a.Equal(tc.isSensor, tc.node.IsSensor()) + a.Equal(tc.sensor, tc.node.SensorType()) + a.Equal(tc.str, tc.node.SensorType().String()) + } +} + +func TestNodeHardware(t *testing.T) { + a := assert.New(t) + tt := []struct { + node *Node + kind HardwareType + str string + isHW bool + }{ + {&Node{}, UnknownHardware, "Unknown", false}, + {&Node{Text: "unknown hardware"}, UnknownHardware, "Unknown", false}, + {&Node{ImageURL: "x"}, UnknownHardware, "Unknown", false}, + + {&Node{ImageURL: "x/ati.png", Text: "x"}, GpuAti, "GpuAti", true}, + {&Node{ImageURL: "x/bigng.png", Text: "x"}, Controller, "Controller", true}, + {&Node{ImageURL: "x/chip.png", Text: "x"}, SuperIO, "SuperIO", true}, + {&Node{ImageURL: "x/cpu.png", Text: "x"}, CPU, "CPU", true}, + {&Node{ImageURL: "x/hdd.png", Text: "x"}, HDD, "HDD", true}, + {&Node{ImageURL: "x/mainboard.png", Text: "x"}, Mainboard, "Mainboard", true}, + {&Node{ImageURL: "x/nvidia.png", Text: "x"}, GpuNvidia, "GpuNvidia", true}, + {&Node{ImageURL: "x/ram.png", Text: "x"}, RAM, "RAM", true}, + {&Node{ImageURL: "x/computer.png", Text: "x"}, Computer, "Computer", true}, + } + for _, tc := range tt { + a.Equal(tc.isHW, tc.node.IsHardware()) + a.Equal(tc.kind, tc.node.HardwareType()) + a.Equal(tc.str, tc.node.HardwareType().String()) + } + + a.Equal("TBalancer", TBalancer.String()) + a.Equal("Heatmaster", Heatmaster.String()) + a.Equal("Unknown", HardwareType(1234).String()) +} + +var exampleNode = &Node{ + ID: 0, Text: "Sensor", Min: "Min", Value: "Value", Max: "Max", + Children: []Node{{ + ID: 1, Text: "hostname", ImageURL: "images_icon/computer.png", + Children: []Node{{ + ID: 2, Text: "board model", ImageURL: "images_icon/mainboard.png", + Children: []Node{{ + ID: 3, Text: "chipset model", ImageURL: "images_icon/chip.png", + Children: []Node{ + { + ID: 4, Text: "Voltages", ImageURL: "images_icon/voltage.png", + Children: []Node{{ + ID: 5, + Text: "Voltage #1", + Min: "0.540 V", + Value: "1.332 V", + Max: "1.392 V", + ImageURL: "images/transparent.png", + }}, + }, + { + ID: 6, Text: "Temperatures", ImageURL: "images_icon/temperature.png", + Children: []Node{{ + ID: 7, + Text: "Temperature #1", + Min: "20.0 °C", + Value: "36.0 °C", + Max: "51.0 °C", + ImageURL: "images/transparent.png", + }}, + }, + }, + }, { + ID: 8, Text: "CPU Model", ImageURL: "images_icon/cpu.png", + Children: []Node{{ + ID: 9, Text: "Clocks", ImageURL: "images_icon/clock.png", + Children: []Node{{ + ID: 10, + Text: "Bus Speed", + Min: "100 MHz", + Value: "100 MHz", + Max: "100 MHz", + ImageURL: "images/transparent.png", + }, { + ID: 11, + Text: "CPU Core #1", + Min: "802 MHz", + Value: "4008 MHz", + Max: "4208 MHz", + ImageURL: "images/transparent.png", + }}, + }}, + }}, + }}, + }}, +} + +func TestNodeValues(t *testing.T) { + a := assert.New(t) + + tt := []struct { + node *Node + values []Value + }{ + {&Node{}, []Value{}}, + + { + node: exampleNode, + + values: []Value{{ + Unit: Voltage, + Label: "Voltage #1", + Value: "1.332 V", + Hardware: []Hardware{ + Hardware{Type: Computer, Value: "hostname", TypeCount: 1}, + Hardware{Type: Mainboard, Value: "board model", TypeCount: 1}, + Hardware{Type: SuperIO, Value: "chipset model", TypeCount: 1}, + }, + }, { + Unit: Temperature, + Label: "Temperature #1", + Value: "36.0 °C", + Hardware: []Hardware{ + {Type: Computer, Value: "hostname", TypeCount: 1}, + {Type: Mainboard, Value: "board model", TypeCount: 1}, + {Type: SuperIO, Value: "chipset model", TypeCount: 1}, + }, + }, { + Unit: Clock, + Label: "Bus Speed", + Value: "100 MHz", + Hardware: []Hardware{ + {Type: Computer, Value: "hostname", TypeCount: 1}, + {Type: Mainboard, Value: "board model", TypeCount: 1}, + {Type: CPU, Value: "CPU Model", TypeCount: 1}, + }, + }, { + Unit: Clock, + Label: "CPU Core #1", + Value: "4008 MHz", + Hardware: []Hardware{ + {Type: Computer, Value: "hostname", TypeCount: 1}, + {Type: Mainboard, Value: "board model", TypeCount: 1}, + {Type: CPU, Value: "CPU Model", TypeCount: 1}, + }, + }}, + }, + } + + for _, tc := range tt { + actual, err := tc.node.Values() + a.Nil(err) + a.Equal(actual, tc.values) + } +} + +func TestNodeStringify(t *testing.T) { + a := assert.New(t) + + tt := []struct { + node *Node + expected string + }{ + {&Node{}, "\n"}, + { + node: exampleNode, + expected: `Sensor: Value (Min - Max) + hostname + board model + chipset model + Voltages + Voltage #1: 1.332 V (0.540 V - 1.392 V) + Temperatures + Temperature #1: 36.0 °C (20.0 °C - 51.0 °C) + CPU Model + Clocks + Bus Speed: 100 MHz (100 MHz - 100 MHz) + CPU Core #1: 4008 MHz (802 MHz - 4208 MHz) +`, + }, + } + + for _, tc := range tt { + a.Equal(tc.expected, tc.node.Stringify()) + } +}