refactor: move hrp/ to root folder

This commit is contained in:
lilong.129
2025-02-06 10:52:08 +08:00
parent 9376692b71
commit 1f063dd6f7
221 changed files with 206 additions and 211 deletions

64
cmd/adb/devices.go Normal file
View File

@@ -0,0 +1,64 @@
package adb
import (
"encoding/json"
"fmt"
"os"
"strings"
"time"
"github.com/spf13/cobra"
"github.com/httprunner/httprunner/v5/internal/sdk"
"github.com/httprunner/httprunner/v5/pkg/uixt"
)
func format(data map[string]string) string {
result, _ := json.MarshalIndent(data, "", "\t")
return string(result)
}
var listAndroidDevicesCmd = &cobra.Command{
Use: "devices",
Short: "List all Android devices",
RunE: func(cmd *cobra.Command, args []string) (err error) {
startTime := time.Now()
defer func() {
sdk.SendGA4Event("hrp_adb_devices", map[string]interface{}{
"args": strings.Join(args, "-"),
"success": err == nil,
"engagement_time_msec": time.Since(startTime).Milliseconds(),
})
}()
deviceList, err := uixt.GetAndroidDevices(serial)
if err != nil {
fmt.Println(err)
os.Exit(0)
}
for _, d := range deviceList {
if isDetail {
fmt.Println(format(d.DeviceInfo()))
} else {
if usb, err := d.Usb(); err != nil {
fmt.Println(d.Serial())
} else {
fmt.Println(d.Serial(), usb)
}
}
}
return nil
},
}
var (
serial string
isDetail bool
)
func init() {
listAndroidDevicesCmd.Flags().StringVarP(&serial, "serial", "s", "", "filter by device's serial")
listAndroidDevicesCmd.Flags().BoolVarP(&isDetail, "detail", "d", false, "print device's detail")
androidRootCmd.AddCommand(listAndroidDevicesCmd)
}

31
cmd/adb/init.go Normal file
View File

@@ -0,0 +1,31 @@
package adb
import (
"fmt"
"github.com/spf13/cobra"
"github.com/httprunner/httprunner/v5/pkg/gadb"
"github.com/httprunner/httprunner/v5/pkg/uixt"
)
var androidRootCmd = &cobra.Command{
Use: "adb",
Short: "simple utils for android device management",
PersistentPreRun: func(cmd *cobra.Command, args []string) {},
}
func getDevice(serial string) (*gadb.Device, error) {
devices, err := uixt.GetAndroidDevices(serial)
if err != nil {
return nil, err
}
if len(devices) > 1 {
return nil, fmt.Errorf("found multiple attached devices, please specify android serial")
}
return devices[0], nil
}
func Init(rootCmd *cobra.Command) {
rootCmd.AddCommand(androidRootCmd)
}

69
cmd/adb/install.go Normal file
View File

@@ -0,0 +1,69 @@
package adb
import (
"fmt"
"strings"
"time"
"github.com/spf13/cobra"
"github.com/httprunner/httprunner/v5/internal/sdk"
"github.com/httprunner/httprunner/v5/pkg/uixt"
)
var (
replace bool
downgrade bool
grant bool
)
var installCmd = &cobra.Command{
Use: "install [flags] PACKAGE",
Short: "push package to the device and install them automatically",
Args: cobra.MinimumNArgs(1),
RunE: func(cmd *cobra.Command, args []string) (err error) {
startTime := time.Now()
defer func() {
sdk.SendGA4Event("hrp_adb_devices", map[string]interface{}{
"args": strings.Join(args, "-"),
"success": err == nil,
"engagement_time_msec": time.Since(startTime).Milliseconds(),
})
}()
_, err = getDevice(serial)
if err != nil {
return err
}
device, err := uixt.NewAndroidDevice(uixt.WithSerialNumber(serial))
if err != nil {
fmt.Println(err)
return err
}
driverExt, err := device.NewDriver()
if err != nil {
fmt.Println(err)
return err
}
err = driverExt.Install(args[0],
uixt.WithReinstall(replace),
uixt.WithDowngrade(downgrade),
uixt.WithGrantPermission(grant),
)
if err != nil {
fmt.Println(err)
return err
}
fmt.Println("success")
return nil
},
}
func init() {
installCmd.Flags().StringVarP(&serial, "serial", "s", "", "filter by device's serial")
installCmd.Flags().BoolVarP(&replace, "replace", "r", false, "replace existing application")
installCmd.Flags().BoolVarP(&downgrade, "downgrade", "d", false, "allow version code downgrade (debuggable packages only)")
installCmd.Flags().BoolVarP(&grant, "grant", "g", false, "grant all runtime permissions")
androidRootCmd.AddCommand(installCmd)
}

