diff --git a/lib/gocannon.go b/lib/gocannon.go index fe7dcfe..f4b6251 100644 --- a/lib/gocannon.go +++ b/lib/gocannon.go @@ -15,19 +15,35 @@ type Gocannon struct { plugin common.GocannonPlugin } +// Option updates a Gocannon +type Option func(*Gocannon) + +// WithPlugin defines the plugin to be used by Gocannon +func WithPlugin(plugin common.GocannonPlugin) Option { + return func(gc *Gocannon) { + gc.plugin = plugin + } +} + // NewGocannon creates a new gocannon instance using a provided config. -func NewGocannon(cfg common.Config) (Gocannon, error) { +func NewGocannon(cfg common.Config, opts ...Option) (Gocannon, error) { var err error gocannon := Gocannon{cfg: cfg} - if *cfg.Plugin != "" { + for _, o := range opts { + o(&gocannon) + } + + if gocannon.plugin == nil && *cfg.Plugin != "" { gocannonPlugin, err := loadPlugin(*cfg.Plugin, *cfg.Format != "default") if err != nil { return gocannon, err } gocannon.plugin = gocannonPlugin - gocannonPlugin.Startup(cfg) + } + if gocannon.plugin != nil { + gocannon.plugin.Startup(cfg) } c, err := newHTTPClient(*cfg.Target, *cfg.Timeout, *cfg.Connections, *cfg.TrustAll, true) diff --git a/lib/integration_test.go b/lib/integration_test.go index f1d4504..ddf7b18 100644 --- a/lib/integration_test.go +++ b/lib/integration_test.go @@ -147,6 +147,12 @@ func TestGocannonDefaultValues(t *testing.T) { func TestGocanonWithPlugin(t *testing.T) { + if runtime.GOOS == "darwin" && runtime.GOARCH == "arm64" { + t.SkipNow() + } else if runtime.GOOS == "windows" { + t.SkipNow() + } + err := exec.Command("go", "build", "-race", "-buildmode=plugin", "-o", "../_example_plugin/plugin.so", "../_example_plugin/plugin.go"). Run() @@ -204,3 +210,84 @@ func TestGocanonWithPlugin(t *testing.T) { } } + +type TestLibraryPlugin struct { + cfg common.Config + StartupCalled bool + BeforeRequestCalled bool + GetNameCalled bool +} + +func (p *TestLibraryPlugin) Startup(cfg common.Config) { + p.cfg = cfg + p.StartupCalled = true +} + +func (p *TestLibraryPlugin) BeforeRequest(cid int) (target string, method string, body common.RawRequestBody, headers common.RequestHeaders) { + headers = *p.cfg.Headers + method = *p.cfg.Method + body = *p.cfg.Body + target = *p.cfg.Target + method = *p.cfg.Method + return +} + +func (p *TestLibraryPlugin) GetName() string { + return "TestLibraryPlugin" +} + +func TestGocanonWithLibraryPlugin(t *testing.T) { + duration := time.Second * 1 + connections := 50 + cpus := runtime.NumCPU() + timeout := time.Millisecond * 200 + mode := "hist" + outputFile := "" + interval := time.Millisecond * 250 + preallocate := 1000 + method := "GET" + body := common.RawRequestBody{} + header := common.RequestHeaders{} + trustAll := true + format := "json" + plugin := "" + target := "http://localhost:3000/hello" + + cfg := common.Config{ + Duration: &duration, + Connections: &connections, + CPUs: &cpus, + Timeout: &timeout, + Mode: &mode, + OutputFile: &outputFile, + Interval: &interval, + Preallocate: &preallocate, + Method: &method, + Body: &body, + Headers: &header, + TrustAll: &trustAll, + Format: &format, + Plugin: &plugin, + Target: &target, + } + + libplugin := &TestLibraryPlugin{} + + g, creationErr := NewGocannon(cfg, WithPlugin(libplugin)) + + assert.Nil(t, creationErr, "gocannon instance with a plugin should be created without errors") + + if creationErr == nil { + results, execErr := g.Run() + + assert.Nil(t, execErr, "the load test should be completed without errors") + + assert.Greater( + t, + results.GetReqPerSec(), + 100.0, + "a throughput of at least 100 req/s should be achieved", + ) + } + +}