Skip to content

Commit

Permalink
pillar/watcher: Add tests for runtime configuration updates in gorout…
Browse files Browse the repository at this point in the history
…ine leak detection.

Extended tests to validate that the goroutine leak detection respects
updates to configuration values at runtime. Specifically, the tests
confirm that adjustments to the `keepStatsFor` parameter dynamically
alter the stats slice size, with changes logged as expected.

Signed-off-by: Nikolay Martyanov <[email protected]>
  • Loading branch information
OhmSpectator committed Nov 8, 2024
1 parent cd7fafc commit f6ccdc1
Showing 1 changed file with 120 additions and 0 deletions.
120 changes: 120 additions & 0 deletions pkg/pillar/cmd/watcher/watcher_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@
package watcher

import (
"fmt"
"github.com/lf-edge/eve/pkg/pillar/agentlog"
"github.com/sirupsen/logrus"
"io"
"math"
"os"
Expand Down Expand Up @@ -340,5 +342,123 @@ func TestGoroutinesMonitorLeak(t *testing.T) {
if !strings.Contains(string(output), "leak detected") {
t.Errorf("Expected log output to contain 'leak detected'")
}
}

// Adjust stats slice size dynamically based on updated parameters
func TestGoroutinesMonitorUpdateParamsKeepStatsDecrease(t *testing.T) {
backupOut := logger.Out
bakcupLevel := logger.Level
// Create a pipe to capture log output
r, w, _ := os.Pipe()
logger.Out = w

logger.Level = logrus.TraceLevel

// Define a context with default parameters
ctx := &watcherContext{}

// Define parameters
goroutinesThreshold := 100
checkInterval := 1 * time.Millisecond
checkStatsFor := 10 * time.Millisecond
keepStatsFor := 24 * 60 * time.Millisecond
cooldownPeriod := 5 * time.Millisecond

// Set the parameters
ctx.GRLDParams.Set(goroutinesThreshold, checkInterval, checkStatsFor, keepStatsFor, cooldownPeriod)

go goroutinesMonitor(ctx)

// Wait until we fill the stats slice
time.Sleep(2 * keepStatsFor)

// Count the expected size of the stats slice
oldSize := int(keepStatsFor / checkInterval)

// Change the keepStatsFor parameter to force resizing of the stats slice
keepStatsFor /= 2

ctx.GRLDParams.Set(goroutinesThreshold, checkInterval, checkStatsFor, keepStatsFor, cooldownPeriod)

// Wait for several check intervals to allow the new context to be updated
time.Sleep(checkInterval * 100)

// Close the pipe
_ = w.Close()
output, _ := io.ReadAll(r)
logger.Out = backupOut
logger.Level = bakcupLevel

expectedNewSize := int(keepStatsFor / checkInterval)
expectedRemovedEntries := oldSize - expectedNewSize

// Define the expected log output with the new size
expectedMsgUpdate := fmt.Sprintf("Update entriesToKeep: %d", expectedNewSize)
expectedMsgRemove := fmt.Sprintf("Remove oldest entries: %d", expectedRemovedEntries)

expectedMsgs := []string{expectedMsgUpdate, expectedMsgRemove}

// Check if the log output contains the expected messages
for _, expectedMsg := range expectedMsgs {
if !strings.Contains(string(output), expectedMsg) {
t.Errorf("Expected log output to contain '%s'", expectedMsg)
}
}
}

// Adjust stats slice size dynamically based on updated parameters
func TestGoroutinesMonitorUpdateParamsKeepStatsIncrease(t *testing.T) {
backupOut := logger.Out
bakcupLevel := logger.Level
// Create a pipe to capture log output
r, w, _ := os.Pipe()
logger.Out = w

logger.Level = logrus.TraceLevel

// Define a context with default parameters
ctx := &watcherContext{}

// Define parameters
goroutinesThreshold := 100
checkInterval := 1 * time.Millisecond
checkStatsFor := 10 * time.Millisecond
keepStatsFor := 24 * 60 * time.Millisecond
cooldownPeriod := 5 * time.Millisecond

// Set the parameters
ctx.GRLDParams.Set(goroutinesThreshold, checkInterval, checkStatsFor, keepStatsFor, cooldownPeriod)

go goroutinesMonitor(ctx)

// Wait until we fill the stats slice
time.Sleep(2 * keepStatsFor)

// Change the keepStatsFor parameter to force resizing of the stats slice
keepStatsFor *= 2

ctx.GRLDParams.Set(goroutinesThreshold, checkInterval, checkStatsFor, keepStatsFor, cooldownPeriod)

// Wait for several check intervals to allow the new context to be updated
time.Sleep(checkInterval * 100)

// Close the pipe
_ = w.Close()
output, _ := io.ReadAll(r)
logger.Out = backupOut
logger.Level = bakcupLevel

expectedNewSize := int(keepStatsFor/checkInterval) + 1

// Define the expected log output with the new size
expectedMsgUpdate := fmt.Sprintf("Capacity insufficient; create a new slice: %d", expectedNewSize)

expectedMsgs := []string{expectedMsgUpdate}

// Check if the log output contains the expected messages
for _, expectedMsg := range expectedMsgs {
if !strings.Contains(string(output), expectedMsg) {
t.Errorf("Expected log output to contain '%s'", expectedMsg)
}
}
}

0 comments on commit f6ccdc1

Please sign in to comment.