50
cmd/adb/screencap.go Normal file
View File

@@ -0,0 +1,50 @@
package adb
import (
"fmt"
"os"
"strings"
"time"
"github.com/spf13/cobra"
"github.com/httprunner/httprunner/v5/internal/builtin"
"github.com/httprunner/httprunner/v5/internal/sdk"
)
var screencapAndroidDevicesCmd = &cobra.Command{
Use: "screencap",
Short: "Start android screen capture",
RunE: func(cmd *cobra.Command, args []string) (err error) {
startTime := time.Now()
defer func() {
sdk.SendGA4Event("hrp_adb_screencap", map[string]interface{}{
"args": strings.Join(args, "-"),
"success": err == nil,
"engagement_time_msec": time.Since(startTime).Milliseconds(),
})
}()
device, err := getDevice(serial)
if err != nil {
return err
}
res, err := device.ScreenCap()
if err != nil {
return err
}
filepath := fmt.Sprintf("%s.png", builtin.GenNameWithTimestamp("screencap_%d"))
if err = os.WriteFile(filepath, res, 0o644); err != nil {
return err
}
fmt.Println("screencap saved to", filepath)
return nil
},
}
func init() {
screencapAndroidDevicesCmd.Flags().StringVarP(&serial, "serial", "s", "", "filter by device's serial")
androidRootCmd.AddCommand(screencapAndroidDevicesCmd)
}

39
cmd/build.go Normal file
View File

@@ -0,0 +1,39 @@
package cmd
import (
"strings"
"time"
"github.com/spf13/cobra"
hrp "github.com/httprunner/httprunner/v5"
"github.com/httprunner/httprunner/v5/internal/sdk"
)
var buildCmd = &cobra.Command{
Use: "build $path ...",
Short: "build plugin for testing",
Long: `build python/go plugin for testing`,
Example: ` $ hrp build plugin/debugtalk.go
$ hrp build plugin/debugtalk.py`,
Args: cobra.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) (err error) {
startTime := time.Now()
defer func() {
sdk.SendGA4Event("hrp_build", map[string]interface{}{
"args": strings.Join(args, "-"),
"success": err == nil,
"engagement_time_msec": time.Since(startTime).Milliseconds(),
})
}()
return hrp.BuildPlugin(args[0], output)
},
}
var output string
func init() {
rootCmd.AddCommand(buildCmd)
buildCmd.Flags().StringVarP(&output, "output", "o", "", "funplugin product output path, default: cwd")
}

26
cmd/cli/main.go Normal file
View File

@@ -0,0 +1,26 @@
package main
import (
"os"
"time"
"github.com/getsentry/sentry-go"
"github.com/httprunner/httprunner/v5/cmd"
)
func main() {
defer func() {
if err := recover(); err != nil {
// report panic to sentry
sentry.CurrentHub().Recover(err)
sentry.Flush(time.Second * 5)
// print panic trace
panic(err)
}
}()
exitCode := cmd.Execute()
os.Exit(exitCode)
}

122
cmd/convert.go Normal file
View File

