Adds log rotation, config file support, and refactoring. Colorschemes are self-registered. Merge remote-tracking branch 'jrswab/configFile111' into config_file; drastically refactored, and am not sure how much original code from the patch was used.

This commit is contained in:
Sean E. Russell 2020-02-17 11:40:16 -06:00
commit 2a53398681
17 changed files with 474 additions and 230 deletions

View File

@ -15,10 +15,19 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [3.3.0] -
- Changed: Logs are now rotated. Settings are currently hard-coded at 4 files of 5MB
- Added: Logs are now rotated. Settings are currently hard-coded at 4 files of 5MB
each, so logs shouldn't take up more than 20MB. I'm going to see how many
complain about wanting to configure these settings before I add code to do
that.
- Added: Config file support. \$XDG_CONFIG_HOME/gotop/gotop.conf can now
contain any field in Config. Syntax is simply KEY=VALUE. Values in config
file are overridden by command-line arguments (although, there's a weakness
in that there's no way to disable boolean fields enabled in the config).
- Changed: Colorscheme registration is changed to be less hard-coded.
Colorschemes can now be created and added to the repo, without having to also
add hard-coded references elsewhere.
- Changed: Minor code refactoring to support Config file changes has resulted
in better isolation.
## [3.2.0] - 2020-02-14

View File

@ -149,14 +149,14 @@ build massive edifices, you're in for disappointment.
### CLI Options
`-c`, `--color=NAME` Set a colorscheme.
`-m`, `--minimal` Only show CPU, Mem and Process widgets.
`-m`, `--minimal` Only show CPU, Mem and Process widgets. (DEPRECATED for `-l minimal`)
`-r`, `--rate=RATE` Number of times per second to update CPU and Mem widgets [default: 1].
`-V`, `--version` Print version and exit.
`-p`, `--percpu` Show each CPU in the CPU widget.
`-a`, `--averagecpu` Show average CPU in the CPU widget.
`-f`, `--fahrenheit` Show temperatures in fahrenheit.
`-s`, `--statusbar` Show a statusbar with the time.
`-b`, `--battery` Show battery level widget (`minimal` turns off). [preview](./assets/screenshots/battery.png)
`-b`, `--battery` Show battery level widget (`minimal` turns off). [preview](./assets/screenshots/battery.png) (DEPRECATED for `-l battery`)
`-i`, `--interface=NAME` Select network interface [default: all].
`-l`, `--layout=NAME` Choose a layout. gotop searches for a file by NAME in \$XDG_CONFIG_HOME/gotop, then relative to the current path. "-" reads a layout from stdin, allowing for simple, one-off layouts such as `echo net | gotop -l -`

View File

@ -1,9 +1,8 @@
package main
import (
"encoding/json"
"fmt"
"io/ioutil"
"io"
"log"
"os"
"os/signal"
@ -42,7 +41,7 @@ var (
stderrLogger = log.New(os.Stderr, "", 0)
)
func parseArgs() (gotop.Config, error) {
func parseArgs(conf *gotop.Config) error {
usage := `
Usage: gotop [options]
@ -75,133 +74,65 @@ Colorschemes:
vice
`
ld := utils.GetLogDir(appName)
cd := utils.GetConfigDir(appName)
conf = gotop.Config{
ConfigDir: cd,
LogDir: ld,
LogFile: "errors.log",
GraphHorizontalScale: 7,
HelpVisible: false,
Colorscheme: colorschemes.Default,
UpdateInterval: time.Second,
AverageLoad: false,
PercpuLoad: false,
TempScale: w.Celsius,
Statusbar: false,
NetInterface: w.NET_INTERFACE_ALL,
MaxLogSize: 5000000,
var err error
conf.Colorscheme, err = colorschemes.FromName(conf.ConfigDir, "default")
if err != nil {
return err
}
args, err := docopt.ParseArgs(usage, os.Args[1:], version)
if err != nil {
return conf, err
return err
}
if val, _ := args["--layout"]; val != nil {
s := val.(string)
switch s {
case "-":
conf.Layout = os.Stdin
case "default":
conf.Layout = strings.NewReader(defaultUI)
case "minimal":
conf.Layout = strings.NewReader(minimalUI)
case "battery":
conf.Layout = strings.NewReader(batteryUI)
default:
fp := filepath.Join(cd, s)
conf.Layout, err = os.Open(fp)
if err != nil {
conf.Layout, err = os.Open(s)
if err != nil {
stderrLogger.Fatalf("Unable to open layout file %s or ./%s", fp, s)
}
}
}
} else {
conf.Layout = strings.NewReader(defaultUI)
conf.Layout = val.(string)
}
if val, _ := args["--color"]; val != nil {
cs, err := handleColorscheme(val.(string))
cs, err := colorschemes.FromName(conf.ConfigDir, val.(string))
if err != nil {
return conf, err
return err
}
conf.Colorscheme = cs
}
conf.AverageLoad, _ = args["--averagecpu"].(bool)
conf.PercpuLoad, _ = args["--percpu"].(bool)
statusbar, _ = args["--statusbar"].(bool)
if args["--averagecpu"].(bool) {
conf.AverageLoad, _ = args["--averagecpu"].(bool)
}
if args["--percpu"].(bool) {
conf.PercpuLoad, _ = args["--percpu"].(bool)
}
if args["--statusbar"].(bool) {
statusbar, _ = args["--statusbar"].(bool)
}
if args["--battery"].(bool) {
log.Printf("BATTERY %s", batteryUI)
conf.Layout = strings.NewReader(batteryUI)
conf.Layout = "battery"
}
if args["--minimal"].(bool) {
conf.Layout = strings.NewReader(minimalUI)
conf.Layout = "minimal"
}
rateStr, _ := args["--rate"].(string)
rate, err := strconv.ParseFloat(rateStr, 64)
if err != nil {
return conf, fmt.Errorf("invalid rate parameter")
}
if rate < 1 {
conf.UpdateInterval = time.Second * time.Duration(1/rate)
} else {
conf.UpdateInterval = time.Second / time.Duration(rate)
}
fahrenheit, _ := args["--fahrenheit"].(bool)
if fahrenheit {
conf.TempScale = w.Fahrenheit
}
conf.NetInterface, _ = args["--interface"].(string)
return conf, nil
}
func handleColorscheme(c string) (colorschemes.Colorscheme, error) {
var cs colorschemes.Colorscheme
switch c {
case "default":
cs = colorschemes.Default
case "solarized":
cs = colorschemes.Solarized
case "solarized16-light":
cs = colorschemes.Solarized16Light
case "solarized16-dark":
cs = colorschemes.Solarized16Dark
case "monokai":
cs = colorschemes.Monokai
case "vice":
cs = colorschemes.Vice
case "default-dark":
cs = colorschemes.DefaultDark
case "nord":
cs = colorschemes.Nord
default:
custom, err := getCustomColorscheme(conf, c)
if val, _ := args["--statusbar"]; val != nil {
rateStr, _ := args["--rate"].(string)
rate, err := strconv.ParseFloat(rateStr, 64)
if err != nil {
return cs, err
return fmt.Errorf("invalid rate parameter")
}
if rate < 1 {
conf.UpdateInterval = time.Second * time.Duration(1/rate)
} else {
conf.UpdateInterval = time.Second / time.Duration(rate)
}
cs = custom
}
return cs, nil
}
if val, _ := args["--fahrenheit"]; val != nil {
fahrenheit, _ := val.(bool)
if fahrenheit {
conf.TempScale = w.Fahrenheit
}
}
if val, _ := args["--interface"]; val != nil {
conf.NetInterface, _ = args["--interface"].(string)
}
// getCustomColorscheme tries to read a custom json colorscheme from <configDir>/<name>.json
func getCustomColorscheme(c gotop.Config, name string) (colorschemes.Colorscheme, error) {
var cs colorschemes.Colorscheme
filePath := filepath.Join(c.ConfigDir, name+".json")
dat, err := ioutil.ReadFile(filePath)
if err != nil {
return cs, fmt.Errorf("failed to read colorscheme file: %v", err)
}
err = json.Unmarshal(dat, &cs)
if err != nil {
return cs, fmt.Errorf("failed to parse colorscheme file: %v", err)
}
return cs, nil
return nil
}
func setDefaultTermuiColors(c gotop.Config) {
@ -388,8 +319,40 @@ func eventLoop(c gotop.Config, grid *layout.MyGrid) {
}
}
func makeConfig() gotop.Config {
ld := utils.GetLogDir(appName)
cd := utils.GetConfigDir(appName)
conf = gotop.Config{
ConfigDir: cd,
LogDir: ld,
LogFile: "errors.log",
GraphHorizontalScale: 7,
HelpVisible: false,
UpdateInterval: time.Second,
AverageLoad: false,
PercpuLoad: false,
TempScale: w.Celsius,
Statusbar: false,
NetInterface: w.NET_INTERFACE_ALL,
MaxLogSize: 5000000,
Layout: "default",
}
return conf
}
func main() {
conf, err := parseArgs()
// Set up default config
conf := makeConfig()
// Parse the config file
cfn := filepath.Join(conf.ConfigDir, "gotop.conf")
if cf, err := os.Open(cfn); err == nil {
err := gotop.Parse(cf, &conf)
if err != nil {
stderrLogger.Fatalf("error parsing config file %v", err)
}
}
// Override with command line arguments
err := parseArgs(&conf)
if err != nil {
stderrLogger.Fatalf("failed to parse cli args: %v", err)
}
@ -411,7 +374,8 @@ func main() {
bar = w.NewStatusBar()
}
ly := layout.ParseLayout(conf.Layout)
lstream := getLayout(conf)
ly := layout.ParseLayout(lstream)
grid, err := layout.Layout(ly, conf)
if err != nil {
stderrLogger.Fatalf("failed to initialize termui: %v", err)
@ -433,3 +397,27 @@ func main() {
eventLoop(conf, grid)
}
func getLayout(conf gotop.Config) io.Reader {
switch conf.Layout {
case "-":
return os.Stdin
case "default":
return strings.NewReader(defaultUI)
case "minimal":
return strings.NewReader(minimalUI)
case "battery":
return strings.NewReader(batteryUI)
default:
log.Printf("layout = %s", conf.Layout)
fp := filepath.Join(conf.ConfigDir, conf.Layout)
fin, err := os.Open(fp)
if err != nil {
fin, err = os.Open(conf.Layout)
if err != nil {
log.Fatalf("Unable to open layout file %s or ./%s", fp, conf.Layout)
}
}
return fin
}
}

View File

@ -1,25 +1,27 @@
package colorschemes
var Default = Colorscheme{
Fg: 7,
Bg: -1,
func init() {
register("default", Colorscheme{
Fg: 7,
Bg: -1,
BorderLabel: 7,
BorderLine: 6,
BorderLabel: 7,
BorderLine: 6,
CPULines: []int{4, 3, 2, 1, 5, 6, 7, 8},
CPULines: []int{4, 3, 2, 1, 5, 6, 7, 8},
BattLines: []int{4, 3, 2, 1, 5, 6, 7, 8},
BattLines: []int{4, 3, 2, 1, 5, 6, 7, 8},
MainMem: 5,
SwapMem: 11,
MainMem: 5,
SwapMem: 11,
ProcCursor: 4,
ProcCursor: 4,
Sparkline: 4,
Sparkline: 4,
DiskBar: 7,
DiskBar: 7,
TempLow: 2,
TempHigh: 1,
TempLow: 2,
TempHigh: 1,
})
}

View File

@ -1,25 +1,27 @@
package colorschemes
var DefaultDark = Colorscheme{
Fg: 235,
Bg: -1,
func init() {
register("defaultdark", Colorscheme{
Fg: 235,
Bg: -1,
BorderLabel: 235,
BorderLine: 6,
BorderLabel: 235,
BorderLine: 6,
CPULines: []int{4, 3, 2, 1, 5, 6, 7, 8},
CPULines: []int{4, 3, 2, 1, 5, 6, 7, 8},
BattLines: []int{4, 3, 2, 1, 5, 6, 7, 8},
BattLines: []int{4, 3, 2, 1, 5, 6, 7, 8},
MainMem: 5,
SwapMem: 3,
MainMem: 5,
SwapMem: 3,
ProcCursor: 33,
ProcCursor: 33,
Sparkline: 4,
Sparkline: 4,
DiskBar: 252,
DiskBar: 252,
TempLow: 2,
TempHigh: 1,
TempLow: 2,
TempHigh: 1,
})
}

View File

@ -1,25 +1,27 @@
package colorschemes
var Monokai = Colorscheme{
Fg: 249,
Bg: -1,
func init() {
register("monokai", Colorscheme{
Fg: 249,
Bg: -1,
BorderLabel: 249,
BorderLine: 239,
BorderLabel: 249,
BorderLine: 239,
CPULines: []int{81, 70, 208, 197, 249, 141, 221, 186},
CPULines: []int{81, 70, 208, 197, 249, 141, 221, 186},
BattLines: []int{81, 70, 208, 197, 249, 141, 221, 186},
BattLines: []int{81, 70, 208, 197, 249, 141, 221, 186},
MainMem: 208,
SwapMem: 186,
MainMem: 208,
SwapMem: 186,
ProcCursor: 197,
ProcCursor: 197,
Sparkline: 81,
Sparkline: 81,
DiskBar: 102,
DiskBar: 102,
TempLow: 70,
TempHigh: 208,
TempLow: 70,
TempHigh: 208,
})
}

View File

@ -11,28 +11,30 @@
package colorschemes
var Nord = Colorscheme{
Name: "A Nord Approximation",
Author: "@jrswab",
Fg: 254, // lightest
Bg: -1,
func init() {
register("nord", Colorscheme{
Name: "A Nord Approximation",
Author: "@jrswab",
Fg: 254, // lightest
Bg: -1,
BorderLabel: 254,
BorderLine: 96, // Purple
BorderLabel: 254,
BorderLine: 96, // Purple
CPULines: []int{4, 3, 2, 1, 5, 6, 7, 8},
CPULines: []int{4, 3, 2, 1, 5, 6, 7, 8},
BattLines: []int{4, 3, 2, 1, 5, 6, 7, 8},
BattLines: []int{4, 3, 2, 1, 5, 6, 7, 8},
MainMem: 172, // Orange
SwapMem: 221, // yellow
MainMem: 172, // Orange
SwapMem: 221, // yellow
ProcCursor: 31, // blue (nord9)
ProcCursor: 31, // blue (nord9)
Sparkline: 31,
Sparkline: 31,
DiskBar: 254,
DiskBar: 254,
TempLow: 64, // green
TempHigh: 167, // red
TempLow: 64, // green
TempHigh: 167, // red
})
}

43
colorschemes/registry.go Normal file
View File

@ -0,0 +1,43 @@
package colorschemes
import (
"encoding/json"
"fmt"
"io/ioutil"
"path/filepath"
)
var registry map[string]Colorscheme
func FromName(confDir string, c string) (Colorscheme, error) {
cs, ok := registry[c]
if !ok {
cs, err := getCustomColorscheme(confDir, c)
if err != nil {
return cs, err
}
}
return cs, nil
}
func register(name string, c Colorscheme) {
if registry == nil {
registry = make(map[string]Colorscheme)
}
registry[name] = c
}
// getCustomColorscheme tries to read a custom json colorscheme from <configDir>/<name>.json
func getCustomColorscheme(confDir string, name string) (Colorscheme, error) {
var cs Colorscheme
filePath := filepath.Join(confDir, name+".json")
dat, err := ioutil.ReadFile(filePath)
if err != nil {
return cs, fmt.Errorf("failed to read colorscheme file: %v", err)
}
err = json.Unmarshal(dat, &cs)
if err != nil {
return cs, fmt.Errorf("failed to parse colorscheme file: %v", err)
}
return cs, nil
}

View File

@ -3,26 +3,28 @@ package colorschemes
// This is a neutral version of the Solarized 256-color palette. The exception
// is that the one grey color uses the average of base0 and base00, which are
// already middle of the road.
var Solarized = Colorscheme{
Fg: -1,
Bg: -1,
func init() {
register("solarized", Colorscheme{
Fg: -1,
Bg: -1,
BorderLabel: -1,
BorderLine: 37,
BorderLabel: -1,
BorderLine: 37,
CPULines: []int{61, 33, 37, 64, 125, 160, 166, 136},
CPULines: []int{61, 33, 37, 64, 125, 160, 166, 136},
BattLines: []int{61, 33, 37, 64, 125, 160, 166, 136},
BattLines: []int{61, 33, 37, 64, 125, 160, 166, 136},
MainMem: 125,
SwapMem: 166,
MainMem: 125,
SwapMem: 166,
ProcCursor: 136,
ProcCursor: 136,
Sparkline: 33,
Sparkline: 33,
DiskBar: 243,
DiskBar: 243,
TempLow: 64,
TempHigh: 160,
TempLow: 64,
TempHigh: 160,
})
}

View File

@ -2,26 +2,28 @@ package colorschemes
// This scheme assumes the terminal already uses Solarized. Only DiskBar is
// different between dark/light.
var Solarized16Dark = Colorscheme{
Fg: -1,
Bg: -1,
func init() {
register("solarized16dark", Colorscheme{
Fg: -1,
Bg: -1,
BorderLabel: -1,
BorderLine: 6,
BorderLabel: -1,
BorderLine: 6,
CPULines: []int{13, 4, 6, 2, 5, 1, 9, 3},
CPULines: []int{13, 4, 6, 2, 5, 1, 9, 3},
BattLines: []int{13, 4, 6, 2, 5, 1, 9, 3},
BattLines: []int{13, 4, 6, 2, 5, 1, 9, 3},
MainMem: 5,
SwapMem: 9,
MainMem: 5,
SwapMem: 9,
ProcCursor: 4,
ProcCursor: 4,
Sparkline: 4,
Sparkline: 4,
DiskBar: 12, // base0
DiskBar: 12, // base0
TempLow: 2,
TempHigh: 1,
TempLow: 2,
TempHigh: 1,
})
}

View File

@ -2,26 +2,28 @@ package colorschemes
// This scheme assumes the terminal already uses Solarized. Only DiskBar is
// different between dark/light.
var Solarized16Light = Colorscheme{
Fg: -1,
Bg: -1,
func init() {
register("solarized16light", Colorscheme{
Fg: -1,
Bg: -1,
BorderLabel: -1,
BorderLine: 6,
BorderLabel: -1,
BorderLine: 6,
CPULines: []int{13, 4, 6, 2, 5, 1, 9, 3},
CPULines: []int{13, 4, 6, 2, 5, 1, 9, 3},
BattLines: []int{13, 4, 6, 2, 5, 1, 9, 3},
BattLines: []int{13, 4, 6, 2, 5, 1, 9, 3},
MainMem: 5,
SwapMem: 9,
MainMem: 5,
SwapMem: 9,
ProcCursor: 4,
ProcCursor: 4,
Sparkline: 4,
Sparkline: 4,
DiskBar: 11, // base00
DiskBar: 11, // base00
TempLow: 2,
TempHigh: 1,
TempLow: 2,
TempHigh: 1,
})
}

View File

@ -1,25 +1,27 @@
package colorschemes
var Vice = Colorscheme{
Fg: 231,
Bg: -1,
func init() {
register("vice", Colorscheme{
Fg: 231,
Bg: -1,
BorderLabel: 123,
BorderLine: 102,
BorderLabel: 123,
BorderLine: 102,
CPULines: []int{212, 218, 123, 159, 229, 158, 183, 146},
CPULines: []int{212, 218, 123, 159, 229, 158, 183, 146},
BattLines: []int{212, 218, 123, 159, 229, 158, 183, 146},
BattLines: []int{212, 218, 123, 159, 229, 158, 183, 146},
MainMem: 201,
SwapMem: 97,
MainMem: 201,
SwapMem: 97,
ProcCursor: 159,
ProcCursor: 159,
Sparkline: 183,
Sparkline: 183,
DiskBar: 158,
DiskBar: 158,
TempLow: 49,
TempHigh: 197,
TempLow: 49,
TempHigh: 197,
})
}

View File

@ -1,7 +1,11 @@
package gotop
import (
"bufio"
"fmt"
"io"
"strconv"
"strings"
"time"
"github.com/xxxserxxx/gotop/colorschemes"
@ -29,9 +33,92 @@ type Config struct {
UpdateInterval time.Duration
AverageLoad bool
PercpuLoad bool
TempScale widgets.TempScale
Statusbar bool
TempScale widgets.TempScale
NetInterface string
Layout io.Reader
Layout string
MaxLogSize int64
}
func Parse(in io.Reader, conf *Config) error {
r := bufio.NewScanner(in)
var lineNo int
for r.Scan() {
l := strings.TrimSpace(r.Text())
kv := strings.Split(l, "=")
if len(kv) != 2 {
return fmt.Errorf("bad config file syntax; should be KEY=VALUE, was %s", l)
}
key := strings.ToLower(kv[0])
switch key {
default:
return fmt.Errorf("invalid config option %s", key)
case "configdir":
conf.ConfigDir = kv[1]
case "logdir":
conf.LogDir = kv[1]
case "logfile":
conf.LogFile = kv[1]
case "graphhorizontalscale":
iv, err := strconv.Atoi(kv[1])
if err != nil {
return err
}
conf.GraphHorizontalScale = iv
case "helpvisible":
bv, err := strconv.ParseBool(kv[1])
if err != nil {
return fmt.Errorf("line %d: %v", lineNo, err)
}
conf.HelpVisible = bv
case "colorscheme":
cs, err := colorschemes.FromName(conf.ConfigDir, kv[1])
if err != nil {
return fmt.Errorf("line %d: %v", lineNo, err)
}
conf.Colorscheme = cs
case "updateinterval":
iv, err := strconv.Atoi(kv[1])
if err != nil {
return err
}
conf.UpdateInterval = time.Duration(iv)
case "averagecpu":
bv, err := strconv.ParseBool(kv[1])
if err != nil {
return fmt.Errorf("line %d: %v", lineNo, err)
}
conf.AverageLoad = bv
case "percpuload":
bv, err := strconv.ParseBool(kv[1])
if err != nil {
return fmt.Errorf("line %d: %v", lineNo, err)
}
conf.PercpuLoad = bv
case "tempscale":
iv, err := strconv.Atoi(kv[1])
if err != nil {
return err
}
conf.TempScale = widgets.TempScale(iv)
case "statusbar":
bv, err := strconv.ParseBool(kv[1])
if err != nil {
return fmt.Errorf("line %d: %v", lineNo, err)
}
conf.Statusbar = bv
case "netinterface":
conf.NetInterface = kv[1]
case "layout":
conf.Layout = kv[1]
case "maxlogsize":
iv, err := strconv.Atoi(kv[1])
if err != nil {
return err
}
conf.MaxLogSize = int64(iv)
}
}
return nil
}

11
config.json Normal file
View File

@ -0,0 +1,11 @@
{
"colorscheme": "default",
"updateInterval": 1,
"minimalMode": false,
"averageLoad": false,
"percpuLoad": false,
"isFahrenheit": false,
"battery": false,
"statusbar": false,
"netInterface": "NET_INTERFACE_ALL"
}

87
config_test.go Normal file
View File

@ -0,0 +1,87 @@
package gotop
import (
"strings"
"testing"
"time"
"github.com/stretchr/testify/assert"
"github.com/xxxserxxx/gotop/widgets"
)
func TestParse(t *testing.T) {
tests := []struct {
i string
f func(c Config, e error)
}{
{
i: "logdir",
f: func(c Config, e error) {
assert.Error(t, e, "invalid config syntax")
},
},
{
i: "logdir=foo=bar",
f: func(c Config, e error) {
assert.NotNil(t, e)
},
},
{
i: "foo=bar",
f: func(c Config, e error) {
assert.NotNil(t, e)
},
},
{
i: "configdir=abc\nlogdir=bar\nlogfile=errors",
f: func(c Config, e error) {
assert.Nil(t, e, "unexpected error")
assert.Equal(t, "abc", c.ConfigDir)
assert.Equal(t, "bar", c.LogDir)
assert.Equal(t, "errors", c.LogFile)
},
},
{
i: "CONFIGDIR=abc\nloGdir=bar\nlogFILe=errors",
f: func(c Config, e error) {
assert.Nil(t, e, "unexpected error")
assert.Equal(t, "abc", c.ConfigDir)
assert.Equal(t, "bar", c.LogDir)
assert.Equal(t, "errors", c.LogFile)
},
},
{
i: "graphhorizontalscale=a",
f: func(c Config, e error) {
assert.Error(t, e, "expected invalid value for graphhorizontalscale")
},
},
{
i: "helpvisible=a",
f: func(c Config, e error) {
assert.Error(t, e, "expected invalid value for helpvisible")
},
},
{
i: "helpvisible=true\nupdateinterval=30\naveragecpu=true\nPerCPULoad=true\ntempscale=100\nstatusbar=true\nnetinterface=eth0\nlayout=minimal\nmaxlogsize=200",
f: func(c Config, e error) {
assert.Nil(t, e, "unexpected error")
assert.Equal(t, true, c.HelpVisible)
assert.Equal(t, time.Duration(30), c.UpdateInterval)
assert.Equal(t, true, c.AverageLoad)
assert.Equal(t, true, c.PercpuLoad)
assert.Equal(t, widgets.TempScale(100), c.TempScale)
assert.Equal(t, true, c.Statusbar)
assert.Equal(t, "eth0", c.NetInterface)
assert.Equal(t, "minimal", c.Layout)
assert.Equal(t, int64(200), c.MaxLogSize)
},
},
}
for _, tc := range tests {
in := strings.NewReader(tc.i)
c := Config{}
e := Parse(in, &c)
tc.f(c, e)
}
}

1
go.mod
View File

@ -8,6 +8,7 @@ require (
github.com/gizak/termui/v3 v3.0.0
github.com/go-ole/go-ole v1.2.4 // indirect
github.com/mattn/go-runewidth v0.0.4
github.com/mitchellh/go-wordwrap v1.0.0 // indirect
github.com/shirou/gopsutil v2.18.11+incompatible
github.com/shirou/w32 v0.0.0-20160930032740-bb4de0191aa4 // indirect
github.com/stretchr/testify v1.4.0

2
go.sum
View File

@ -26,6 +26,8 @@ github.com/mattn/go-runewidth v0.0.4 h1:2BvfKmzob6Bmd4YsL0zygOqfdFnK7GR4QL06Do4/
github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
github.com/mitchellh/go-wordwrap v0.0.0-20150314170334-ad45545899c7 h1:DpOJ2HYzCv8LZP15IdmG+YdwD2luVPHITV96TkirNBM=
github.com/mitchellh/go-wordwrap v0.0.0-20150314170334-ad45545899c7/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo=
github.com/mitchellh/go-wordwrap v1.0.0 h1:6GlHJ/LTGMrIJbwgdqdl2eEH8o+Exx/0m8ir9Gns0u4=
github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo=
github.com/nsf/termbox-go v0.0.0-20190121233118-02980233997d h1:x3S6kxmy49zXVVyhcnrFqxvNVCBPb2KZ9hV2RBdS840=
github.com/nsf/termbox-go v0.0.0-20190121233118-02980233997d/go.mod h1:IuKpRQcYE1Tfu+oAQqaLisqDeXgjyyltCfsaoYN18NQ=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=