From 604eed334086d1db0cd84eac7fb6ace5ef9e6d46 Mon Sep 17 00:00:00 2001 From: "lilong.129" Date: Sat, 7 Jun 2025 16:16:55 +0800 Subject: [PATCH] refactor: optimize runner error handling and cleanup logic - Use defer for summary saving and HTML report generation to ensure they run regardless of exit path - Remove unnecessary sync.Once for cleanup operations since defer guarantees single execution - Simplify error handling logic by removing redundant runErr checks - Improve interrupt handling with better logging messages - Ensure graceful cleanup and data persistence even when interrupted --- internal/version/VERSION | 2 +- runner.go | 105 ++++++++++++++++++++------------------- 2 files changed, 56 insertions(+), 51 deletions(-) diff --git a/internal/version/VERSION b/internal/version/VERSION index d0e40a61..48732b9e 100644 --- a/internal/version/VERSION +++ b/internal/version/VERSION @@ -1 +1 @@ -v5.0.0-beta-2506071503 +v5.0.0-beta-2506071636 diff --git a/runner.go b/runner.go index 249ce9b5..997f3031 100644 --- a/runner.go +++ b/runner.go @@ -13,7 +13,6 @@ import ( "reflect" "strconv" "strings" - "sync" "syscall" "testing" "time" @@ -219,6 +218,31 @@ func (r *HRPRunner) Run(testcases ...ITestCase) (err error) { // record execution data to summary s := NewSummary() + // defer summary saving and HTML report generation + // this ensures they run regardless of how the function exits + defer func() { + s.Time.Duration = time.Since(s.Time.StartAt).Seconds() + log.Info().Int("duration(s)", int(s.Time.Duration)).Msg("run testcase finished") + + // save summary + if r.saveTests { + if summaryPath, saveErr := s.GenSummary(); saveErr != nil { + log.Error().Err(saveErr).Msg("failed to save summary") + } else { + log.Info().Str("path", summaryPath).Msg("summary saved successfully") + } + } + + // generate HTML report + if r.genHTMLReport { + if reportErr := s.GenHTMLReport(); reportErr != nil { + log.Error().Err(reportErr).Msg("failed to generate HTML report") + } else { + log.Info().Msg("HTML report generated successfully") + } + } + }() + // load all testcases testCases, err := LoadTestCases(testcases...) if err != nil { @@ -228,39 +252,36 @@ func (r *HRPRunner) Run(testcases ...ITestCase) (err error) { // collect all MCP hosts for cleanup var mcpHosts []*mcphost.MCPHost - var cleanupOnce sync.Once // quit all plugins and close MCP hosts defer func() { - cleanupOnce.Do(func() { - pluginMap.Range(func(key, value interface{}) bool { - if plugin, ok := value.(funplugin.IPlugin); ok { - plugin.Quit() - } - return true - }) - - // Close all MCP hosts with timeout - if len(mcpHosts) > 0 { - done := make(chan struct{}) - go func() { - defer close(done) - for _, host := range mcpHosts { - if host != nil { - host.Shutdown() - } - } - }() - - // Wait for cleanup with timeout - select { - case <-done: - log.Debug().Msg("All MCP hosts cleaned up successfully") - case <-time.After(10 * time.Second): - log.Warn().Msg("MCP hosts cleanup timeout") - } + pluginMap.Range(func(key, value interface{}) bool { + if plugin, ok := value.(funplugin.IPlugin); ok { + plugin.Quit() } + return true }) + + // Close all MCP hosts with timeout + if len(mcpHosts) > 0 { + done := make(chan struct{}) + go func() { + defer close(done) + for _, host := range mcpHosts { + if host != nil { + host.Shutdown() + } + } + }() + + // Wait for cleanup with timeout + select { + case <-done: + log.Debug().Msg("All MCP hosts cleaned up successfully") + case <-time.After(10 * time.Second): + log.Warn().Msg("MCP hosts cleanup timeout") + } + } }() var runErr error @@ -290,8 +311,8 @@ func (r *HRPRunner) Run(testcases ...ITestCase) (err error) { // check for interrupt signal before each iteration select { case <-r.interruptSignal: - log.Warn().Msg("interrupted in main runner") - return errors.Wrap(code.InterruptError, "main runner interrupted") + log.Warn().Msg("interrupted in parameter iteration") + return errors.Wrap(code.InterruptError, "parameter iteration interrupted") default: } @@ -302,27 +323,11 @@ func (r *HRPRunner) Run(testcases ...ITestCase) (err error) { s.AddCaseSummary(caseSummary) if err != nil { log.Error().Err(err).Msg("[Run] run testcase failed") + if r.failfast { + return err + } runErr = err } - - if runErr != nil && r.failfast { - break - } - } - } - s.Time.Duration = time.Since(s.Time.StartAt).Seconds() - - // save summary - if r.saveTests { - if _, err := s.GenSummary(); err != nil { - return err - } - } - - // generate HTML report - if r.genHTMLReport { - if err := s.GenHTMLReport(); err != nil { - return err } }