@@ -0,0 +1,122 @@
package cmd
import (
"os"
"path/filepath"
"github.com/httprunner/funplugin/myexec"
"github.com/pkg/errors"
"github.com/rs/zerolog/log"
"github.com/spf13/cobra"
"github.com/httprunner/httprunner/v5/code"
"github.com/httprunner/httprunner/v5/internal/builtin"
"github.com/httprunner/httprunner/v5/pkg/convert"
)
var convertCmd = &cobra.Command{
Use: "convert $path...",
Short: "convert multiple source format to HttpRunner JSON/YAML/gotest/pytest cases",
Args: cobra.MinimumNArgs(1),
SilenceUsage: false,
RunE: func(cmd *cobra.Command, args []string) error {
caseConverter := convert.NewConverter(outputDir, profilePath)
var fromType convert.FromType
if fromYAMLFlag {
fromType = convert.FromTypeYAML
} else if fromPostmanFlag {
fromType = convert.FromTypePostman
} else if fromHARFlag {
fromType = convert.FromTypeHAR
} else if fromCurlFlag {
fromType = convert.FromTypeCurl
} else {
fromType = convert.FromTypeJSON
log.Info().Str("fromType", fromType.String()).Msg("set default")
}
var outputType convert.OutputType
if toYAMLFlag {
outputType = convert.OutputTypeYAML
} else if toPyTestFlag {
packages := []string{"httprunner"}
_, err := myexec.EnsurePython3Venv(venv, packages...)
if err != nil {
log.Error().Err(err).Msg("python3 venv is not ready")
return errors.Wrap(code.InvalidPython3Venv, err.Error())
}
outputType = convert.OutputTypePyTest
} else {
outputType = convert.OutputTypeJSON
log.Info().Str("outputType", outputType.String()).Msg("set default")
}
var files []string
for _, arg := range args {
if builtin.IsFolderPathExists(arg) {
fs, err := os.ReadDir(arg)
if err != nil {
log.Error().Err(err).Str("path", arg).Msg("read dir failed")
continue
}
for _, f := range fs {
files = append(files, filepath.Join(arg, f.Name()))
}
} else {
files = append(files, arg)
}
}
for _, file := range files {
extName := filepath.Ext(file)
if !builtin.Contains(fromType.Extensions(), extName) {
log.Warn().Str("path", file).
Strs("expectExtensions", fromType.Extensions()).
Msg("skip file")
continue
}
if err := caseConverter.Convert(file, fromType, outputType); err != nil {
log.Error().Err(err).Str("path", file).
Str("outputType", outputType.String()).
Msg("convert case failed")
}
}
return nil
},
}
var (
outputDir string
profilePath string
fromJSONFlag bool
fromYAMLFlag bool
fromPostmanFlag bool
fromHARFlag bool
fromCurlFlag bool
toJSONFlag bool
toYAMLFlag bool
toPyTestFlag bool
)
func init() {
rootCmd.AddCommand(convertCmd)
convertCmd.Flags().BoolVar(&fromJSONFlag, "from-json", true, "load from json case format")
convertCmd.Flags().BoolVar(&fromYAMLFlag, "from-yaml", false, "load from yaml case format")
convertCmd.Flags().BoolVar(&fromHARFlag, "from-har", false, "load from HAR format")
convertCmd.Flags().BoolVar(&fromPostmanFlag, "from-postman", false, "load from postman format")
convertCmd.Flags().BoolVar(&fromCurlFlag, "from-curl", false, "load from curl format")
convertCmd.Flags().BoolVar(&toJSONFlag, "to-json", true, "convert to JSON case scripts")
convertCmd.Flags().BoolVar(&toYAMLFlag, "to-yaml", false, "convert to YAML case scripts")
convertCmd.Flags().BoolVar(&toPyTestFlag, "to-pytest", false, "convert to pytest scripts")
convertCmd.Flags().StringVarP(&outputDir, "output-dir", "d", "", "specify output directory")
convertCmd.Flags().StringVarP(&profilePath, "profile", "p", "", "specify profile path to override headers and cookies")
}

15
cmd/doc_test.go Normal file
View File

@@ -0,0 +1,15 @@
package cmd
import (
"testing"
"github.com/spf13/cobra/doc"
)
// run this test to generate markdown docs for hrp command
func TestGenMarkdownTree(t *testing.T) {
err := doc.GenMarkdownTree(rootCmd, "../../docs/cmd")
if err != nil {
t.Fatal(err)
}
}

75
cmd/ios/apps.go Normal file
View File

@@ -0,0 +1,75 @@
package ios
import (
"fmt"
"strings"
"time"
"github.com/mitchellh/mapstructure"
"github.com/spf13/cobra"
"github.com/httprunner/httprunner/v5/internal/sdk"
"github.com/httprunner/httprunner/v5/pkg/uixt"
)
type Application struct {
CFBundleVersion string `json:"version"`
CFBundleDisplayName string `json:"name"`
CFBundleIdentifier string `json:"bundleId"`
}
var listAppsCmd = &cobra.Command{
Use: "apps",
Short: "List all iOS installed apps",
PersistentPreRun: func(cmd *cobra.Command, args []string) {},
RunE: func(cmd *cobra.Command, args []string) (err error) {
startTime := time.Now()
defer func() {
sdk.SendGA4Event("hrp_ios_apps", map[string]interface{}{
"args": strings.Join(args, "-"),
"success": err == nil,
"engagement_time_msec": time.Since(startTime).Milliseconds(),
})
}()
device, err := getDevice(udid)
if err != nil {
return err
}
device.GetDeviceInfo()
var applicationType uixt.ApplicationType
switch appType {
case "user":
applicationType = uixt.ApplicationTypeUser
case "system":
applicationType = uixt.ApplicationTypeSystem
case "internal":
applicationType = uixt.ApplicationTypeInternal
case "all":
applicationType = uixt.ApplicationTypeAny
}
result, err := device.ListApps(applicationType)
if err != nil {
return fmt.Errorf("get app list failed %v", err)
}
for _, app := range result {
a := Application{}
mapstructure.Decode(app, &a)
fmt.Printf("%-30.30s %-50.50s %-s\n",
a.CFBundleDisplayName, a.CFBundleIdentifier, a.CFBundleVersion)
}
return nil
},
}
var appType string
func init() {
listAppsCmd.Flags().StringVarP(&udid, "udid", "u", "", "specify device by udid")
listAppsCmd.Flags().StringVarP(&appType, "type", "t", "user", "filter application type [user|system|internal|all]")
iosRootCmd.AddCommand(listAppsCmd)
}

