Skip to content

Commit

Permalink
Merge pull request #221 from bojand/file_config_option
Browse files Browse the repository at this point in the history
Fix file config API options
  • Loading branch information
bojand committed Aug 30, 2020
2 parents 062100d + 3592229 commit b879715
Show file tree
Hide file tree
Showing 5 changed files with 187 additions and 49 deletions.
7 changes: 0 additions & 7 deletions cmd/ghz/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import (
"errors"
"fmt"
"io/ioutil"
"math"
"os"
"runtime"
"strconv"
Expand Down Expand Up @@ -218,12 +217,6 @@ func main() {
kingpin.FatalIfError(err, "")
}

// init / fix up durations
if cfg.X > 0 {
cfg.Z = cfg.X
} else if cfg.Z > 0 {
cfg.N = math.MaxInt32
}
var logger *zap.SugaredLogger

options := []runner.Option{runner.WithConfig(&cfg)}
Expand Down
46 changes: 33 additions & 13 deletions runner/options.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"fmt"
"io"
"io/ioutil"
"math"
"os"
"path/filepath"
"runtime"
Expand Down Expand Up @@ -79,18 +80,16 @@ type RunConfig struct {
type Option func(*RunConfig) error

// WithConfigFromFile uses a configuration JSON file to populate the RunConfig
// WithConfig("config.json")
// WithConfigFromFile("config.json")
func WithConfigFromFile(file string) Option {
return func(o *RunConfig) error {
data, err := ioutil.ReadFile(file)
var cfg Config
err := LoadConfig(file, &cfg)
if err != nil {
return fmt.Errorf("read config from file: %w", err)
}
var config Config
if err := json.Unmarshal(data, &config); err != nil {
return fmt.Errorf("unmarshal config: %w", err)
return err
}
for _, option := range fromConfig(&config) {

for _, option := range fromConfig(&cfg) {
if err := option(o); err != nil {
return err
}
Expand All @@ -103,11 +102,12 @@ func WithConfigFromFile(file string) Option {
// See also: WithConfigFromFile
func WithConfigFromReader(reader io.Reader) Option {
return func(o *RunConfig) error {
var config Config
if err := json.NewDecoder(reader).Decode(&config); err != nil {
var cfg Config
if err := json.NewDecoder(reader).Decode(&cfg); err != nil {
return fmt.Errorf("unmarshal config: %w", err)
}
for _, option := range fromConfig(&config) {

for _, option := range fromConfig(&cfg) {
if err := option(o); err != nil {
return err
}
Expand All @@ -118,9 +118,17 @@ func WithConfigFromReader(reader io.Reader) Option {

// WithConfig uses the configuration to populate the RunConfig
// See also: WithConfigFromFile, WithConfigFromReader
func WithConfig(c *Config) Option {
func WithConfig(cfg *Config) Option {
return func(o *RunConfig) error {
for _, option := range fromConfig(c) {

// init / fix up durations
if cfg.X > 0 {
cfg.Z = cfg.X
} else if cfg.Z > 0 {
cfg.N = math.MaxInt32
}

for _, option := range fromConfig(cfg) {
if err := option(o); err != nil {
return err
}
Expand Down Expand Up @@ -584,6 +592,11 @@ func newConfig(call, host string, options ...Option) (*RunConfig, error) {
}
}

// fix up durations
if c.z > 0 {
c.n = math.MaxInt32
}

// checks
if c.nConns > c.c {
return nil, errors.New("Number of connections cannot be greater than concurrency")
Expand Down Expand Up @@ -668,6 +681,13 @@ func fromConfig(cfg *Config) []Option {
// set up all the options
options := make([]Option, 0, 17)

// init / fix up durations
if cfg.X > 0 {
cfg.Z = cfg.X
} else if cfg.Z > 0 {
cfg.N = math.MaxInt32
}

options = append(options,
WithProtoFile(cfg.Proto, cfg.ImportPaths),
WithProtoset(cfg.Protoset),
Expand Down
83 changes: 54 additions & 29 deletions runner/options_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package runner

import (
"encoding/json"
"math"
"os"
"runtime"
"testing"
Expand Down Expand Up @@ -88,7 +89,7 @@ func TestRunConfig_newRunConfig(t *testing.T) {
assert.Equal(t, "call", c.call)
assert.Equal(t, "localhost:50050", c.host)
assert.Equal(t, true, c.insecure)
assert.Equal(t, 100, c.n)
assert.Equal(t, math.MaxInt32, c.n)
assert.Equal(t, 20, c.c)
assert.Equal(t, 5, c.qps)
assert.Equal(t, 5, c.skipFirst)
Expand Down Expand Up @@ -118,7 +119,6 @@ func TestRunConfig_newRunConfig(t *testing.T) {
WithConcurrency(20),
WithQPS(5),
WithSkipFirst(5),
WithRunDuration(time.Duration(5*time.Minute)),
WithKeepalive(time.Duration(60*time.Second)),
WithTimeout(time.Duration(10*time.Second)),
WithDialTimeout(time.Duration(30*time.Second)),
Expand All @@ -143,7 +143,7 @@ func TestRunConfig_newRunConfig(t *testing.T) {
assert.Equal(t, 5, c.qps)
assert.Equal(t, 5, c.skipFirst)
assert.Equal(t, true, c.binary)
assert.Equal(t, time.Duration(5*time.Minute), c.z)
assert.Equal(t, time.Duration(0), c.z)
assert.Equal(t, time.Duration(60*time.Second), c.keepaliveTime)
assert.Equal(t, time.Duration(10*time.Second), c.timeout)
assert.Equal(t, time.Duration(30*time.Second), c.dialTimeout)
Expand Down Expand Up @@ -185,7 +185,6 @@ func TestRunConfig_newRunConfig(t *testing.T) {
WithProtoFile("testdata/data.proto", []string{}),
WithCertificate("../testdata/localhost.crt", "../testdata/localhost.key"),
WithInsecure(true),
WithTotalRequests(100),
WithConcurrency(20),
WithQPS(5),
WithSkipFirst(5),
Expand All @@ -208,7 +207,7 @@ func TestRunConfig_newRunConfig(t *testing.T) {
assert.Equal(t, true, c.insecure)
assert.Equal(t, "../testdata/localhost.crt", c.cert)
assert.Equal(t, "../testdata/localhost.key", c.key)
assert.Equal(t, 100, c.n)
assert.Equal(t, math.MaxInt32, c.n)
assert.Equal(t, 20, c.c)
assert.Equal(t, 5, c.qps)
assert.Equal(t, 5, c.skipFirst)
Expand Down Expand Up @@ -382,6 +381,7 @@ func TestRunConfig_newRunConfig(t *testing.T) {

t.Run("with config", func(t *testing.T) {
filename := "../testdata/config.json"

t.Run("from file", func(t *testing.T) {
c, err := newConfig("", "",
WithConfigFromFile(filename))
Expand All @@ -390,51 +390,76 @@ func TestRunConfig_newRunConfig(t *testing.T) {
assert.Equal(t, "0.0.0.0:50051", c.host)
assert.Equal(t, "../../testdata/greeter.proto", c.proto)
assert.Equal(t, []string{"../../testdata", "."}, c.importPaths)
assert.Equal(t, 5000, c.n)
assert.Equal(t, math.MaxInt32, c.n) // max-duration set so n ignored
assert.Equal(t, 50, c.c)
assert.Equal(t, 5, c.skipFirst)
assert.Equal(t, 12*time.Second, c.z)
assert.Equal(t, 7*time.Second, c.z) // max-duration (x) is set to z becomes x
assert.Equal(t, 500*time.Millisecond, c.streamInterval)
assert.Equal(t, []byte(`{"name":"Bob {{.TimestampUnix}}"}`), c.data)
assert.Equal(t, []byte(`{"rn":"{{.RequestNumber}}"}`), c.metadata)
assert.Equal(t, true, c.insecure)
})
file, _ := os.Open(filename)
defer file.Close()

t.Run("from file 2", func(t *testing.T) {
c, err := newConfig("", "",
WithConfigFromFile("../testdata/config5.toml"))
assert.Nil(t, err)
assert.Equal(t, "helloworld.Greeter.SayHello", c.call)
assert.Equal(t, "0.0.0.0:50051", c.host)
assert.Equal(t, "../../testdata/greeter.proto", c.proto)
assert.Equal(t, []string{"../../testdata", "."}, c.importPaths)
assert.Equal(t, 5000, c.n)
assert.Equal(t, 40, c.c)
assert.Equal(t, 100, c.skipFirst)
assert.Equal(t, time.Duration(0), c.z)
assert.Equal(t, time.Duration(0), c.streamInterval)
assert.Equal(t, []byte(`{"name":"Bob {{.TimestampUnix}}"}`), c.data)
assert.Equal(t, []byte(`{"rn":"{{.RequestNumber}}"}`), c.metadata)
assert.Equal(t, true, c.insecure)
})

t.Run("from reader", func(t *testing.T) {
file, _ := os.Open(filename)
defer file.Close()

c, err := newConfig("call", "localhost:50050",
WithConfigFromReader(file))
assert.Nil(t, err)
assert.Equal(t, "helloworld.Greeter.SayHello", c.call)
assert.Equal(t, "0.0.0.0:50051", c.host)
assert.Equal(t, "../../testdata/greeter.proto", c.proto)
assert.Equal(t, []string{"../../testdata", "."}, c.importPaths)
assert.Equal(t, 5000, c.n)
assert.Equal(t, math.MaxInt32, c.n) // max-duration set so n ignored
assert.Equal(t, 50, c.c)
assert.Equal(t, 5, c.skipFirst)
assert.Equal(t, 12*time.Second, c.z)
assert.Equal(t, 7*time.Second, c.z) // max-duration (x) is set to z becomes x
assert.Equal(t, 500*time.Millisecond, c.streamInterval)
assert.Equal(t, []byte(`{"name":"Bob {{.TimestampUnix}}"}`), c.data)
assert.Equal(t, []byte(`{"rn":"{{.RequestNumber}}"}`), c.metadata)
assert.Equal(t, true, c.insecure)
})

t.Run("from reader", func(t *testing.T) {
file, _ := os.Open(filename)
defer file.Close()

var config Config
_ = json.NewDecoder(file).Decode(&config)
c, err := newConfig("call", "localhost:50050",
WithConfig(&config))
assert.Nil(t, err)
assert.Equal(t, "helloworld.Greeter.SayHello", c.call)
assert.Equal(t, "0.0.0.0:50051", c.host)
assert.Equal(t, "../../testdata/greeter.proto", c.proto)
assert.Equal(t, []string{"../../testdata", "."}, c.importPaths)
assert.Equal(t, math.MaxInt32, c.n)
assert.Equal(t, 50, c.c)
assert.Equal(t, 5, c.skipFirst)
assert.Equal(t, 7*time.Second, c.z)
assert.Equal(t, 500*time.Millisecond, c.streamInterval)
assert.Equal(t, []byte(`{"name":"Bob {{.TimestampUnix}}"}`), c.data)
assert.Equal(t, []byte(`{"rn":"{{.RequestNumber}}"}`), c.metadata)
assert.Equal(t, true, c.insecure)
})
file, _ = os.Open(filename)
var config Config
_ = json.NewDecoder(file).Decode(&config)
c, err := newConfig("call", "localhost:50050",
WithConfig(&config))
assert.Nil(t, err)
assert.Equal(t, "helloworld.Greeter.SayHello", c.call)
assert.Equal(t, "0.0.0.0:50051", c.host)
assert.Equal(t, "../../testdata/greeter.proto", c.proto)
assert.Equal(t, []string{"../../testdata", "."}, c.importPaths)
assert.Equal(t, 5000, c.n)
assert.Equal(t, 50, c.c)
assert.Equal(t, 5, c.skipFirst)
assert.Equal(t, 12*time.Second, c.z)
assert.Equal(t, 500*time.Millisecond, c.streamInterval)
assert.Equal(t, []byte(`{"name":"Bob {{.TimestampUnix}}"}`), c.data)
assert.Equal(t, []byte(`{"rn":"{{.RequestNumber}}"}`), c.metadata)
assert.Equal(t, true, c.insecure)
})
}
87 changes: 87 additions & 0 deletions runner/run_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package runner

import (
"fmt"
"strconv"
"sync"
"testing"
Expand Down Expand Up @@ -159,6 +160,92 @@ func TestRunUnary(t *testing.T) {
assert.Equal(t, 1, connCount)
})

t.Run("test n default", func(t *testing.T) {
gs.ResetCounters()

data := make(map[string]interface{})
data["name"] = "bob"

report, err := Run(
"helloworld.Greeter.SayHello",
internal.TestLocalhost,
WithProtoFile("../testdata/greeter.proto", []string{}),
WithData(data),
WithInsecure(true),
)

assert.NoError(t, err)

assert.NotNil(t, report)

assert.Equal(t, 200, int(report.Count))
assert.NotZero(t, report.Average)
assert.NotZero(t, report.Fastest)
assert.NotZero(t, report.Slowest)
assert.NotZero(t, report.Rps)
assert.NotEmpty(t, report.Date)
assert.NotEmpty(t, report.Options)
assert.NotEmpty(t, report.Details)
assert.Equal(t, true, report.Options.Insecure)
assert.NotEmpty(t, report.LatencyDistribution)
assert.Equal(t, ReasonNormalEnd, report.EndReason)
assert.Empty(t, report.ErrorDist)

assert.NotEqual(t, report.Average, report.Slowest)
assert.NotEqual(t, report.Average, report.Fastest)
assert.NotEqual(t, report.Slowest, report.Fastest)

count := gs.GetCount(callType)
assert.Equal(t, 200, count)

connCount := gs.GetConnectionCount()
assert.Equal(t, 1, connCount)
})

t.Run("test run duration", func(t *testing.T) {
gs.ResetCounters()

data := make(map[string]interface{})
data["name"] = "bob"

report, err := Run(
"helloworld.Greeter.SayHello",
internal.TestLocalhost,
WithProtoFile("../testdata/greeter.proto", []string{}),
WithRunDuration(3*time.Second),
WithData(data),
WithInsecure(true),
)

assert.NoError(t, err)

assert.NotNil(t, report)

assert.True(t, int(report.Count) > 200, fmt.Sprintf("%d not > 200", int64(report.Count)))
assert.True(t, report.Total.Milliseconds() >= 3000 && report.Total.Milliseconds() < 3100, fmt.Sprintf("duration %d expected value", report.Total.Milliseconds()))
assert.NotZero(t, report.Average)
assert.NotZero(t, report.Fastest)
assert.NotZero(t, report.Slowest)
assert.NotZero(t, report.Rps)
assert.NotEmpty(t, report.Date)
assert.NotEmpty(t, report.Options)
assert.NotEmpty(t, report.Details)
assert.Equal(t, true, report.Options.Insecure)
assert.NotEmpty(t, report.LatencyDistribution)
assert.Equal(t, ReasonTimeout, report.EndReason)
// assert.Empty(t, report.ErrorDist)

assert.NotEqual(t, report.Average, report.Slowest)
assert.NotEqual(t, report.Average, report.Fastest)
assert.NotEqual(t, report.Slowest, report.Fastest)

// count := gs.GetCount(callType)
// assert.Equal(t, int64(report.Count), int64(count))

connCount := gs.GetConnectionCount()
assert.Equal(t, 1, connCount)
})

t.Run("test QPS", func(t *testing.T) {
gs.ResetCounters()

Expand Down
13 changes: 13 additions & 0 deletions testdata/config5.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
total = 5000
concurrency = 40
skipFirst = 100
proto = "../../testdata/greeter.proto"
call = "helloworld.Greeter.SayHello"
host = "0.0.0.0:50051"
insecure = true

[data]
name = "Bob {{.TimestampUnix}}"

[metadata]
rn = "{{.RequestNumber}}"

0 comments on commit b879715

Please sign in to comment.