mirror of
https://github.com/httprunner/httprunner.git
synced 2026-05-07 08:02:42 +08:00
- 为 AI 相关方法添加 context.Context 参数支持中断 - 在重试循环中添加上下文取消检查 - 创建可取消的上下文并监听中断信号 - 更新 MCP 工具调用使用带上下文的方法 现在用户可以通过 CTRL+C 正常中断长时间运行的 AI 自动化任务
96 lines
2.4 KiB
Go
96 lines
2.4 KiB
Go
package hrp
|
|
|
|
import (
|
|
"fmt"
|
|
"time"
|
|
|
|
"github.com/rs/zerolog/log"
|
|
|
|
"github.com/httprunner/httprunner/v5/internal/builtin"
|
|
)
|
|
|
|
type ThinkTime struct {
|
|
Time float64 `json:"time" yaml:"time"`
|
|
}
|
|
|
|
// StepThinkTime implements IStep interface.
|
|
type StepThinkTime struct {
|
|
StepConfig
|
|
ThinkTime *ThinkTime `json:"think_time,omitempty" yaml:"think_time,omitempty"`
|
|
}
|
|
|
|
func (s *StepThinkTime) Name() string {
|
|
return s.StepName
|
|
}
|
|
|
|
func (s *StepThinkTime) Type() StepType {
|
|
return StepTypeThinkTime
|
|
}
|
|
|
|
func (s *StepThinkTime) Config() *StepConfig {
|
|
return &s.StepConfig
|
|
}
|
|
|
|
func (s *StepThinkTime) Run(r *SessionRunner) (*StepResult, error) {
|
|
thinkTime := s.ThinkTime
|
|
log.Info().Float64("time", thinkTime.Time).Msg("think time")
|
|
|
|
stepResult := &StepResult{
|
|
Name: s.Name(),
|
|
StepType: s.Type(),
|
|
Success: true,
|
|
}
|
|
|
|
cfg := r.caseRunner.Config.Get().ThinkTimeSetting
|
|
if cfg == nil {
|
|
cfg = &ThinkTimeConfig{ThinkTimeDefault, nil, 0}
|
|
}
|
|
|
|
var tt time.Duration
|
|
switch cfg.Strategy {
|
|
case ThinkTimeDefault:
|
|
tt = time.Duration(thinkTime.Time*1000) * time.Millisecond
|
|
case ThinkTimeRandomPercentage:
|
|
// e.g. {"min_percentage": 0.5, "max_percentage": 1.5}
|
|
m, ok := cfg.Setting.(map[string]float64)
|
|
if !ok {
|
|
tt = time.Duration(thinkTime.Time*1000) * time.Millisecond
|
|
break
|
|
}
|
|
res := builtin.GetRandomNumber(int(thinkTime.Time*m["min_percentage"]*1000), int(thinkTime.Time*m["max_percentage"]*1000))
|
|
tt = time.Duration(res) * time.Millisecond
|
|
case ThinkTimeMultiply:
|
|
value, ok := cfg.Setting.(float64) // e.g. 0.5
|
|
if !ok || value <= 0 {
|
|
value = thinkTimeDefaultMultiply
|
|
}
|
|
tt = time.Duration(thinkTime.Time*value*1000) * time.Millisecond
|
|
case ThinkTimeIgnore:
|
|
// nothing to do
|
|
}
|
|
|
|
// no more than limit
|
|
if cfg.Limit > 0 {
|
|
limit := time.Duration(cfg.Limit*1000) * time.Millisecond
|
|
if limit < tt {
|
|
tt = limit
|
|
}
|
|
}
|
|
|
|
// Use interruptible sleep that can respond to signals
|
|
log.Debug().Float64("duration_ms", float64(tt.Milliseconds())).Msg("starting think time")
|
|
|
|
select {
|
|
case <-time.After(tt):
|
|
// Normal completion
|
|
log.Debug().Float64("duration_ms", float64(tt.Milliseconds())).Msg("think time completed normally")
|
|
case <-r.caseRunner.hrpRunner.interruptSignal:
|
|
// Interrupted by signal
|
|
log.Info().Float64("planned_duration_ms", float64(tt.Milliseconds())).
|
|
Msg("think time interrupted by signal")
|
|
return stepResult, fmt.Errorf("think time interrupted")
|
|
}
|
|
|
|
return stepResult, nil
|
|
}
|