81
cmd/ios/devices.go Normal file
View File

@@ -0,0 +1,81 @@
package ios
import (
"encoding/json"
"fmt"
"os"
"strings"
"time"
"github.com/danielpaulus/go-ios/ios"
"github.com/spf13/cobra"
"github.com/httprunner/httprunner/v5/internal/sdk"
"github.com/httprunner/httprunner/v5/pkg/uixt"
)
type Device struct {
d ios.DeviceEntry
UDID string `json:"UDID"`
Status string `json:"status"`
ConnectionType string `json:"connectionType"`
ConnectionSpeed int `json:"connectionSpeed"`
DeviceDetail *uixt.DeviceDetail `json:"deviceDetail,omitempty"`
}
func (device *Device) GetStatus() string {
if device.ConnectionType != "" {
return "online"
} else {
return "offline"
}
}
func (device *Device) ToFormat() string {
result, _ := json.MarshalIndent(device, "", "\t")
return string(result)
}
var listDevicesCmd = &cobra.Command{
Use: "devices",
Short: "List all iOS devices",
PersistentPreRun: func(cmd *cobra.Command, args []string) {},
RunE: func(cmd *cobra.Command, args []string) (err error) {
startTime := time.Now()
defer func() {
sdk.SendGA4Event("hrp_ios_devices", map[string]interface{}{
"args": strings.Join(args, "-"),
"success": err == nil,
"engagement_time_msec": time.Since(startTime).Milliseconds(),
})
}()
devices, err := uixt.GetIOSDevices(udid)
if err != nil {
fmt.Println(err)
os.Exit(0)
}
for _, d := range devices {
deviceProperties := d.Properties
device := &Device{
d: d,
UDID: deviceProperties.SerialNumber,
ConnectionType: deviceProperties.ConnectionType,
ConnectionSpeed: deviceProperties.ConnectionSpeed,
}
device.Status = device.GetStatus()
fmt.Println(device.UDID, device.ConnectionType, device.Status)
}
return nil
},
}
var udid string
func init() {
listDevicesCmd.Flags().StringVarP(&udid, "udid", "u", "", "filter by device's udid")
iosRootCmd.AddCommand(listDevicesCmd)
}

24
cmd/ios/init.go Normal file
View File

@@ -0,0 +1,24 @@
package ios
import (
"github.com/spf13/cobra"
"github.com/httprunner/httprunner/v5/pkg/uixt"
)
var iosRootCmd = &cobra.Command{
Use: "ios",
Short: "simple utils for ios device management",
}
func getDevice(udid string) (*uixt.IOSDevice, error) {
device, err := uixt.NewIOSDevice(uixt.WithUDID(udid))
if err != nil {
return nil, err
}
return device, nil
}
func Init(rootCmd *cobra.Command) {
rootCmd.AddCommand(iosRootCmd)
}

52
cmd/ios/install.go Normal file
View File

@@ -0,0 +1,52 @@
package ios
import (
"fmt"
"strings"
"time"
"github.com/spf13/cobra"
"github.com/httprunner/httprunner/v5/internal/sdk"
"github.com/httprunner/httprunner/v5/pkg/uixt"
)
var installCmd = &cobra.Command{
Use: "install [flags] PACKAGE",
Short: "push package to the device and install them automatically",
Args: cobra.MinimumNArgs(1),
RunE: func(cmd *cobra.Command, args []string) (err error) {
startTime := time.Now()
defer func() {
sdk.SendGA4Event("hrp_adb_devices", map[string]interface{}{
"args": strings.Join(args, "-"),
"success": err == nil,
"engagement_time_msec": time.Since(startTime).Milliseconds(),
})
}()
_, err = getDevice(udid)
if err != nil {
return err
}
device, err := uixt.NewIOSDevice(uixt.WithUDID(udid))
if err != nil {
fmt.Println(err)
return err
}
err = device.Install(args[0])
if err != nil {
fmt.Println(err)
return err
}
fmt.Println("success")
return nil
},
}
func init() {
installCmd.Flags().StringVarP(&udid, "udid", "u", "", "filter by device's serial")
iosRootCmd.AddCommand(installCmd)
}

14
cmd/ios/ios_test.go Normal file
View File

@@ -0,0 +1,14 @@
//go:build localtest
package ios
import "testing"
func TestGetDevice(t *testing.T) {
device, err := getDevice(udid)
if err != nil {
t.Fatal(err)
}
t.Logf("device: %v", device)
}

