mirror of
https://github.com/httprunner/httprunner.git
synced 2026-07-07 23:41:22 +08:00
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
This commit is contained in:
@@ -1 +1 @@
|
|||||||
v5.0.0-beta-2506071503
|
v5.0.0-beta-2506071636
|
||||||
|
|||||||
105
runner.go
105
runner.go
@@ -13,7 +13,6 @@ import (
|
|||||||
"reflect"
|
"reflect"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
|
||||||
"syscall"
|
"syscall"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
@@ -219,6 +218,31 @@ func (r *HRPRunner) Run(testcases ...ITestCase) (err error) {
|
|||||||
// record execution data to summary
|
// record execution data to summary
|
||||||
s := NewSummary()
|
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
|
// load all testcases
|
||||||
testCases, err := LoadTestCases(testcases...)
|
testCases, err := LoadTestCases(testcases...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -228,39 +252,36 @@ func (r *HRPRunner) Run(testcases ...ITestCase) (err error) {
|
|||||||
|
|
||||||
// collect all MCP hosts for cleanup
|
// collect all MCP hosts for cleanup
|
||||||
var mcpHosts []*mcphost.MCPHost
|
var mcpHosts []*mcphost.MCPHost
|
||||||
var cleanupOnce sync.Once
|
|
||||||
|
|
||||||
// quit all plugins and close MCP hosts
|
// quit all plugins and close MCP hosts
|
||||||
defer func() {
|
defer func() {
|
||||||
cleanupOnce.Do(func() {
|
pluginMap.Range(func(key, value interface{}) bool {
|
||||||
pluginMap.Range(func(key, value interface{}) bool {
|
if plugin, ok := value.(funplugin.IPlugin); ok {
|
||||||
if plugin, ok := value.(funplugin.IPlugin); ok {
|
plugin.Quit()
|
||||||
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")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
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
|
var runErr error
|
||||||
@@ -290,8 +311,8 @@ func (r *HRPRunner) Run(testcases ...ITestCase) (err error) {
|
|||||||
// check for interrupt signal before each iteration
|
// check for interrupt signal before each iteration
|
||||||
select {
|
select {
|
||||||
case <-r.interruptSignal:
|
case <-r.interruptSignal:
|
||||||
log.Warn().Msg("interrupted in main runner")
|
log.Warn().Msg("interrupted in parameter iteration")
|
||||||
return errors.Wrap(code.InterruptError, "main runner interrupted")
|
return errors.Wrap(code.InterruptError, "parameter iteration interrupted")
|
||||||
default:
|
default:
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -302,27 +323,11 @@ func (r *HRPRunner) Run(testcases ...ITestCase) (err error) {
|
|||||||
s.AddCaseSummary(caseSummary)
|
s.AddCaseSummary(caseSummary)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error().Err(err).Msg("[Run] run testcase failed")
|
log.Error().Err(err).Msg("[Run] run testcase failed")
|
||||||
|
if r.failfast {
|
||||||
|
return err
|
||||||
|
}
|
||||||
runErr = 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
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user