From fcc5a64ce469bd662089f032fe383786ce0a147d Mon Sep 17 00:00:00 2001 From: shubham Date: Mon, 16 Nov 2020 20:36:57 +0530 Subject: [PATCH 1/2] Fix Progress gauge to reach 100% Signed-off-by: shubham --- gui/drawer.go | 22 ++++++++++++++-------- gui/drawer_test.go | 9 ++++++--- gui/keybinds.go | 4 +--- 3 files changed, 21 insertions(+), 14 deletions(-) diff --git a/gui/drawer.go b/gui/drawer.go index 81aaf69..d8a6b89 100644 --- a/gui/drawer.go +++ b/gui/drawer.go @@ -45,13 +45,14 @@ type values struct { // appendChartValues appends entities as soon as a result arrives. // Given maxSize, then it can be pre-allocated. -func (d *drawer) appendChartValues(ctx context.Context, maxSize int) { +func (d *drawer) appendChartValues(ctx context.Context, rate int, duration time.Duration) { // TODO: Change how to stop `redrawGauge`. // We currently use this way to ensure to stop `redrawGauge` after the increase process is complete. // But, it's preferable to stop goroutine where it's generated. + maxSize := rate * int(duration/time.Second) child, cancel := context.WithCancel(ctx) defer cancel() - go d.redrawGauge(child, maxSize) + go d.redrawGauge(child, duration) d.chartValues.latencies = make([]float64, 0, maxSize) d.chartValues.p50 = make([]float64, 0, maxSize) @@ -125,17 +126,22 @@ L: d.chartDrawing.Store(false) } -func (d *drawer) redrawGauge(ctx context.Context, maxSize int) { - var count float64 - size := float64(maxSize) +func (d *drawer) redrawGauge(ctx context.Context, duration time.Duration) { + totalTime := float64(duration) d.widgets.progressGauge.Percent(0) - for { + for start := time.Now(); ; { select { case <-ctx.Done(): return case <-d.gaugeCh: - count++ - d.widgets.progressGauge.Percent(int(count / size * 100)) + passed := float64(time.Since(start)) + percent := int(passed / totalTime * 100) + // as time.Duration is the unit of nanoseconds + // small duration can exceed 100 on slow machines + if percent > 100 { + percent = 100 + } + d.widgets.progressGauge.Percent(percent) } } } diff --git a/gui/drawer_test.go b/gui/drawer_test.go index f78957d..68fdff9 100644 --- a/gui/drawer_test.go +++ b/gui/drawer_test.go @@ -21,6 +21,7 @@ func TestAppendChartValues(t *testing.T) { tests := []struct { name string results []*attacker.Result + duration time.Duration progressGauge Gauge }{ { @@ -34,6 +35,7 @@ func TestAppendChartValues(t *testing.T) { P99: 990000, }, }, + duration: 1, progressGauge: func() Gauge { g := NewMockGauge(ctrl) g.EXPECT().Percent(gomock.Any()).AnyTimes() @@ -58,6 +60,7 @@ func TestAppendChartValues(t *testing.T) { P99: 1980000, }, }, + duration: 2, progressGauge: func() Gauge { g := NewMockGauge(ctrl) g.EXPECT().Percent(gomock.Any()).AnyTimes() @@ -75,7 +78,7 @@ func TestAppendChartValues(t *testing.T) { doneCh: make(chan struct{}), chartDrawing: atomic.NewBool(false), } - go d.appendChartValues(ctx, len(tt.results)) + go d.appendChartValues(ctx, len(tt.results), tt.duration) for _, res := range tt.results { d.chartCh <- res } @@ -155,7 +158,7 @@ func TestRedrawGauge(t *testing.T) { tests := []struct { name string - size int + size time.Duration count int gauge Gauge }{ @@ -178,7 +181,7 @@ func TestRedrawGauge(t *testing.T) { gaugeCh: make(chan struct{}), } go d.redrawGauge(ctx, tt.size) - for i := 0; i < tt.size; i++ { + for i := 0; i < int(tt.size); i++ { d.gaugeCh <- struct{}{} } }) diff --git a/gui/keybinds.go b/gui/keybinds.go index 9b3f797..df2ebd5 100644 --- a/gui/keybinds.go +++ b/gui/keybinds.go @@ -2,7 +2,6 @@ package gui import ( "context" - "time" "github.com/mum4k/termdash/container" "github.com/mum4k/termdash/keyboard" @@ -50,11 +49,10 @@ func attack(ctx context.Context, d *drawer, target string, opts attacker.Options if d.chartDrawing.Load() { return } - requestNum := opts.Rate * int(opts.Duration/time.Second) d.doneCh = make(chan struct{}) // To initialize, run redrawChart on a per-attack basis. - go d.appendChartValues(ctx, requestNum) + go d.appendChartValues(ctx, opts.Rate, opts.Duration) go d.redrawCharts(ctx) go func(ctx context.Context, d *drawer, t string, o attacker.Options) { attacker.Attack(ctx, t, d.chartCh, d.metricsCh, o) // this blocks until attack finishes From 7e4921b02675c0f9e71a66f04d3ffe8e2a6504ea Mon Sep 17 00:00:00 2001 From: shubham Date: Fri, 20 Nov 2020 11:59:05 +0530 Subject: [PATCH 2/2] addressed review comments --- gui/drawer.go | 11 ++++++----- gui/drawer_test.go | 10 ++-------- gui/gui.go | 1 - 3 files changed, 8 insertions(+), 14 deletions(-) diff --git a/gui/drawer.go b/gui/drawer.go index d8a6b89..d8ac7ab 100644 --- a/gui/drawer.go +++ b/gui/drawer.go @@ -23,7 +23,6 @@ type drawer struct { gridOpts *gridOpts chartCh chan *attacker.Result - gaugeCh chan struct{} metricsCh chan *attacker.Metrics doneCh chan struct{} @@ -75,8 +74,6 @@ L: if res == nil { continue } - // Increment the gauge. - d.gaugeCh <- struct{}{} d.mu.Lock() d.chartValues.latencies = appendValue(d.chartValues.latencies, res.Latency) @@ -127,19 +124,23 @@ L: } func (d *drawer) redrawGauge(ctx context.Context, duration time.Duration) { + ticker := time.NewTicker(redrawInterval) + defer ticker.Stop() + totalTime := float64(duration) + d.widgets.progressGauge.Percent(0) for start := time.Now(); ; { select { case <-ctx.Done(): return - case <-d.gaugeCh: + case <-ticker.C: passed := float64(time.Since(start)) percent := int(passed / totalTime * 100) // as time.Duration is the unit of nanoseconds // small duration can exceed 100 on slow machines if percent > 100 { - percent = 100 + continue } d.widgets.progressGauge.Percent(percent) } diff --git a/gui/drawer_test.go b/gui/drawer_test.go index 68fdff9..a84a8b9 100644 --- a/gui/drawer_test.go +++ b/gui/drawer_test.go @@ -74,7 +74,6 @@ func TestAppendChartValues(t *testing.T) { d := &drawer{ widgets: &widgets{progressGauge: tt.progressGauge}, chartCh: make(chan *attacker.Result), - gaugeCh: make(chan struct{}, 100), doneCh: make(chan struct{}), chartDrawing: atomic.NewBool(false), } @@ -132,7 +131,6 @@ func TestRedrawCharts(t *testing.T) { d := &drawer{ widgets: &widgets{latencyChart: tt.latencyChart, percentilesChart: tt.percentilesChart}, chartCh: make(chan *attacker.Result), - gaugeCh: make(chan struct{}, 100), doneCh: make(chan struct{}), chartDrawing: atomic.NewBool(false), chartValues: values{ @@ -167,8 +165,8 @@ func TestRedrawGauge(t *testing.T) { size: 1, gauge: func() Gauge { g := NewMockGauge(ctrl) - g.EXPECT().Percent(0) - g.EXPECT().Percent(100) + g.EXPECT().Percent(0, gomock.Any()).AnyTimes() + g.EXPECT().Percent(100, gomock.Any()).AnyTimes() return g }(), }, @@ -178,12 +176,8 @@ func TestRedrawGauge(t *testing.T) { t.Run(tt.name, func(t *testing.T) { d := &drawer{ widgets: &widgets{progressGauge: tt.gauge}, - gaugeCh: make(chan struct{}), } go d.redrawGauge(ctx, tt.size) - for i := 0; i < int(tt.size); i++ { - d.gaugeCh <- struct{}{} - } }) } } diff --git a/gui/gui.go b/gui/gui.go index 660f3fd..52e9a3b 100644 --- a/gui/gui.go +++ b/gui/gui.go @@ -69,7 +69,6 @@ func run(t terminalapi.Terminal, r runner, targetURL string, opts *attacker.Opti widgets: w, gridOpts: gridOpts, chartCh: make(chan *attacker.Result, 10000), - gaugeCh: make(chan struct{}), metricsCh: make(chan *attacker.Metrics), chartDrawing: atomic.NewBool(false), metrics: &attacker.Metrics{},