77
cmd/ios/mount.go Normal file
View File

@@ -0,0 +1,77 @@
package ios
import (
"fmt"
"strings"
"time"
"github.com/rs/zerolog/log"
"github.com/spf13/cobra"
"github.com/httprunner/httprunner/v5/internal/builtin"
"github.com/httprunner/httprunner/v5/internal/sdk"
)
// mountCmd represents the mount command
var mountCmd = &cobra.Command{
Use: "mount",
Short: "A brief description of your command",
RunE: func(cmd *cobra.Command, args []string) (err error) {
startTime := time.Now()
defer func() {
sdk.SendGA4Event("hrp_ios_mount", map[string]interface{}{
"args": strings.Join(args, "-"),
"success": err == nil,
"engagement_time_msec": time.Since(startTime).Milliseconds(),
})
}()
device, err := getDevice(udid)
if err != nil {
return err
}
images, errImage := device.ListImages()
if err != nil {
return fmt.Errorf("list device images failed: %v", err)
}
if listDeveloperDiskImage {
for i, imgSign := range images {
fmt.Printf("[%d] %s\n", i+1, imgSign)
}
return nil
}
if errImage == nil && len(images) > 0 {
log.Info().Strs("images", images).Msg("ios developer image is already mounted")
return nil
}
log.Info().Str("dir", developerDiskImageDir).Msg("start to mount ios developer image")
if !builtin.IsFolderPathExists(developerDiskImageDir) {
return fmt.Errorf("developer disk image directory not exist: %s", developerDiskImageDir)
}
if err = device.AutoMountImage(developerDiskImageDir); err != nil {
return fmt.Errorf("mount developer disk image failed: %s", err)
}
log.Info().Msg("mount developer disk image successfully")
return nil
},
}
const defaultDeveloperDiskImageDir = "/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/DeviceSupport/"
var (
developerDiskImageDir string
listDeveloperDiskImage bool
)
func init() {
mountCmd.Flags().BoolVar(&listDeveloperDiskImage, "list", false, "list developer disk images")
mountCmd.Flags().StringVarP(&developerDiskImageDir, "dir", "d", defaultDeveloperDiskImageDir, "specify developer disk image directory")
mountCmd.Flags().StringVarP(&udid, "udid", "u", "", "specify device by udid")
iosRootCmd.AddCommand(mountCmd)
}

50
cmd/ios/ps.go Normal file
View File

@@ -0,0 +1,50 @@
package ios
import (
"fmt"
"strings"
"time"
"github.com/spf13/cobra"
"github.com/httprunner/httprunner/v5/internal/sdk"
)
var psCmd = &cobra.Command{
Use: "ps",
Short: "show running processes",
PersistentPreRun: func(cmd *cobra.Command, args []string) {},
RunE: func(cmd *cobra.Command, args []string) (err error) {
startTime := time.Now()
defer func() {
sdk.SendGA4Event("hrp_ios_ps", map[string]interface{}{
"args": strings.Join(args, "-"),
"success": err == nil,
"engagement_time_msec": time.Since(startTime).Milliseconds(),
})
}()
device, err := getDevice(udid)
if err != nil {
return err
}
runningProcesses, err := device.ListProcess(!isAll)
if err != nil {
return err
}
for _, p := range runningProcesses {
fmt.Printf("%4d %-"+fmt.Sprintf("%d", len(runningProcesses))+"s %20s %s\n",
p.Pid, p.Name, time.Since(p.StartDate).String(), bundleID)
}
return nil
},
}
var isAll bool
func init() {
psCmd.Flags().StringVarP(&udid, "udid", "u", "", "specify device by udid")
psCmd.Flags().BoolVarP(&isAll, "all", "a", false, "print all processes including system processes")
iosRootCmd.AddCommand(psCmd)
}

44
cmd/ios/reboot.go Normal file
View File

@@ -0,0 +1,44 @@
package ios
import (
"fmt"
"strings"
"time"
"github.com/spf13/cobra"
"github.com/httprunner/httprunner/v5/internal/sdk"
)
var rebootCmd = &cobra.Command{
Use: "reboot",
Short: "reboot ios device",
PersistentPreRun: func(cmd *cobra.Command, args []string) {},
RunE: func(cmd *cobra.Command, args []string) (err error) {
startTime := time.Now()
defer func() {
sdk.SendGA4Event("hrp_ios_reboot", map[string]interface{}{
"args": strings.Join(args, "-"),
"success": err == nil,
"engagement_time_msec": time.Since(startTime).Milliseconds(),
})
}()
device, err := getDevice(udid)
if err != nil {
return err
}
err = device.Reboot()
if err != nil {
return err
}
fmt.Printf("reboot %s success\n", device.UDID)
return nil
},
}
func init() {
rebootCmd.Flags().StringVarP(&udid, "udid", "u", "", "specify device by udid")
iosRootCmd.AddCommand(rebootCmd)
}

