diff --git a/hrp/cmd/boom.go b/hrp/cmd/boom.go index 22d8782d..5ae038e1 100644 --- a/hrp/cmd/boom.go +++ b/hrp/cmd/boom.go @@ -1,12 +1,15 @@ package cmd import ( + "os" "time" + "github.com/rs/zerolog/log" "github.com/spf13/cobra" "github.com/httprunner/httprunner/v4/hrp" "github.com/httprunner/httprunner/v4/hrp/internal/boomer" + "github.com/httprunner/httprunner/v4/hrp/internal/builtin" ) // boomCmd represents the boom command @@ -28,57 +31,70 @@ var boomCmd = &cobra.Command{ path := hrp.TestCasePath(arg) paths = append(paths, &path) } - hrpBoomer := hrp.NewBoomer(spawnCount, spawnRate) - hrpBoomer.SetRateLimiter(maxRPS, requestIncreaseRate) - if loopCount > 0 { - hrpBoomer.SetLoopCount(loopCount) + // if set profile, the priority is higher than the other commands + if boomArgs.profile != "" { + err := builtin.LoadFile(boomArgs.profile, &boomArgs) + if err != nil { + log.Error().Err(err).Msg("failed to load profile") + os.Exit(1) + } } - if !disableConsoleOutput { + + hrpBoomer := hrp.NewBoomer(boomArgs.SpawnCount, boomArgs.SpawnRate) + hrpBoomer.SetRateLimiter(boomArgs.MaxRPS, boomArgs.RequestIncreaseRate) + if boomArgs.LoopCount > 0 { + hrpBoomer.SetLoopCount(boomArgs.LoopCount) + } + if !boomArgs.DisableConsoleOutput { hrpBoomer.AddOutput(boomer.NewConsoleOutput()) } - if prometheusPushgatewayURL != "" { - hrpBoomer.AddOutput(boomer.NewPrometheusPusherOutput(prometheusPushgatewayURL, "hrp", hrpBoomer.GetMode())) + if boomArgs.PrometheusPushgatewayURL != "" { + hrpBoomer.AddOutput(boomer.NewPrometheusPusherOutput(boomArgs.PrometheusPushgatewayURL, "hrp", hrpBoomer.GetMode())) } - hrpBoomer.SetDisableKeepAlive(disableKeepalive) - hrpBoomer.SetDisableCompression(disableCompression) + hrpBoomer.SetDisableKeepAlive(boomArgs.DisableKeepalive) + hrpBoomer.SetDisableCompression(boomArgs.DisableCompression) hrpBoomer.SetClientTransport() - hrpBoomer.EnableCPUProfile(cpuProfile, cpuProfileDuration) - hrpBoomer.EnableMemoryProfile(memoryProfile, memoryProfileDuration) + hrpBoomer.EnableCPUProfile(boomArgs.CPUProfile, boomArgs.CPUProfileDuration) + hrpBoomer.EnableMemoryProfile(boomArgs.MemoryProfile, boomArgs.MemoryProfileDuration) hrpBoomer.EnableGracefulQuit() hrpBoomer.Run(paths...) }, } -var ( - spawnCount int - spawnRate float64 - maxRPS int64 - loopCount int64 - requestIncreaseRate string - memoryProfile string - memoryProfileDuration time.Duration - cpuProfile string - cpuProfileDuration time.Duration - prometheusPushgatewayURL string - disableConsoleOutput bool - disableCompression bool - disableKeepalive bool -) +type BoomArgs struct { + SpawnCount int `json:"spawn-count,omitempty" yaml:"spawn-count,omitempty"` + SpawnRate float64 `json:"spawn-rate,omitempty" yaml:"spawn-rate,omitempty"` + MaxRPS int64 `json:"max-rps,omitempty" yaml:"max-rps,omitempty"` + LoopCount int64 `json:"loop-count,omitempty" yaml:"loop-count,omitempty"` + RequestIncreaseRate string `json:"request-increase-rate,omitempty" yaml:"request-increase-rate,omitempty"` + MemoryProfile string `json:"memory-profile,omitempty" yaml:"memory-profile,omitempty"` + MemoryProfileDuration time.Duration `json:"memory-profile-duration" yaml:"memory-profile-duration"` + CPUProfile string `json:"cpu-profile,omitempty" yaml:"cpu-profile,omitempty"` + CPUProfileDuration time.Duration `json:"cpu-profile-duration,omitempty" yaml:"cpu-profile-duration,omitempty"` + PrometheusPushgatewayURL string `json:"prometheus-gateway,omitempty" yaml:"prometheus-gateway,omitempty"` + DisableConsoleOutput bool `json:"disable-console-output,omitempty" yaml:"disable-console-output,omitempty"` + DisableCompression bool `json:"disable-compression,omitempty" yaml:"disable-compression,omitempty"` + DisableKeepalive bool `json:"disable-keepalive,omitempty" yaml:"disable-keepalive,omitempty"` + profile string +} + +var boomArgs BoomArgs func init() { rootCmd.AddCommand(boomCmd) - boomCmd.Flags().Int64Var(&maxRPS, "max-rps", 0, "Max RPS that boomer can generate, disabled by default.") - boomCmd.Flags().StringVar(&requestIncreaseRate, "request-increase-rate", "-1", "Request increase rate, disabled by default.") - boomCmd.Flags().IntVar(&spawnCount, "spawn-count", 1, "The number of users to spawn for load testing") - boomCmd.Flags().Float64Var(&spawnRate, "spawn-rate", 1, "The rate for spawning users") - boomCmd.Flags().Int64Var(&loopCount, "loop-count", -1, "The specify running cycles for load testing") - boomCmd.Flags().StringVar(&memoryProfile, "mem-profile", "", "Enable memory profiling.") - boomCmd.Flags().DurationVar(&memoryProfileDuration, "mem-profile-duration", 30*time.Second, "Memory profile duration.") - boomCmd.Flags().StringVar(&cpuProfile, "cpu-profile", "", "Enable CPU profiling.") - boomCmd.Flags().DurationVar(&cpuProfileDuration, "cpu-profile-duration", 30*time.Second, "CPU profile duration.") - boomCmd.Flags().StringVar(&prometheusPushgatewayURL, "prometheus-gateway", "", "Prometheus Pushgateway url.") - boomCmd.Flags().BoolVar(&disableConsoleOutput, "disable-console-output", false, "Disable console output.") - boomCmd.Flags().BoolVar(&disableCompression, "disable-compression", false, "Disable compression") - boomCmd.Flags().BoolVar(&disableKeepalive, "disable-keepalive", false, "Disable keepalive") + boomCmd.Flags().Int64Var(&boomArgs.MaxRPS, "max-rps", 0, "Max RPS that boomer can generate, disabled by default.") + boomCmd.Flags().StringVar(&boomArgs.RequestIncreaseRate, "request-increase-rate", "-1", "Request increase rate, disabled by default.") + boomCmd.Flags().IntVar(&boomArgs.SpawnCount, "spawn-count", 1, "The number of users to spawn for load testing") + boomCmd.Flags().Float64Var(&boomArgs.SpawnRate, "spawn-rate", 1, "The rate for spawning users") + boomCmd.Flags().Int64Var(&boomArgs.LoopCount, "loop-count", -1, "The specify running cycles for load testing") + boomCmd.Flags().StringVar(&boomArgs.MemoryProfile, "mem-profile", "", "Enable memory profiling.") + boomCmd.Flags().DurationVar(&boomArgs.MemoryProfileDuration, "mem-profile-duration", 30*time.Second, "Memory profile duration.") + boomCmd.Flags().StringVar(&boomArgs.CPUProfile, "cpu-profile", "", "Enable CPU profiling.") + boomCmd.Flags().DurationVar(&boomArgs.CPUProfileDuration, "cpu-profile-duration", 30*time.Second, "CPU profile duration.") + boomCmd.Flags().StringVar(&boomArgs.PrometheusPushgatewayURL, "prometheus-gateway", "", "Prometheus Pushgateway url.") + boomCmd.Flags().BoolVar(&boomArgs.DisableConsoleOutput, "disable-console-output", false, "Disable console output.") + boomCmd.Flags().BoolVar(&boomArgs.DisableCompression, "disable-compression", false, "Disable compression") + boomCmd.Flags().BoolVar(&boomArgs.DisableKeepalive, "disable-keepalive", false, "Disable keepalive") + boomCmd.Flags().StringVar(&boomArgs.profile, "profile", "", "profile for load testing") } diff --git a/hrp/internal/scaffold/main.go b/hrp/internal/scaffold/main.go index ca505d68..ba6a1c24 100644 --- a/hrp/internal/scaffold/main.go +++ b/hrp/internal/scaffold/main.go @@ -6,6 +6,7 @@ import ( "os" "os/exec" "path/filepath" + "time" "github.com/httprunner/funplugin/shared" "github.com/pkg/errors" @@ -13,6 +14,7 @@ import ( "github.com/httprunner/httprunner/v4/hrp/internal/builtin" "github.com/httprunner/httprunner/v4/hrp/internal/sdk" + "github.com/httprunner/httprunner/v4/hrp/internal/version" ) type PluginType string @@ -23,6 +25,12 @@ const ( Go PluginType = "go" ) +type ProjectInfo struct { + ProjectName string `json:"project_name,omitempty" yaml:"project_name,omitempty"` + CreateTime time.Time `json:"create_time,omitempty" yaml:"create_time,omitempty"` + Version string `json:"hrp_version,omitempty" yaml:"hrp_version,omitempty"` +} + //go:embed templates/* var templatesDir embed.FS @@ -88,8 +96,17 @@ func CreateScaffold(projectName string, pluginType PluginType, force bool) error return err } + projectInfo := &ProjectInfo{ + ProjectName: projectName, + CreateTime: time.Now(), + Version: version.VERSION, + } + + // dump project information to file + err := builtin.Dump2JSON(projectInfo, filepath.Join(projectName, "proj.json")) + // create .gitignore - err := CopyFile("templates/gitignore", filepath.Join(projectName, ".gitignore")) + err = CopyFile("templates/gitignore", filepath.Join(projectName, ".gitignore")) if err != nil { return err } diff --git a/hrp/parameters.go b/hrp/parameters.go index 06007025..376232fa 100644 --- a/hrp/parameters.go +++ b/hrp/parameters.go @@ -20,9 +20,9 @@ type TParamsConfig struct { type iteratorPickOrder string const ( - strategySequential iteratorPickOrder = "sequential" - strategyRandom iteratorPickOrder = "random" - strategyUnique iteratorPickOrder = "unique" + pickOrderSequential iteratorPickOrder = "sequential" + pickOrderRandom iteratorPickOrder = "random" + pickOrderUnique iteratorPickOrder = "unique" ) /* @@ -67,15 +67,15 @@ func newParametersIterator(parameters map[string]Parameters, config *TParamsConf parametersList := make([]Parameters, 0) for paramName := range parameters { - // check parameter individual strategy + // check parameter individual pick order strategy strategy, ok := config.Strategies[paramName] if !ok { - // default to overall pick order + // default to overall pick order strategy strategy.PickOrder = config.PickOrder } - // group parameters by strategy - if strategy.PickOrder == strategyRandom { + // group parameters by pick order strategy + if strategy.PickOrder == pickOrderRandom { iterator.randomParameterNames = append(iterator.randomParameterNames, paramName) } else { parametersList = append(parametersList, parameters[paramName]) diff --git a/hrp/plugin.go b/hrp/plugin.go index 7929d030..ab2f34e6 100644 --- a/hrp/plugin.go +++ b/hrp/plugin.go @@ -118,7 +118,7 @@ func locateFile(startPath string, destFile string) (string, error) { return locateFile(parentDir, destFile) } -func getProjectRootDirPath(path string) (rootDir string, err error) { +func GetProjectRootDirPath(path string) (rootDir string, err error) { pluginPath, err := locatePlugin(path) if err == nil { rootDir = filepath.Dir(pluginPath) diff --git a/hrp/testcase.go b/hrp/testcase.go index 05c2b051..6cefbbb7 100644 --- a/hrp/testcase.go +++ b/hrp/testcase.go @@ -75,7 +75,7 @@ func (path *TestCasePath) ToTestCase() (*TestCase, error) { } // locate project root dir by plugin path - projectRootDir, err := getProjectRootDirPath(casePath) + projectRootDir, err := GetProjectRootDirPath(casePath) if err != nil { return nil, errors.Wrap(err, "failed to get project root dir") }