59
cmd/ios/uninstall.go Normal file
View File

@@ -0,0 +1,59 @@
package ios
import (
"fmt"
"strings"
"time"
"github.com/spf13/cobra"
"github.com/httprunner/httprunner/v5/internal/sdk"
"github.com/httprunner/httprunner/v5/pkg/uixt"
)
var uninstallCmd = &cobra.Command{
Use: "uninstall [flags] PACKAGE",
Short: "uninstall package automatically",
Args: cobra.MinimumNArgs(0),
RunE: func(cmd *cobra.Command, args []string) (err error) {
startTime := time.Now()
defer func() {
sdk.SendGA4Event("hrp_adb_devices", map[string]interface{}{
"args": strings.Join(args, "-"),
"success": err == nil,
"engagement_time_msec": time.Since(startTime).Milliseconds(),
})
}()
if len(bundleId) == 0 {
return fmt.Errorf("bundleId is empty")
}
_, err = getDevice(udid)
if err != nil {
return err
}
device, err := uixt.NewIOSDevice(uixt.WithUDID(udid))
if err != nil {
fmt.Println(err)
return err
}
err = device.Uninstall(bundleId)
if err != nil {
fmt.Println(err)
return err
}
fmt.Println("success")
return nil
},
}
var bundleId string
func init() {
uninstallCmd.Flags().StringVarP(&udid, "udid", "u", "", "filter by device's serial")
uninstallCmd.Flags().StringVarP(&bundleId, "bundleId", "b", "", "bundleId to uninstall")
iosRootCmd.AddCommand(uninstallCmd)
}

56
cmd/ios/xctest.go Normal file
View File

@@ -0,0 +1,56 @@
package ios
import (
"context"
"fmt"
"strings"
"time"
"github.com/pkg/errors"
"github.com/spf13/cobra"
"github.com/httprunner/httprunner/v5/internal/sdk"
)
var xctestCmd = &cobra.Command{
Use: "xctest",
Short: "run xctest",
RunE: func(cmd *cobra.Command, args []string) (err error) {
startTime := time.Now()
defer func() {
sdk.SendGA4Event("hrp_ios_xctest", map[string]interface{}{
"args": strings.Join(args, "-"),
"success": err == nil,
"engagement_time_msec": time.Since(startTime).Milliseconds(),
})
}()
if bundleID == "" {
return fmt.Errorf("bundleID is required")
}
device, err := getDevice(udid)
if err != nil {
return err
}
err = device.RunXCTest(context.Background(), bundleID, testRunnerBundleID, xctestConfig)
if err != nil {
return errors.Wrap(err, "run xctest failed")
}
return nil
},
}
var (
bundleID string
testRunnerBundleID string
xctestConfig string
)
func init() {
xctestCmd.Flags().StringVarP(&udid, "udid", "u", "", "specify ios device's UDID")
xctestCmd.Flags().StringVarP(&bundleID, "bundleID", "b", "com.gtf.wda.runner.xctrunner", "specify ios bundleID")
xctestCmd.Flags().StringVarP(&testRunnerBundleID, "testRunnerBundleID", "t", "com.gtf.wda.runner.xctrunner", "specify ios testRunnerBundleID")
xctestCmd.Flags().StringVarP(&xctestConfig, "xctestConfig", "x", "GtfWdaRunner.xctest", "specify ios xctestConfig")
iosRootCmd.AddCommand(xctestCmd)
}

44
cmd/pytest.go Normal file
View File

@@ -0,0 +1,44 @@
package cmd
import (
"strings"
"time"
"github.com/httprunner/funplugin/myexec"
"github.com/pkg/errors"
"github.com/rs/zerolog/log"
"github.com/spf13/cobra"
"github.com/httprunner/httprunner/v5/code"
"github.com/httprunner/httprunner/v5/internal/pytest"
"github.com/httprunner/httprunner/v5/internal/sdk"
)
var pytestCmd = &cobra.Command{
Use: "pytest $path ...",
Short: "run API test with pytest",
Args: cobra.MinimumNArgs(1),
DisableFlagParsing: true, // allow to pass any args to pytest
RunE: func(cmd *cobra.Command, args []string) (err error) {
startTime := time.Now()
defer func() {
sdk.SendGA4Event("hrp_pytest", map[string]interface{}{
"args": strings.Join(args, "-"),
"success": err == nil,
"engagement_time_msec": time.Since(startTime).Milliseconds(),
})
}()
packages := []string{"httprunner"}
_, err = myexec.EnsurePython3Venv(venv, packages...)
if err != nil {
log.Error().Err(err).Msg("python3 venv is not ready")
return errors.Wrap(code.InvalidPython3Venv, err.Error())
}
return pytest.RunPytest(args)
},
}
func init() {
rootCmd.AddCommand(pytestCmd)
}

59
cmd/root.go Normal file
View File

@@ -0,0 +1,59 @@
package cmd
import (
"github.com/spf13/cobra"
hrp "github.com/httprunner/httprunner/v5"
"github.com/httprunner/httprunner/v5/cmd/adb"
"github.com/httprunner/httprunner/v5/cmd/ios"
"github.com/httprunner/httprunner/v5/code"
"github.com/httprunner/httprunner/v5/internal/version"
)
// rootCmd represents the base command when called without any subcommands
var rootCmd = &cobra.Command{
Use: "hrp",
Short: "Next-Generation API Testing Solution.",
Long: `
██╗ ██╗████████╗████████╗██████╗ ██████╗ ██╗ ██╗███╗ ██╗███╗ ██╗███████╗██████╗
██║ ██║╚══██╔══╝╚══██╔══╝██╔══██╗██╔══██╗██║ ██║████╗ ██║████╗ ██║██╔════╝██╔══██╗
███████║ ██║ ██║ ██████╔╝██████╔╝██║ ██║██╔██╗ ██║██╔██╗ ██║█████╗ ██████╔╝
██╔══██║ ██║ ██║ ██╔═══╝ ██╔══██╗██║ ██║██║╚██╗██║██║╚██╗██║██╔══╝ ██╔══██╗
██║ ██║ ██║ ██║ ██║ ██║ ██║╚██████╔╝██║ ╚████║██║ ╚████║███████╗██║ ██║
╚═╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═══╝╚═╝ ╚═══╝╚══════╝╚═╝ ╚═╝
HttpRunner is an open source API testing tool that supports HTTP(S)/HTTP2/WebSocket/RPC
network protocols, covering API testing, performance testing and digital experience
monitoring (DEM) test types. Enjoy! ✨ 🚀 ✨
License: Apache-2.0
Website: https://httprunner.com
Github: https://github.com/httprunner/httprunner
Copyright 2017 debugtalk`,
PersistentPreRun: func(cmd *cobra.Command, args []string) {
hrp.InitLogger(logLevel, logJSON)
},
Version: version.VERSION,
TraverseChildren: true, // parses flags on all parents before executing child command
SilenceUsage: true, // silence usage when an error occurs
}
var (
logLevel string
logJSON bool
venv string
)
// Execute adds all child commands to the root command and sets flags appropriately.
// This is called by main.main(). It only needs to happen once to the rootCmd.
func Execute() int {
rootCmd.PersistentFlags().StringVarP(&logLevel, "log-level", "l", "INFO", "set log level")
rootCmd.PersistentFlags().BoolVar(&logJSON, "log-json", false, "set log to json format (default colorized console)")
rootCmd.PersistentFlags().StringVar(&venv, "venv", "", "specify python3 venv path")
ios.Init(rootCmd)
adb.Init(rootCmd)
err := rootCmd.Execute()
return code.GetErrorCode(err)
}

76
cmd/run.go Normal file
View File

@@ -0,0 +1,76 @@
package cmd
import (
"github.com/spf13/cobra"
hrp "github.com/httprunner/httprunner/v5"
)
// runCmd represents the run command
var runCmd = &cobra.Command{
Use: "run $path...",
Short: "run API test with go engine",
Long: `run yaml/json testcase files for API test`,
Example: ` $ hrp run demo.json # run specified json testcase file
$ hrp run demo.yaml # run specified yaml testcase file
$ hrp run examples/ # run testcases in specified folder`,
Args: cobra.MinimumNArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
var paths []hrp.ITestCase
for _, arg := range args {
path := hrp.TestCasePath(arg)
paths = append(paths, &path)
}
runner := makeHRPRunner()
return runner.Run(paths...)
},
}
var (
continueOnFailure bool
requestsLogOff bool
httpStatOn bool
pluginLogOn bool
proxyUrl string
saveTests bool
genHTMLReport bool
caseTimeout float32
)
func init() {
rootCmd.AddCommand(runCmd)
runCmd.Flags().BoolVarP(&continueOnFailure, "continue-on-failure", "c", false, "continue running next step when failure occurs")
runCmd.Flags().BoolVar(&requestsLogOff, "log-requests-off", false, "turn off request & response details logging")
runCmd.Flags().BoolVar(&httpStatOn, "http-stat", false, "turn on HTTP latency stat (DNSLookup, TCP Connection, etc.)")
runCmd.Flags().BoolVar(&pluginLogOn, "log-plugin", false, "turn on plugin logging")
runCmd.Flags().StringVarP(&proxyUrl, "proxy-url", "p", "", "set proxy url")
runCmd.Flags().BoolVarP(&saveTests, "save-tests", "s", false, "save tests summary")
runCmd.Flags().BoolVarP(&genHTMLReport, "gen-html-report", "g", false, "generate html report")
runCmd.Flags().Float32Var(&caseTimeout, "case-timeout", 3600, "set testcase timeout (seconds)")
}
func makeHRPRunner() *hrp.HRPRunner {
runner := hrp.NewRunner(nil).
SetFailfast(!continueOnFailure).
SetSaveTests(saveTests).
SetCaseTimeout(caseTimeout)
if genHTMLReport {
runner.GenHTMLReport()
}
if !requestsLogOff {
runner.SetRequestsLogOn()
}
if httpStatOn {
runner.SetHTTPStatOn()
}
if pluginLogOn {
runner.SetPluginLogOn()
}
if venv != "" {
runner.SetPython3Venv(venv)
}
if proxyUrl != "" {
runner.SetProxyUrl(proxyUrl)
}
return runner
}

58
cmd/scaffold.go Normal file
View File

@@ -0,0 +1,58 @@
package cmd
import (
"errors"
"github.com/rs/zerolog/log"
"github.com/spf13/cobra"
"github.com/httprunner/httprunner/v5/internal/scaffold"
)
var scaffoldCmd = &cobra.Command{
Use: "startproject $project_name",
Aliases: []string{"scaffold"},
Short: "create a scaffold project",
Args: cobra.ExactValidArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
if !ignorePlugin && !genPythonPlugin && !genGoPlugin {
return errors.New("please specify function plugin type")
}
var pluginType scaffold.PluginType
if empty {
pluginType = scaffold.Empty
} else if ignorePlugin {
pluginType = scaffold.Ignore
} else if genGoPlugin {
pluginType = scaffold.Go
} else {
pluginType = scaffold.Py // default
}
err := scaffold.CreateScaffold(args[0], pluginType, venv, force)
if err != nil {
log.Error().Err(err).Msg("create scaffold project failed")
return err
}
log.Info().Str("projectName", args[0]).Msg("create scaffold success")
return nil
},
}
var (
empty bool
ignorePlugin bool
genPythonPlugin bool
genGoPlugin bool
force bool
)
func init() {
rootCmd.AddCommand(scaffoldCmd)
scaffoldCmd.Flags().BoolVarP(&force, "force", "f", false, "force to overwrite existing project")
scaffoldCmd.Flags().BoolVar(&genPythonPlugin, "py", true, "generate hashicorp python plugin")
scaffoldCmd.Flags().BoolVar(&genGoPlugin, "go", false, "generate hashicorp go plugin")
scaffoldCmd.Flags().BoolVar(&ignorePlugin, "ignore-plugin", false, "ignore function plugin")
scaffoldCmd.Flags().BoolVar(&empty, "empty", false, "generate empty project")
}

25
cmd/server.go Normal file
View File

@@ -0,0 +1,25 @@
package cmd
import (
"github.com/spf13/cobra"
"github.com/httprunner/httprunner/v5/pkg/server"
)
// serverCmd represents the server command
var serverCmd = &cobra.Command{
Use: "server start",
Short: "start hrp server",
Long: `start hrp server. exec automation by http`,
Args: cobra.MinimumNArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
return server.NewServer(port)
},
}
var port int
func init() {
rootCmd.AddCommand(serverCmd)
serverCmd.Flags().IntVarP(&port, "port", "p", 8082, "Port to run the server on")
}

32
cmd/wiki.go Normal file
View File

@@ -0,0 +1,32 @@
package cmd
import (
"strings"
"time"
"github.com/spf13/cobra"
"github.com/httprunner/httprunner/v5/internal/sdk"
"github.com/httprunner/httprunner/v5/internal/wiki"
)
var wikiCmd = &cobra.Command{
Use: "wiki",
Aliases: []string{"info", "docs", "doc"},
Short: "visit https://httprunner.com",
RunE: func(cmd *cobra.Command, args []string) (err error) {
startTime := time.Now()
defer func() {
sdk.SendGA4Event("hrp_wiki", map[string]interface{}{
"args": strings.Join(args, "-"),
"success": err == nil,
"engagement_time_msec": time.Since(startTime).Milliseconds(),
})
}()
return wiki.OpenWiki()
},
}
func init() {
rootCmd.AddCommand(wikiCmd)
}