提交 33c50ec0 编写于 作者: aaronchen2k2k's avatar aaronchen2k2k

init

上级
此差异已折叠。
# zentaoatf
ZentaoATF is an automation testing framework written in Golang.
## Features
1. Support popular programming languages like Python, Ruby, Lua, Tcl, PHP, GO, Shell and Bat;
2. Reduce the invasive of existing testing scripts;
3. Integration with ZenTao - Open source project management system.
## QuickStart
### Run from release file
1. Download last release file from [here](https://github.com/easysoft/zentaoatf/releases);
2. Type 'ztf/ztf.exe help' to get the help doc.
### Run from Golang codes
1. Enter 'git clone https://github.com/easysoft/zentaoatf.git' to get the source codes;
2. Overwrite edit.go and view.go from https://github.com/rocket049/gocui to fix the Chinese related bug;
3. Type 'go run src/atf.go help' to get the help doc.
## Licenses
All source code is licensed under the [GPLv3 License](LICENSE.md).
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
package model
type Config struct {
Version int
Language string
Url string
Account string
Password string
Javascript string
Lua string
Perl string
Php string
Python string
Ruby string
Tcl string
Autoit string
}
package assertUtils
import (
"encoding/json"
"github.com/easysoft/zentaoatf/src/model"
commonUtils "github.com/easysoft/zentaoatf/src/utils/common"
constant "github.com/easysoft/zentaoatf/src/utils/const"
"github.com/easysoft/zentaoatf/src/utils/file"
langUtils "github.com/easysoft/zentaoatf/src/utils/lang"
stringUtils "github.com/easysoft/zentaoatf/src/utils/string"
"github.com/easysoft/zentaoatf/src/utils/vari"
zentaoUtils "github.com/easysoft/zentaoatf/src/utils/zentao"
"io/ioutil"
"os"
"path"
"regexp"
"strconv"
"strings"
)
func GetCaseByDirAndFile(files []string) []string {
cases := make([]string, 0)
for _, file := range files {
GetAllScriptsInDir(file, &cases)
}
return cases
}
func GetAllScriptsInDir(filePthParam string, files *[]string) error {
sep := string(os.PathSeparator)
if !fileUtils.IsDir(filePthParam) { // first call, param is file
regx := langUtils.GetSupportLanguageExtRegx()
pass, _ := regexp.MatchString(`.*\.`+regx+`$`, filePthParam)
if pass {
pass := zentaoUtils.CheckFileIsScript(filePthParam)
if pass {
*files = append(*files, filePthParam)
}
}
return nil
}
filePthParam = fileUtils.AbosutePath(filePthParam)
dir, err := ioutil.ReadDir(filePthParam)
if err != nil {
return err
}
for _, fi := range dir {
name := fi.Name()
if commonUtils.IngoreFile(name) {
continue
}
if fi.IsDir() { // 目录, 递归遍历
GetAllScriptsInDir(filePthParam+name+sep, files)
} else {
path := filePthParam + name
regx := langUtils.GetSupportLanguageExtRegx()
pass, _ := regexp.MatchString("^*.\\."+regx+"$", path)
if pass {
pass = zentaoUtils.CheckFileIsScript(path)
if pass {
*files = append(*files, path)
}
}
}
}
return nil
}
func GetScriptByIdsInDir(dirPth string, idMap map[int]string, files *[]string) error {
dirPth = fileUtils.AbosutePath(dirPth)
sep := string(os.PathSeparator)
if commonUtils.IngoreFile(dirPth) {
return nil
}
dir, err := ioutil.ReadDir(dirPth)
if err != nil {
return err
}
for _, fi := range dir {
name := fi.Name()
if fi.IsDir() { // 目录, 递归遍历
GetScriptByIdsInDir(dirPth+name+sep, idMap, files)
} else {
regx := langUtils.GetSupportLanguageExtRegx()
pass, _ := regexp.MatchString("^*.\\."+regx+"$", name)
if !pass {
continue
}
path := dirPth + name
pass, id, _, _ := zentaoUtils.GetCaseInfo(path)
if pass {
_, ok := idMap[id]
if ok {
*files = append(*files, path)
}
}
}
}
return nil
}
func GetCaseIdsInSuiteFile(name string, fileIdMap *map[int]string) {
content := fileUtils.ReadFile(name)
for _, line := range strings.Split(content, "\n") {
idStr := strings.TrimSpace(line)
if idStr == "" {
continue
}
id, err := strconv.Atoi(idStr)
if err == nil {
(*fileIdMap)[id] = ""
}
}
}
func GetFailedCasesDirectlyFromTestResult(resultFile string) []string {
cases := make([]string, 0)
extName := path.Ext(resultFile)
if extName == "."+constant.ExtNameResult {
resultFile = strings.Replace(resultFile, extName, "."+constant.ExtNameJson, -1)
}
content := fileUtils.ReadFile(resultFile)
var report model.TestReport
json.Unmarshal([]byte(content), &report)
for _, cs := range report.FuncResult {
if cs.Status != "pass" {
cases = append(cases, cs.Path)
}
}
return cases
}
func GetScriptType(scripts []string) []string {
vari.ScriptExtToNameMap = langUtils.GetExtToNameMap()
exts := make([]string, 0)
for _, script := range scripts {
ext := path.Ext(script)
if ext != "" {
ext = ext[1:]
name := vari.ScriptExtToNameMap[ext]
if !stringUtils.FindInArr(name, exts) {
exts = append(exts, name)
}
}
}
return exts
}
package commonUtils
import (
"strings"
)
type sliceValue []string
func NewSliceValue(vals []string, p *[]string) *sliceValue {
*p = vals
return (*sliceValue)(p)
}
func (s *sliceValue) Set(val string) error {
*s = sliceValue(strings.Split(val, ","))
return nil
}
func (s *sliceValue) String() string {
*s = sliceValue([]string{})
return "It's none of my business"
}
package commonUtils
import (
"github.com/easysoft/zentaoatf/src/model"
"github.com/easysoft/zentaoatf/src/utils/const"
stringUtils "github.com/easysoft/zentaoatf/src/utils/string"
"github.com/emirpasic/gods/maps"
"os"
"path"
"path/filepath"
"reflect"
"regexp"
"runtime"
"strings"
)
func Base(pathStr string) string {
pathStr = filepath.ToSlash(pathStr)
return path.Base(pathStr)
}
func RemoveBlankLine(str string) string {
myExp := regexp.MustCompile(`\n{3,}`) // 连续换行
ret := myExp.ReplaceAllString(str, "\n\n")
ret = strings.Trim(ret, "\n")
ret = strings.TrimSpace(ret)
return ret
}
func BoolToPass(b bool) string {
if b {
return constant.PASS.String()
} else {
return constant.FAIL.String()
}
}
func GetOs() string {
osName := runtime.GOOS
if osName == "darwin" {
return "mac"
} else {
return osName
}
}
func IsWin() bool {
return GetOs() == "windows"
}
func IsLinux() bool {
return GetOs() == "linux"
}
func IsMac() bool {
return GetOs() == "mac"
}
func IsRelease() bool {
if _, err := os.Stat("res"); os.IsNotExist(err) {
return true
}
return false
}
func UpdateUrl(url string) string {
if strings.LastIndex(url, "/") < len(url)-1 {
url += "/"
}
return url
}
func IngoreFile(path string) bool {
path = filepath.Base(path)
if strings.Index(path, ".") == 0 ||
path == "bin" || path == "release" || path == "logs" || path == "xdoc" {
return true
} else {
return false
}
}
func GetFieldVal(config model.Config, key string) string {
key = stringUtils.Ucfirst(key)
immutable := reflect.ValueOf(config)
val := immutable.FieldByName(key).String()
return val
}
func SetFieldVal(config *model.Config, key string, val string) string {
key = stringUtils.Ucfirst(key)
mutable := reflect.ValueOf(config).Elem()
mutable.FieldByName(key).SetString(val)
return val
}
func LinkedMapToMap(mp maps.Map) map[string]string {
ret := make(map[string]string, 0)
for _, keyIfs := range mp.Keys() {
valueIfs, _ := mp.Get(keyIfs)
key := strings.TrimSpace(keyIfs.(string))
value := strings.TrimSpace(valueIfs.(string))
ret[key] = value
}
return ret
}
package configUtils
import (
"fmt"
"github.com/easysoft/zentaoatf/src/model"
assertUtils "github.com/easysoft/zentaoatf/src/utils/assert"
commonUtils "github.com/easysoft/zentaoatf/src/utils/common"
"github.com/easysoft/zentaoatf/src/utils/const"
"github.com/easysoft/zentaoatf/src/utils/display"
fileUtils "github.com/easysoft/zentaoatf/src/utils/file"
"github.com/easysoft/zentaoatf/src/utils/i118"
langUtils "github.com/easysoft/zentaoatf/src/utils/lang"
logUtils "github.com/easysoft/zentaoatf/src/utils/log"
stdinUtils "github.com/easysoft/zentaoatf/src/utils/stdin"
"github.com/easysoft/zentaoatf/src/utils/vari"
"github.com/fatih/color"
"gopkg.in/ini.v1"
"os"
"reflect"
)
func InitConfig() {
vari.ZTFDir = fileUtils.GetZTFDir()
CheckConfigPermission()
constant.ConfigFile = vari.ZTFDir + constant.ConfigFile
vari.Config = getInst()
// screen size
InitScreenSize()
// internationalization
i118Utils.InitI118(vari.Config.Language)
}
func InitScreenSize() {
w, h := display.GetScreenSize()
vari.ScreenWidth = w
vari.ScreenHeight = h
}
func SaveConfig(conf model.Config) error {
fileUtils.MkDirIfNeeded(fileUtils.GetZTFDir() + "conf")
conf.Version = constant.ConfigVer
cfg := ini.Empty()
cfg.ReflectFrom(&conf)
cfg.SaveTo(constant.ConfigFile)
vari.Config = ReadCurrConfig()
return nil
}
func PrintCurrConfig() {
logUtils.PrintToWithColor("\n"+i118Utils.I118Prt.Sprintf("current_config"), color.FgCyan)
val := reflect.ValueOf(vari.Config)
typeOfS := val.Type()
for i := 0; i < reflect.ValueOf(vari.Config).NumField(); i++ {
if !commonUtils.IsWin() && i > 4 {
break
}
val := val.Field(i)
name := typeOfS.Field(i).Name
fmt.Printf(" %s: %v \n", name, val.Interface())
}
}
func ReadCurrConfig() model.Config {
config := model.Config{}
configPath := constant.ConfigFile
if !fileUtils.FileExist(configPath) {
config.Language = "en"
i118Utils.InitI118("en")
return config
}
ini.MapTo(&config, constant.ConfigFile)
config.Url = commonUtils.UpdateUrl(config.Url)
return config
}
func getInst() model.Config {
isSetAction := len(os.Args) > 1 && (os.Args[1] == "set" || os.Args[1] == "-set")
if !isSetAction {
CheckConfigReady()
}
ini.MapTo(&vari.Config, constant.ConfigFile)
if vari.Config.Version != constant.ConfigVer { // old config file, re-init
if vari.Config.Language != "en" && vari.Config.Language != "zh" {
vari.Config.Language = "en"
}
SaveConfig(vari.Config)
}
return vari.Config
}
func CheckConfigPermission() {
//err := syscall.Access(vari.ZTFDir, syscall.O_RDWR)
err := fileUtils.MkDirIfNeeded(vari.ZTFDir + "conf")
if err != nil {
logUtils.PrintToWithColor(
fmt.Sprintf("Permission denied to open %s for write. Please change work dir.", vari.ZTFDir), color.FgRed)
os.Exit(0)
}
}
func CheckConfigReady() {
if !fileUtils.FileExist(constant.ConfigFile) {
InputForSet()
}
}
func InputForSet() {
conf := ReadCurrConfig()
var configSite bool
logUtils.PrintToWithColor(i118Utils.I118Prt.Sprintf("begin_config"), color.FgCyan)
enCheck := ""
var numb string
if conf.Language == "en" {
enCheck = "*"
numb = "1"
}
zhCheck := ""
if conf.Language == "zh" {
zhCheck = "*"
numb = "2"
}
numbSelected := stdinUtils.GetInput("(1|2)", numb, "enter_language", enCheck, zhCheck)
if numbSelected == "1" {
conf.Language = "en"
} else {
conf.Language = "zh"
}
stdinUtils.InputForBool(&configSite, true, "config_zentao_site")
if configSite {
conf.Url = stdinUtils.GetInput("(http://.*)", conf.Url, "enter_url", conf.Url)
conf.Account = stdinUtils.GetInput("(.{2,})", conf.Account, "enter_account", conf.Account)
conf.Password = stdinUtils.GetInput("(.{2,})", conf.Password, "enter_password", conf.Password)
}
if commonUtils.IsWin() {
var configInterpreter bool
stdinUtils.InputForBool(&configInterpreter, true, "config_script_interpreter")
if configInterpreter {
scripts := assertUtils.GetCaseByDirAndFile([]string{"."})
InputForScriptInterpreter(scripts, &conf, "set")
}
}
SaveConfig(conf)
PrintCurrConfig()
}
func CheckRequestConfig() {
conf := ReadCurrConfig()
if conf.Url == "" || conf.Account == "" || conf.Password == "" {
InputForRequest()
}
}
func InputForRequest() {
conf := ReadCurrConfig()
logUtils.PrintToWithColor(i118Utils.I118Prt.Sprintf("need_config"), color.FgCyan)
conf.Url = stdinUtils.GetInput("(http://.*)", conf.Url, "enter_url", conf.Url)
conf.Account = stdinUtils.GetInput("(.{2,})", conf.Account, "enter_account", conf.Account)
conf.Password = stdinUtils.GetInput("(.{2,})", conf.Password, "enter_password", conf.Password)
SaveConfig(conf)
}
func InputForScriptInterpreter(scripts []string, config *model.Config, from string) bool {
configChanged := false
langs := assertUtils.GetScriptType(scripts)
for _, lang := range langs {
if lang == "bat" || lang == "shell" {
continue
}
deflt := commonUtils.GetFieldVal(*config, lang)
if from == "run" && deflt != "" { // already set when run, "-" means ignore
continue
}
if deflt == "-" {
deflt = ""
}
sampleOrDefaultTips := ""
if deflt == "" {
sampleOrDefaultTips = i118Utils.I118Prt.Sprintf("for_example", langUtils.LangMap[lang]["interpreter"]) + " " +
i118Utils.I118Prt.Sprintf("empty_to_ignore")
} else {
sampleOrDefaultTips = deflt
}
configChanged = true
inter := stdinUtils.GetInputForScriptInterpreter(deflt, "set_script_interpreter", lang, sampleOrDefaultTips)
commonUtils.SetFieldVal(config, lang, inter)
}
return configChanged
}
package constant
import (
"fmt"
"os"
)
var (
ConfigVer = 1
ConfigFile = fmt.Sprintf("conf%sztf.conf", string(os.PathSeparator))
UrlZentaoSettings = "zentaoSettings"
UrlImportProject = "importProject"
UrlSubmitResult = "submitResults"
UrlReportBug = "reportBug"
ExtNameSuite = "cs"
ExtNameJson = "json"
ExtNameResult = "txt"
LanguageDefault = "en"
LanguageEN = "en"
LanguageZH = "zh"
EnRes = fmt.Sprintf("res%smessages_en.json", string(os.PathSeparator))
ZhRes = fmt.Sprintf("res%smessages_zh.json", string(os.PathSeparator))
//ScriptDir = fmt.Sprintf("scripts%s", string(os.PathSeparator))
LogDir = fmt.Sprintf("log%s", string(os.PathSeparator))
LeftWidth = 36
MinWidth = 130
MinHeight = 36
CmdViewHeight = 10
RequestTypePathInfo = "PATH_INFO"
UnitTestType []string = []string{"junit", "testng", "phpunit", "pytest", "jtest", "cppunit", "gtest", "qtest"}
AutoTestType []string = []string{"selenium", "appium"}
)
package constant
type LangType int
const (
GO LangType = iota
LUA
PERL
PHP
PYTHON
RUBY
SHELL
TCL
AUTOIT
)
func (c LangType) String() string {
switch c {
case GO:
return "go"
case LUA:
return "lua"
case PERL:
return "perl"
case PHP:
return "php"
case PYTHON:
return "python"
case RUBY:
return "ruby"
case SHELL:
return "shell"
case TCL:
return "tcl"
case AUTOIT:
return "autoit"
}
return "unknown"
}
type ResultStatus int
const (
PASS ResultStatus = iota
FAIL
SKIP
BLOCKED
)
func (c ResultStatus) String() string {
switch c {
case PASS:
return "pass"
case FAIL:
return "fail"
case SKIP:
return "skip"
case BLOCKED:
return "blocked"
}
return "UNKNOWN"
}
type RunMode int
const (
RunModeDir RunMode = iota
RunModeBatch
RunModeSuite
RunModeScript
)
func (c RunMode) String() string {
switch c {
case RunModeDir:
return "dir"
case RunModeBatch:
return "batch"
case RunModeSuite: // can be show with cui by select a suite file
return "suite"
case RunModeScript: // can be show with cui by select a script file
return "script"
}
return "unknown"
}
package dateUtils
import (
"time"
)
func DateStr(tm time.Time) string {
return tm.Format("2006-01-02")
}
func TimeStr(tm time.Time) string {
return tm.Format("15:04:05")
}
func DateTimeStrFmt(tm time.Time, fm string) string {
return tm.Format(fm)
}
func DateTimeStr(tm time.Time) string {
return tm.Format("2006-01-02 15:04:05")
}
func DateTimeStrLong(tm time.Time) string {
return tm.Format("20060102150405")
}
package display
import (
"github.com/easysoft/zentaoatf/src/utils/common"
"github.com/easysoft/zentaoatf/src/utils/shell"
"os"
"os/exec"
"regexp"
"strconv"
"strings"
)
func GetScreenSize() (int, int) {
var cmd string
var width int
var height int
if commonUtils.IsWin() {
cmd = "mode" // tested for win7
out, _ := shellUtils.ExeShell(cmd)
//out := `设备状态 CON:
// ---------
// 行:  300
// 列:   80
// 键盘速度: 31
// 键盘延迟:  1
// 代码页: 936
//`
myExp := regexp.MustCompile(`CON:\s+[^\s]+\s*(.*?)(\d+)\s\s*(.*?)(\d+)\s`)
arr := myExp.FindStringSubmatch(out)
if len(arr) > 4 {
height, _ = strconv.Atoi(strings.TrimSpace(arr[2]))
width, _ = strconv.Atoi(strings.TrimSpace(arr[4]))
}
} else {
width, height = noWindowsSize()
}
return width, height
}
func noWindowsSize() (int, int) {
cmd := exec.Command("stty", "size")
cmd.Stdin = os.Stdin
out, err := cmd.Output()
output := string(out)
if err != nil {
return 0, 0
}
width, height, err := parseSize(output)
return width, height
}
func parseSize(input string) (int, int, error) {
parts := strings.Split(input, " ")
h, err := strconv.Atoi(parts[0])
if err != nil {
return 0, 0, err
}
w, err := strconv.Atoi(strings.Replace(parts[1], "\n", "", 1))
if err != nil {
return 0, 0, err
}
return int(w), int(h), nil
}
package fileUtils
import (
"fmt"
"github.com/easysoft/zentaoatf/res"
commonUtils "github.com/easysoft/zentaoatf/src/utils/common"
constant "github.com/easysoft/zentaoatf/src/utils/const"
"github.com/easysoft/zentaoatf/src/utils/vari"
"io"
"io/ioutil"
"os"
"os/exec"
"path"
"path/filepath"
"regexp"
"strconv"
"strings"
)
func ReadFile(filePath string) string {
buf := ReadFileBuf(filePath)
str := string(buf)
str = commonUtils.RemoveBlankLine(str)
return str
}
func ReadFileBuf(filePath string) []byte {
buf, err := ioutil.ReadFile(filePath)
if err != nil {
return []byte(err.Error())
}
return buf
}
func WriteFile(filePath string, content string) {
dir := filepath.Dir(filePath)
MkDirIfNeeded(dir)
var d1 = []byte(content)
err2 := ioutil.WriteFile(filePath, d1, 0666) //写入文件(字节数组)
check(err2)
}
func check(e error) {
if e != nil {
panic(e)
}
}
func FileExist(path string) bool {
var exist = true
if _, err := os.Stat(path); os.IsNotExist(err) {
exist = false
}
return exist
}
func MkDirIfNeeded(dir string) error {
if !FileExist(dir) {
err := os.MkdirAll(dir, os.ModePerm)
return err
}
return nil
}
func IsDir(f string) bool {
fi, e := os.Stat(f)
if e != nil {
return false
}
return fi.IsDir()
}
func AbosutePath(pth string) string {
if !IsAbosutePath(pth) {
pth, _ = filepath.Abs(pth)
}
pth = UpdateDir(pth)
return pth
}
func IsAbosutePath(pth string) bool {
return path.IsAbs(pth) ||
strings.Index(pth, ":") == 1 // windows
}
func UpdateDir(pth string) string {
sepa := string(os.PathSeparator)
if strings.LastIndex(pth, sepa) < len(pth)-1 {
pth += sepa
}
return pth
}
func GetFilesFromParams(arguments []string) []string {
ret := make([]string, 0)
for _, arg := range arguments {
if strings.Index(arg, "-") != 0 {
if arg == "." {
arg = AbosutePath(".")
} else if strings.Index(arg, "."+string(os.PathSeparator)) == 0 {
arg = AbosutePath(".") + arg[2:]
} else if !IsAbosutePath(arg) {
arg = AbosutePath(".") + arg
}
ret = append(ret, arg)
} else {
break
}
}
return ret
}
func ReadResData(path string) string {
isRelease := commonUtils.IsRelease()
var jsonStr string
if isRelease {
data, _ := res.Asset(path)
jsonStr = string(data)
} else {
jsonStr = ReadFile(path)
}
return jsonStr
}
func GetZTFDir() string { // where ztf command in
var dir string
arg1 := strings.ToLower(os.Args[0])
name := filepath.Base(arg1)
if strings.Index(name, "ztf") == 0 && strings.Index(arg1, "go-build") < 0 {
p, _ := exec.LookPath(os.Args[0])
if strings.Index(p, string(os.PathSeparator)) > -1 {
dir = p[:strings.LastIndex(p, string(os.PathSeparator))]
}
} else { // debug
dir, _ = os.Getwd()
}
dir, _ = filepath.Abs(dir)
dir = UpdateDir(dir)
//fmt.Printf("Debug: Launch %s in %s \n", arg1, dir)
return dir
}
func GetLogDir() string {
path := vari.ZTFDir + constant.LogDir
dir, _ := ioutil.ReadDir(path)
regx := `^\d\d\d$`
numb := 0
for _, fi := range dir {
if fi.IsDir() {
name := fi.Name()
pass, _ := regexp.MatchString(regx, name)
if pass { // 999
name = strings.TrimLeft(name, "0")
nm, _ := strconv.Atoi(name)
if nm >= numb {
numb = nm
}
}
}
}
if numb >= 9 {
numb = 0
tempDir := path[:len(path)-1] + "-bak" + string(os.PathSeparator) + path[len(path):]
childDir := path + "bak" + string(os.PathSeparator) + path[len(path):]
os.RemoveAll(childDir)
os.Rename(path, tempDir)
MkDirIfNeeded(path)
err := os.Rename(tempDir, childDir)
_ = err
}
ret := getLogNumb(numb + 1)
return UpdateDir(path + ret)
}
func getLogNumb(numb int) string {
return fmt.Sprintf("%03s", strconv.Itoa(numb))
}
func CopyFile(src, dst string) (int64, error) {
sourceFileStat, err := os.Stat(src)
if err != nil {
return 0, err
}
if !sourceFileStat.Mode().IsRegular() {
return 0, fmt.Errorf("%s is not a regular file", src)
}
source, err := os.Open(src)
if err != nil {
return 0, err
}
defer source.Close()
destination, err := os.Create(dst)
if err != nil {
return 0, err
}
defer destination.Close()
nBytes, err := io.Copy(destination, source)
return nBytes, err
}
package i118Utils
import (
"encoding/json"
"github.com/easysoft/zentaoatf/res"
"github.com/easysoft/zentaoatf/src/utils/common"
constant "github.com/easysoft/zentaoatf/src/utils/const"
"golang.org/x/text/language"
"golang.org/x/text/message"
"io/ioutil"
)
var I118Prt *message.Printer
func InitI118(lang string) {
//var once sync.Once
//once.Do(func() {
isRelease := commonUtils.IsRelease()
var langRes string
if lang == constant.LanguageEN {
langRes = constant.EnRes
} else {
langRes = constant.ZhRes
}
if isRelease {
data, _ := res.Asset(langRes)
InitResFromAsset(data)
} else {
InitRes(langRes)
}
if lang == "zh" {
I118Prt = message.NewPrinter(language.SimplifiedChinese)
} else {
I118Prt = message.NewPrinter(language.AmericanEnglish)
}
//})
}
type I18n struct {
Language string `json:"language"`
Messages []Message `json:"messages"`
}
type Message struct {
Id string `json:"id"`
Message string `json:"message,omitempty"`
Translation string `json:"translation,omitempty"`
}
func Check(e error) {
if e != nil {
panic(e)
}
}
func ReadI18nJson(file string) string {
b, err := ioutil.ReadFile(file)
Check(err)
str := string(b)
return str
}
func InitRes(jsonPath string) {
var i18n I18n
str := ReadI18nJson(jsonPath)
json.Unmarshal([]byte(str), &i18n)
msgArr := i18n.Messages
tag := language.MustParse(i18n.Language)
for _, e := range msgArr {
message.SetString(tag, e.Id, e.Translation)
}
}
func InitResFromAsset(bytes []byte) {
var i18n I18n
json.Unmarshal(bytes, &i18n)
msgArr := i18n.Messages
tag := language.MustParse(i18n.Language)
for _, e := range msgArr {
message.SetString(tag, e.Id, e.Translation)
}
}
package langUtils
import (
i118Utils "github.com/easysoft/zentaoatf/src/utils/i118"
logUtils "github.com/easysoft/zentaoatf/src/utils/log"
stringUtils "github.com/easysoft/zentaoatf/src/utils/string"
"github.com/easysoft/zentaoatf/src/utils/vari"
"github.com/fatih/color"
"path"
"sort"
"strconv"
"strings"
"sync"
)
var LangMap map[string]map[string]string
func initSupportedScriptLang() map[string]map[string]string {
var once sync.Once
once.Do(func() {
LangMap = map[string]map[string]string{
"bat": {
"extName": "bat",
"commentsTag": "::",
"printGrammar": "echo #",
},
"javascript": {
"extName": "js",
"commentsTag": "//",
"printGrammar": "console.log(\"#\")",
"interpreter": "C:\\nodejs\\node.exe",
},
"lua": {
"extName": "lua",
"commentsTag": "--",
"printGrammar": "print('#')",
"interpreter": "C:\\Lua\\5.1\\lua.exe",
},
"perl": {
"extName": "pl",
"commentsTag": "#",
"printGrammar": "print \"#\\n\";",
"interpreter": "C:\\Perl64\\bin\\perl.exe",
},
"php": {
"extName": "php",
"commentsTag": "//",
"printGrammar": "echo \"#\\n\";",
"interpreter": "C:\\php-7.3.9-Win32-VC15-x64\\php.exe",
},
"python": {
"extName": "py",
"commentsTag": "#",
"printGrammar": "print(\"#\")",
"interpreter": "C:\\Python37-32\\python.exe",
},
"ruby": {
"extName": "rb",
"commentsTag": "#",
"printGrammar": "print(\"#\\n\")",
"interpreter": "C:\\Ruby26-x64\\bin\\ruby.exe",
},
"shell": {
"extName": "sh",
"commentsTag": "#",
"printGrammar": "echo \"#\"",
},
"tcl": {
"extName": "tl",
"commentsTag": "#",
"printGrammar": "set hello \"#\"; \n puts [set hello];",
"interpreter": "C:\\ActiveTcl\\bin\\tclsh.exe",
},
"autoit": {
"extName": "au3",
"commentsTag": "#",
"printGrammar": "ConsoleWrite(text & @CRLF)",
"interpreter": "c:\\Program Files (x86)\\AutoIt3\\AutoIt3_x64.exe",
},
}
})
return LangMap
}
func GetSupportLanguageOptions(scriptExtsInDir []string) ([]string, []string, []string) {
arr0 := GetSupportLanguageArrSort()
numbs := make([]string, 0)
names := make([]string, 0)
labels := make([]string, 0)
for idx, lang := range arr0 {
ext := LangMap[lang]["extName"]
if scriptExtsInDir != nil {
found := stringUtils.FindInArr(ext, scriptExtsInDir)
if !found {
continue
}
}
numbs = append(numbs, strconv.Itoa(idx+1))
names = append(names, lang)
if lang == "bat" || lang == "php" {
lang = stringUtils.UcAll(lang)
} else {
lang = stringUtils.Ucfirst(lang)
}
labels = append(labels, strconv.Itoa(idx+1)+". "+lang)
}
return numbs, names, labels
}
func GetSupportLanguageArrSort() []string {
arr := make([]string, 0)
for lang, _ := range LangMap {
arr = append(arr, lang)
}
sort.Strings(arr)
return arr
}
func GetSupportLanguageExtArr() []string {
arr := make([]string, 0)
for _, key := range GetSupportLanguageArrSort() {
arr = append(arr, LangMap[key]["extName"])
}
return arr
}
func CheckSupportLanguages(scriptLang string) bool {
if LangMap[scriptLang] == nil {
langStr := strings.Join(GetSupportLanguageArrSort(), ", ")
logUtils.PrintToWithColor(i118Utils.I118Prt.Sprintf("only_support_script_language", langStr)+"\n", color.FgRed)
return false
}
return true
}
func GetSupportLanguageExtRegx() string {
regx := "(" + strings.Join(GetSupportLanguageExtArr(), "|") + ")"
return regx
}
func GetExtToNameMap() map[string]string {
extMap := make(map[string]string, 0)
for _, key := range GetSupportLanguageArrSort() {
extMap[LangMap[key]["extName"]] = key
}
return extMap
}
func GetLangByFile(filePath string) string {
ext := path.Ext(filePath)
ext = ext[1:]
lang := vari.ScriptExtToNameMap[ext]
return lang
}
func init() {
initSupportedScriptLang()
}
package logUtils
import (
"fmt"
fileUtils "github.com/easysoft/zentaoatf/src/utils/file"
"github.com/easysoft/zentaoatf/src/utils/vari"
"github.com/fatih/color"
"github.com/rifflock/lfshook"
"github.com/sirupsen/logrus"
"io/ioutil"
"strings"
"unicode/utf8"
)
var Logger *logrus.Logger
func GetWholeLine(msg string, char string) string {
prefixLen := (vari.ScreenWidth - utf8.RuneCountInString(msg) - 2) / 2
if prefixLen <= 0 { // no width in debug mode
prefixLen = 6
}
postfixLen := vari.ScreenWidth - utf8.RuneCountInString(msg) - 2 - prefixLen - 1
if postfixLen <= 0 { // no width in debug mode
postfixLen = 6
}
preFixStr := strings.Repeat(char, prefixLen)
postFixStr := strings.Repeat(char, postfixLen)
return fmt.Sprintf("%s %s %s", preFixStr, msg, postFixStr)
}
func ColoredStatus(status string) string {
temp := strings.ToLower(status)
switch temp {
case "pass":
return color.GreenString(i118Utils.I118Prt.Sprintf(temp))
case "fail":
return color.RedString(i118Utils.I118Prt.Sprintf(temp))
case "skip":
return color.YellowString(i118Utils.I118Prt.Sprintf(temp))
}
return status
}
func InitLogger() *logrus.Logger {
vari.LogDir = fileUtils.GetLogDir()
if Logger != nil {
return Logger
}
Logger = logrus.New()
Logger.Out = ioutil.Discard
pathMap := lfshook.PathMap{
logrus.WarnLevel: vari.LogDir + "log.txt",
logrus.ErrorLevel: vari.LogDir + "result.txt",
}
Logger.Hooks.Add(lfshook.NewHook(
pathMap,
&MyFormatter{},
))
Logger.SetFormatter(&MyFormatter{})
return Logger
}
func Screen(msg string) {
PrintTo(msg)
}
func Log(msg string) {
Logger.Warnln(msg)
}
func Result(msg string) {
Logger.Errorln(msg)
}
func ScreenAndResult(msg string) {
Screen(msg)
Result(msg)
}
type MyFormatter struct {
logrus.TextFormatter
}
func (f *MyFormatter) Format(entry *logrus.Entry) ([]byte, error) {
return []byte(entry.Message + "\n"), nil
}
package logUtils
import (
"encoding/json"
"fmt"
commonUtils "github.com/easysoft/zentaoatf/src/utils/common"
fileUtils "github.com/easysoft/zentaoatf/src/utils/file"
"github.com/easysoft/zentaoatf/src/utils/vari"
"github.com/fatih/color"
"io"
"os"
"regexp"
"strings"
)
var (
usageFile = fmt.Sprintf("res%sdoc%susage.txt", string(os.PathSeparator), string(os.PathSeparator))
sampleFile = fmt.Sprintf("res%sdoc%ssample.txt", string(os.PathSeparator), string(os.PathSeparator))
)
func PrintUsage() {
PrintToWithColor("Usage: ", color.FgCyan)
usage := fileUtils.ReadResData(usageFile)
exeFile := "ztf"
if commonUtils.IsWin() {
exeFile += ".exe"
}
usage = fmt.Sprintf(usage, exeFile)
fmt.Printf("%s\n", usage)
PrintToWithColor("\nExample: ", color.FgCyan)
sample := fileUtils.ReadResData(sampleFile)
if !commonUtils.IsWin() {
regx, _ := regexp.Compile(`\\`)
sample = regx.ReplaceAllString(sample, "/")
regx, _ = regexp.Compile(`ztf.exe`)
sample = regx.ReplaceAllString(sample, "ztf")
regx, _ = regexp.Compile(`/bat/`)
sample = regx.ReplaceAllString(sample, "/shell/")
regx, _ = regexp.Compile(`\.bat\s{4}`)
sample = regx.ReplaceAllString(sample, ".shell")
}
fmt.Printf("%s\n", sample)
}
func PrintTo(str string) {
output := color.Output
fmt.Fprint(output, str+"\n")
}
func PrintToWithColor(msg string, attr color.Attribute) {
output := color.Output
if attr == -1 {
fmt.Fprint(output, msg+"\n")
} else {
color.New(attr).Fprintf(output, msg+"\n")
}
}
func PrintToCmd(msg string, attr color.Attribute) {
var output io.Writer
if vari.RunFromCui {
output, _ = vari.Cui.View("cmd")
} else {
output = color.Output
}
if attr == -1 {
fmt.Fprint(output, msg+"\n")
} else {
clr := color.New(attr)
clr.Fprint(output, msg+"\n")
}
}
func PrintUnicode(str []byte) {
var a interface{}
temp := strings.Replace(string(str), "\\\\", "\\", -1)
err := json.Unmarshal([]byte(temp), &a)
var msg string
if err == nil {
msg = fmt.Sprint(a)
} else {
msg = temp
}
PrintToCmd(msg, -1)
}
package scriptUtils
import (
"fmt"
"github.com/easysoft/zentaoatf/src/model"
fileUtils "github.com/easysoft/zentaoatf/src/utils/file"
zentaoUtils "github.com/easysoft/zentaoatf/src/utils/zentao"
"github.com/emirpasic/gods/maps"
"github.com/emirpasic/gods/maps/linkedhashmap"
"regexp"
"strconv"
"strings"
)
func GetStepAndExpectMap(file string) (maps.Map, maps.Map, maps.Map) {
if fileUtils.FileExist(file) {
txt := fileUtils.ReadFile(file)
_, content := zentaoUtils.ReadCaseInfo(txt)
lines := strings.Split(content, "\n")
groupBlockArr := getGroupBlockArr(lines)
groupArr := getStepNestedArr(groupBlockArr)
_, stepMap, stepTypeMap, expectMap := getSortedTextFromNestedSteps(groupArr)
return stepMap, stepTypeMap, expectMap
}
return nil, nil, nil
}
func SortFile(file string) {
stepsTxt := ""
if fileUtils.FileExist(file) {
txt := fileUtils.ReadFile(file)
info, content := zentaoUtils.ReadCaseInfo(txt)
lines := strings.Split(content, "\n")
groupBlockArr := getGroupBlockArr(lines)
groupArr := getStepNestedArr(groupBlockArr)
stepsTxt, _, _, _ = getSortedTextFromNestedSteps(groupArr)
// replace info
re, _ := regexp.Compile(`(?s)\[case\].*\[esac\]`)
script := re.ReplaceAllString(txt, "[case]\n"+info+"\n"+stepsTxt+"\n\n[esac]")
fileUtils.WriteFile(file, script)
}
}
func getGroupBlockArr(lines []string) [][]string {
groupBlockArr := make([][]string, 0)
idx := 0
for true {
if idx >= len(lines) {
break
}
var groupContent []string
line := strings.TrimSpace(lines[idx])
if isGroup(line) { // must match a group
groupContent = make([]string, 0)
groupContent = append(groupContent, line)
idx++
for true {
if idx >= len(lines) {
groupBlockArr = append(groupBlockArr, groupContent)
break
}
line = strings.TrimSpace(lines[idx])
if isGroup(line) {
groupBlockArr = append(groupBlockArr, groupContent)
break
} else if line != "" && !isGroup(line) {
groupContent = append(groupContent, line)
}
idx++
}
} else {
idx++
}
}
return groupBlockArr
}
func getStepNestedArr(blocks [][]string) []model.TestStep {
ret := make([]model.TestStep, 0)
for _, block := range blocks {
name := block[0]
group := model.TestStep{Desc: name}
if isStepsIdent(block[1]) { // muti line
group.MutiLine = true
childs := loadMutiLineSteps(block[1:])
group.Children = append(group.Children, childs...)
} else {
childs := loadSingleLineSteps(block[1:])
group.Children = append(group.Children, childs...)
}
ret = append(ret, group)
}
return ret
}
func loadMutiLineSteps(arr []string) []model.TestStep {
childs := make([]model.TestStep, 0)
child := model.TestStep{}
idx := 0
for true {
if idx >= len(arr) {
if child.Desc != "" {
childs = append(childs, child)
}
break
}
line := arr[idx]
line = strings.TrimSpace(line)
if isStepsIdent(line) {
if idx > 0 {
childs = append(childs, child)
}
child = model.TestStep{}
idx++
stp := ""
for true { // retrieve next lines
if idx >= len(arr) || hasBrackets(arr[idx]) {
child.Desc = stp
break
}
stp += arr[idx] + "\n"
idx++
}
}
if isExpectsIdent(line) {
idx++
exp := ""
for true { // retrieve next lines
if idx >= len(arr) || hasBrackets(arr[idx]) {
child.Expect = exp
break
}
temp := strings.TrimSpace(arr[idx])
if temp == ">>" {
temp = "pending"
}
exp += temp + "\n"
idx++
}
}
}
return childs
}
func loadSingleLineSteps(arr []string) []model.TestStep {
childs := make([]model.TestStep, 0)
for _, line := range arr {
line = strings.TrimSpace(line)
sections := strings.Split(line, ">>")
expect := ""
if len(sections) > 1 { // has expect
expect = strings.TrimSpace(sections[1])
if expect == "" { // in independent file
expect = "pending"
}
}
child := model.TestStep{Desc: sections[0], Expect: expect}
childs = append(childs, child)
}
return childs
}
func isGroupIdent(str string) bool {
pass, _ := regexp.MatchString(`(?i)\[\s*group\s*\]`, str)
return pass
}
func isStepsIdent(str string) bool {
pass, _ := regexp.MatchString(`(?i)\[.*steps\.*\]`, str)
return pass
}
func isExpectsIdent(str string) bool {
pass, _ := regexp.MatchString(`(?i)\[.*expects\.*\]`, str)
return pass
}
func hasBrackets(str string) bool {
pass, _ := regexp.MatchString(`(?i)()\[.*\]`, str)
return pass
}
func isGroup(str string) bool {
ret := strings.Index(str, ">>") < 0 && hasBrackets(str) && !isStepsIdent(str) && !isExpectsIdent(str)
return ret
}
func getSortedTextFromNestedSteps(groups []model.TestStep) (string, maps.Map, maps.Map, maps.Map) {
ret := make([]string, 0)
stepMap := linkedhashmap.New()
stepTypeMap := linkedhashmap.New()
expectMap := linkedhashmap.New()
groupNumb := 1
for _, group := range groups {
desc := group.Desc
if desc == "[group]" {
ret = append(ret, "\n"+desc)
for idx, child := range group.Children { // level 1 item
numbStr := getNumbStr(groupNumb, -1)
stepTypeMap.Put(numbStr, "item")
if group.MutiLine {
// steps
tag := replaceNumb("[steps]", groupNumb, -1, true)
ret = append(ret, " "+tag)
stepTxt := printMutiStepOrExpect(child.Desc)
ret = append(ret, stepTxt)
stepMap.Put(numbStr, stepTxt)
// expects
tag = replaceNumb("[expects]", groupNumb, -1, true)
ret = append(ret, " "+tag)
expectTxt := printMutiStepOrExpect(child.Expect)
ret = append(ret, expectTxt)
if idx < len(group.Children)-1 {
ret = append(ret, "")
}
expectMap.Put(numbStr, expectTxt)
} else {
stepTxt := strings.TrimSpace(child.Desc)
stepTxtWithNumb := replaceNumb(stepTxt, groupNumb, -1, false)
stepMap.Put(numbStr, stepTxt)
expectTxt := child.Expect
expectTxt = strings.TrimSpace(expectTxt)
expectMap.Put(numbStr, expectTxt)
if expectTxt != "" {
expectTxt = ">> " + expectTxt
}
ret = append(ret, fmt.Sprintf(" %s %s", stepTxtWithNumb, expectTxt))
}
groupNumb++
}
} else {
desc = replaceNumb(group.Desc, groupNumb, -1, true)
ret = append(ret, "\n"+desc)
numbStr := getNumbStr(groupNumb, -1)
stepMap.Put(numbStr, getGroupName(group.Desc))
stepTypeMap.Put(numbStr, "group")
expectMap.Put(numbStr, "")
childNumb := 1
for _, child := range group.Children {
numbStr := getNumbStr(groupNumb, childNumb)
stepTypeMap.Put(numbStr, "item")
if group.MutiLine {
// steps
tag := replaceNumb("[steps]", groupNumb, childNumb, true)
ret = append(ret, " "+tag)
stepTxt := printMutiStepOrExpect(child.Desc)
ret = append(ret, stepTxt)
stepMap.Put(numbStr, stepTxt)
// expects
tag = replaceNumb("[expects]", groupNumb, childNumb, true)
ret = append(ret, " "+tag)
expectTxt := printMutiStepOrExpect(child.Expect)
ret = append(ret, expectTxt)
expectMap.Put(numbStr, expectTxt)
} else {
stepTxt := strings.TrimSpace(child.Desc)
stepMap.Put(numbStr, stepTxt)
expectTxt := child.Expect
expectTxt = strings.TrimSpace(expectTxt)
expectMap.Put(numbStr, expectTxt)
if expectTxt != "" {
expectTxt = ">> " + expectTxt
}
ret = append(ret, fmt.Sprintf(" %s %s", stepTxt, expectTxt))
}
childNumb++
}
groupNumb++
}
}
return strings.Join(ret, "\n"), stepMap, stepTypeMap, expectMap
}
func replaceNumb(str string, groupNumb int, childNumb int, withBrackets bool) string {
numb := getNumbStr(groupNumb, childNumb)
reg := `[\d\.\s]*(.*)`
repl := numb + " ${1}"
if withBrackets {
reg = `\[` + reg + `\]`
repl = `[` + repl + `]`
}
regx, _ := regexp.Compile(reg)
str = regx.ReplaceAllString(str, repl)
return str
}
func getNumbStr(groupNumb int, childNumb int) string {
numb := strconv.Itoa(groupNumb) + "."
if childNumb != -1 {
numb += strconv.Itoa(childNumb) + "."
}
return numb
}
func getGroupName(str string) string {
reg := `\[\d\.\s]*(.*)\]`
repl := "${1}"
regx, _ := regexp.Compile(reg)
str = regx.ReplaceAllString(str, repl)
return str
}
func printMutiStepOrExpect(str string) string {
str = strings.TrimSpace(str)
ret := make([]string, 0)
for _, line := range strings.Split(str, "\n") {
line = strings.TrimSpace(line)
ret = append(ret, fmt.Sprintf("%s%s", strings.Repeat(" ", 4), line))
}
return strings.Join(ret, "\r\n")
}
func GetExpectMapFromIndependentFile(expectMap maps.Map, content string, withEmptyExpect bool) maps.Map {
retMap := linkedhashmap.New()
expectArr := zentaoUtils.ReadExpectIndependentArr(content)
idx := 0
for _, keyIfs := range expectMap.Keys() {
valueIfs, _ := expectMap.Get(keyIfs)
key := strings.TrimSpace(keyIfs.(string))
value := strings.TrimSpace(valueIfs.(string))
if value == "pending" {
retMap.Put(key, strings.Join(expectArr[idx], "\r\n"))
idx++
} else {
if withEmptyExpect {
retMap.Put(key, "")
}
}
}
return retMap
}
package shellUtils
import (
"bufio"
"bytes"
"fmt"
commonUtils "github.com/easysoft/zentaoatf/src/utils/common"
i118Utils "github.com/easysoft/zentaoatf/src/utils/i118"
langUtils "github.com/easysoft/zentaoatf/src/utils/lang"
logUtils "github.com/easysoft/zentaoatf/src/utils/log"
stringUtils "github.com/easysoft/zentaoatf/src/utils/string"
"github.com/easysoft/zentaoatf/src/utils/vari"
"io"
"os"
"os/exec"
"strings"
)
func ExeShell(cmdStr string) (string, error) {
var cmd *exec.Cmd
if commonUtils.IsWin() {
cmd = exec.Command(cmdStr)
} else {
cmd = exec.Command("/bin/bash", "-c", cmdStr)
}
var out bytes.Buffer
cmd.Stdout = &out
err := cmd.Run()
return out.String(), err
}
func ExeShellWithOutput(cmdStr string) []string {
var cmd *exec.Cmd
if commonUtils.IsWin() {
cmd = exec.Command("cmd", "/C", cmdStr)
} else {
cmd = exec.Command("/bin/bash", "-c", cmdStr)
}
output := make([]string, 0)
stdout, err := cmd.StdoutPipe()
if err != nil {
fmt.Println(err)
return output
}
cmd.Start()
if err != nil {
output = append(output, fmt.Sprint(err))
return output
}
reader := bufio.NewReader(stdout)
for {
line, err2 := reader.ReadString('\n')
if err2 != nil || io.EOF == err2 {
break
}
logUtils.Screen(strings.TrimRight(line, "\n"))
output = append(output, line)
}
cmd.Wait()
return output
}
func ExecFile(filePath string) string {
var cmd *exec.Cmd
if commonUtils.IsWin() {
lang := langUtils.GetLangByFile(filePath)
scriptInterpreter := ""
if strings.ToLower(lang) != "bat" {
if vari.Interpreter != "" {
scriptInterpreter = vari.Interpreter
fmt.Printf("use interpreter %s for script %s\n", scriptInterpreter, filePath)
} else {
scriptInterpreter = commonUtils.GetFieldVal(vari.Config, stringUtils.Ucfirst(lang))
}
}
if scriptInterpreter != "" {
if strings.Index(strings.ToLower(scriptInterpreter), "autoit") > -1 {
cmd = exec.Command("cmd", "/C", scriptInterpreter, filePath, "|", "more")
} else {
cmd = exec.Command("cmd", "/C", scriptInterpreter, filePath)
}
} else if strings.ToLower(lang) == "bat" {
cmd = exec.Command("cmd", "/C", filePath)
} else {
fmt.Printf("use interpreter %s for script %s\n", scriptInterpreter, filePath)
i118Utils.I118Prt.Printf("no_interpreter_for_run", filePath, lang)
}
} else {
err := os.Chmod(filePath, 0777)
if err != nil {
logUtils.Screen("chmod error" + err.Error())
}
filePath = "\"" + filePath + "\""
cmd = exec.Command("/bin/bash", "-c", filePath)
}
output := make([]string, 0)
stdout, err := cmd.StdoutPipe()
if err != nil {
fmt.Println(err)
return ""
}
cmd.Start()
if err != nil {
return fmt.Sprint(err)
}
reader := bufio.NewReader(stdout)
for {
line, err2 := reader.ReadString('\n')
if err2 != nil || io.EOF == err2 {
break
}
//logUtils.Trace(strings.TrimRight(line, "\n"))
output = append(output, line)
}
cmd.Wait()
return strings.Join(output, "")
}
package stdinUtils
import (
"bufio"
"fmt"
fileUtils "github.com/easysoft/zentaoatf/src/utils/file"
i118Utils "github.com/easysoft/zentaoatf/src/utils/i118"
langUtils "github.com/easysoft/zentaoatf/src/utils/lang"
logUtils "github.com/easysoft/zentaoatf/src/utils/log"
"github.com/fatih/color"
"os"
"regexp"
"strconv"
"strings"
)
func InputForCheckout(productId *string, moduleId *string, suiteId *string, taskId *string,
independentFile *bool, scriptLang *string) {
var numb string
productCheckbox := ""
suiteCheckbox := ""
taskCheckbox := ""
if *productId != "" {
productCheckbox = "*"
numb = "1"
} else if *suiteId != "" {
suiteCheckbox = "*"
numb = "2"
} else if *taskId != "" {
taskCheckbox = "*"
numb = "3"
}
coType := GetInput("(1|2|3)", numb, "enter_co_type", productCheckbox, suiteCheckbox, taskCheckbox)
coType = strings.ToLower(coType)
if coType == "1" {
*productId = GetInput("\\d+", *productId,
i118Utils.I118Prt.Sprintf("pls_enter")+" "+i118Utils.I118Prt.Sprintf("product_id")+": "+*productId)
*moduleId = GetInput("\\d*", *moduleId,
i118Utils.I118Prt.Sprintf("pls_enter")+" "+i118Utils.I118Prt.Sprintf("module_id")+": "+*moduleId)
} else if coType == "2" {
*suiteId = GetInput("\\d+", *suiteId,
i118Utils.I118Prt.Sprintf("pls_enter")+" "+i118Utils.I118Prt.Sprintf("suite_id")+": "+*suiteId)
} else if coType == "3" {
*taskId = GetInput("\\d+", *taskId,
i118Utils.I118Prt.Sprintf("pls_enter")+" "+i118Utils.I118Prt.Sprintf("task_id")+": "+*taskId)
}
InputForBool(independentFile, false, "enter_co_independent")
numbs, names, labels := langUtils.GetSupportLanguageOptions(nil)
fmtParam := make([]string, 0)
dft := ""
for idx, label := range labels {
if names[idx] == *scriptLang {
dft = strconv.Itoa(idx + 1)
label += " *"
}
fmtParam = append(fmtParam, label)
}
langStr := GetInput("("+strings.Join(numbs, "|")+")", dft, "enter_co_language", strings.Join(fmtParam, "\n"))
langNumb, _ := strconv.Atoi(langStr)
*scriptLang = names[langNumb-1]
}
func InputForDir(dir *string, dft string, i118Key string) {
*dir = GetInput("is_dir", dft, "enter_dir", i118Utils.I118Prt.Sprintf(i118Key))
}
func InputForBool(in *bool, defaultVal bool, fmtStr string, fmtParam ...interface{}) {
str := GetInput("(yes|no|y|n|)", "", fmtStr, fmtParam...)
if str == "" {
*in = defaultVal
msg := ""
if *in {
msg = "yes"
} else {
msg = "no"
}
logUtils.PrintTo(msg)
return
}
if str == "y" && str != "yes" {
*in = true
} else {
*in = false
}
}
func GetInput(regx string, defaultVal string, fmtStr string, params ...interface{}) string {
var ret string
msg := i118Utils.I118Prt.Sprintf(fmtStr, params...)
for {
logUtils.PrintToWithColor("\n"+msg, color.FgCyan)
// fmt.Scanln(&ret)
Scanf(&ret)
//logUtils.PrintToWithColor(fmt.Sprintf("%v", ret), -1)
if strings.TrimSpace(ret) == "" && defaultVal != "" {
ret = defaultVal
logUtils.PrintTo(ret)
}
temp := strings.ToLower(ret)
if temp == "exit" {
color.Unset()
os.Exit(0)
}
//logUtils.PrintToWithColor(ret, -1)
if regx == "" {
return ret
}
var pass bool
var msg string
if regx == "is_dir" {
pass = fileUtils.IsDir(ret)
msg = "dir_not_exist"
} else {
pass, _ = regexp.MatchString("^"+regx+"$", temp)
msg = "invalid_input"
}
if pass {
return ret
} else {
ret = ""
logUtils.PrintToWithColor(i118Utils.I118Prt.Sprintf(msg), color.FgRed)
}
}
}
func GetInputForScriptInterpreter(defaultVal string, fmtStr string, params ...interface{}) string {
var ret string
msg := i118Utils.I118Prt.Sprintf(fmtStr, params...)
for {
logUtils.PrintToWithColor(msg, color.FgCyan)
Scanf(&ret)
ret = strings.TrimSpace(ret)
if ret == "" && defaultVal != "" {
ret = defaultVal
logUtils.PrintToWithColor(ret, -1)
}
if ret == "exit" {
color.Unset()
os.Exit(0)
}
if ret == "" { // ignore to set
return "-"
}
sep := string(os.PathSeparator)
if sep == `\` {
sep = `\\`
}
reg := fmt.Sprintf(".*%s+[^%s]+", sep, sep)
pass, _ := regexp.MatchString(reg, ret)
if pass {
return ret
} else {
ret = ""
logUtils.PrintToWithColor(i118Utils.I118Prt.Sprintf("invalid_input"), color.FgRed)
}
}
}
func Scanf(a *string) {
reader := bufio.NewReader(os.Stdin)
data, _, _ := reader.ReadLine()
*a = string(data)
}
package stringUtils
import (
"regexp"
"strings"
)
func MatchString(expect string, actual string, langType string) bool {
expect = strings.TrimSpace(expect)
actual = strings.TrimSpace(actual)
expect = strings.Replace(expect, "%s", `.+?`, -1) // 字符串
expect = strings.Replace(expect, "%i", `[+\-]?[0-9]+`, -1) // 十进制数字,可有符号
expect = strings.Replace(expect, "%d", `[0-9]+`, -1) // 十进制数字,无符号
expect = strings.Replace(expect, "%x", `[0-9a-fA-F]+`, -1) // 十六进制数字
expect = strings.Replace(expect, "%f", `[+\-]?\.?[0-9]+\.?[0-9]*(E-?[0-9]+)?`, -1) // 十进制浮点数
expect = strings.Replace(expect, "%c", ".", -1) // 单个字符
pass, _ := regexp.MatchString(expect, actual)
return pass
}
package stringUtils
import (
"bytes"
"encoding/binary"
"encoding/hex"
constant "github.com/easysoft/zentaoatf/src/utils/const"
"github.com/mattn/go-runewidth"
"strings"
"unicode"
)
func TrimAll(str string) string {
str = strings.Trim(str, "\n")
str = strings.TrimSpace(str)
return str
}
func Ucfirst(str string) string {
for i, v := range str {
return string(unicode.ToUpper(v)) + str[i+1:]
}
return ""
}
func UcAll(str string) string {
ret := ""
for _, v := range str {
ret += string(unicode.ToUpper(v))
}
return ret
}
func Lcfirst(str string) string {
for i, v := range str {
return string(unicode.ToLower(v)) + str[i+1:]
}
return ""
}
func U2s(form string) (to string, err error) {
bs, err := hex.DecodeString(strings.Replace(form, `\u`, ``, -1))
if err != nil {
return
}
for i, bl, br, r := 0, len(bs), bytes.NewReader(bs), uint16(0); i < bl; i += 2 {
binary.Read(br, binary.BigEndian, &r)
to += string(r)
}
return
}
func BoolToPass(b bool) string {
if b {
return constant.PASS.String()
} else {
return constant.FAIL.String()
}
}
func FindInArr(str string, arr []string) bool {
for _, s := range arr {
if str == s {
return true
}
}
return false
}
func AddPostfix(str string, width int, ch string) string {
lent := runewidth.StringWidth(str)
if width > lent {
postFix := strings.Repeat(ch, width-lent)
str += postFix
}
return str
}
package vari
import (
"github.com/awesome-gocui/gocui"
"github.com/easysoft/zentaoatf/src/model"
"github.com/easysoft/zentaoatf/src/utils/const"
)
var (
Config = model.Config{}
Cui *gocui.Gui
MainViewHeight int
RunMode constant.RunMode
ZTFDir string
LogDir string
RunFromCui bool
UnitTestType string
UnitTestTool string
UnitTestResult string
ProductId string
SessionVar string
SessionId string
RequestType string
RequestFix string = ""
ScriptExtToNameMap map[string]string
CurrScriptFile string // scripts/tc-001.py
CurrResultDate string // 2019-08-15T173802
CurrCaseId int // 2019-08-15T173802
ScreenWidth int
ScreenHeight int
ZentaoBugFileds model.ZentaoBugFileds
//ZentaoCaseFileds model.ZentaoCaseFileds
CurrBug model.Bug
CurrBugStepIds string
Verbose bool
Interpreter string
)
package zentaoUtils
import (
"fmt"
constant "github.com/easysoft/zentaoatf/src/utils/const"
dateUtils "github.com/easysoft/zentaoatf/src/utils/date"
fileUtils "github.com/easysoft/zentaoatf/src/utils/file"
"github.com/easysoft/zentaoatf/src/utils/vari"
"os"
"path"
"regexp"
"strconv"
"strings"
"time"
)
func GenSuperApiUri(model string, methd string, params [][]string) string {
var sep string
if vari.RequestType == constant.RequestTypePathInfo {
sep = ","
} else {
sep = "&"
}
paramStr := "" // format is key1=val1&key2=val2
i := 0
for _, p := range params {
if i > 0 {
paramStr += sep
}
paramStr += p[0] + "=" + p[1]
i++
}
var uri string
if vari.RequestType == constant.RequestTypePathInfo {
uri = fmt.Sprintf("api-getmodel-%s-%s-%s.json", model, methd, paramStr)
} else {
uri = fmt.Sprintf("index.php?m=api&f=getmodel&model=%s&methodName=%s&params=%s&t=json", model, methd, paramStr)
}
return uri
}
func GenApiUri(module string, methd string, param string) string {
var uri string
if vari.RequestType == constant.RequestTypePathInfo {
uri = fmt.Sprintf("%s-%s-%s.json", module, methd, param)
} else {
uri = fmt.Sprintf("index.php?m=%s&f=%s&%s&t=json", module, methd, param)
}
return uri
}
func ScriptToExpectName(file string) string {
fileSuffix := path.Ext(file)
expectName := strings.TrimSuffix(file, fileSuffix) + ".exp"
return expectName
}
func RunDateFolder() string {
runName := dateUtils.DateTimeStrFmt(time.Now(), "2006-01-02T150405") + string(os.PathSeparator)
return runName
}
func GetCaseInfo(file string) (bool, int, int, string) {
var caseId int
var productId int
var title string
content := fileUtils.ReadFile(file)
pass := CheckFileContentIsScript(content)
if !pass {
return false, caseId, productId, title
}
caseInfo := ""
myExp := regexp.MustCompile(`(?s)\[case\](.*)\[esac\]`)
arr := myExp.FindStringSubmatch(content)
if len(arr) > 1 {
caseInfo = arr[1]
}
myExp = regexp.MustCompile(`[\S\s]*cid=\s*([^\n]*?)\s*\n`)
arr = myExp.FindStringSubmatch(caseInfo)
if len(arr) > 1 {
caseId, _ = strconv.Atoi(arr[1])
}
myExp = regexp.MustCompile(`[\S\s]*pid=\s*([^\n]*?)\s*\n`)
arr = myExp.FindStringSubmatch(caseInfo)
if len(arr) > 1 {
productId, _ = strconv.Atoi(arr[1])
}
myExp = regexp.MustCompile(`[\S\s]*title=\s*([^\n]*?)\s*\n`)
arr = myExp.FindStringSubmatch(caseInfo)
if len(arr) > 1 {
title = arr[1]
}
return pass, caseId, productId, title
}
//func ReadScriptCheckpoints(file string) ([]string, [][]string) {
// _, expectIndependentContent := GetDependentExpect(file)
//
// content := fileUtils.ReadFile(file)
// _, checkpoints := ReadCaseInfo(content)
//
// cpStepArr, expectArr := getCheckpointStepArr(checkpoints, expectIndependentContent)
//
// return cpStepArr, expectArr
//}
func getCheckpointStepArr(content string, expectIndependentContent string) ([]string, [][]string) {
cpStepArr := make([]string, 0)
expectArr := make([][]string, 0)
independentExpect := expectIndependentContent != ""
lines := strings.Split(content, "\n")
i := 0
for i < len(lines) {
step := ""
expects := make([]string, 0)
line := strings.TrimSpace(lines[i])
regx := regexp.MustCompile(`(?U:[\d\.]*)(.+)>>(.*)`)
arr := regx.FindStringSubmatch(line)
if len(arr) > 2 {
step = arr[1]
if !independentExpect {
expects = append(expects, strings.TrimSpace(arr[2]))
}
} else {
regx = regexp.MustCompile(`\[([\d\.]*).*expects\]`)
arr = regx.FindStringSubmatch(line)
if len(arr) > 1 {
step = arr[1]
if !independentExpect {
for i+1 < len(lines) {
ln := strings.TrimSpace(lines[i+1])
if strings.Index(ln, "[") == 0 || strings.Index(ln, ">>") > 0 || ln == "" {
break
} else {
expects = append(expects, ln)
i++
}
}
}
}
}
if step != "" && len(expects) > 0 {
cpStepArr = append(cpStepArr, step)
if !independentExpect {
expectArr = append(expectArr, expects)
}
}
i++
}
if independentExpect {
expectArr = ReadExpectIndependentArr(expectIndependentContent)
}
return cpStepArr, expectArr
}
func ReadExpectIndependentArr(content string) [][]string {
lines := strings.Split(content, "\n")
ret := make([][]string, 0)
var cpArr []string
for idx, line := range lines {
line = strings.TrimSpace(line)
if line == ">>" { // more than one line
cpArr = make([]string, 0)
} else if strings.Index(line, ">>") == 0 { // single line
line = strings.Replace(line, ">>", "", -1)
line = strings.TrimSpace(line)
cpArr = append(cpArr, line)
ret = append(ret, cpArr)
cpArr = make([]string, 0)
} else { // under >>
cpArr = append(cpArr, line)
if idx == len(lines)-1 || strings.Index(lines[idx+1], ">>") > -1 {
ret = append(ret, cpArr)
cpArr = make([]string, 0)
}
}
}
return ret
}
func ReadLogArr(content string) (bool, [][]string) {
lines := strings.Split(content, "\n")
ret := make([][]string, 0)
var cpArr []string
model := ""
for idx, line := range lines {
line = strings.TrimSpace(line)
if line == "skip" {
return true, nil
}
if line == ">>" { // more than one line
model = "multi"
cpArr = make([]string, 0)
} else if strings.Index(line, ">>") == 0 { // single line
model = "single"
line = strings.Replace(line, ">>", "", -1)
line = strings.TrimSpace(line)
cpArr = append(cpArr, line)
ret = append(ret, cpArr)
cpArr = make([]string, 0)
} else {
if model == "" || model == "single" {
continue
}
// under >>
cpArr = append(cpArr, line)
if idx == len(lines)-1 || strings.Index(lines[idx+1], ">>") > -1 {
ret = append(ret, cpArr)
cpArr = make([]string, 0)
}
}
}
return false, ret
}
func CheckFileIsScript(path string) bool {
content := fileUtils.ReadFile(path)
pass := CheckFileContentIsScript(content)
return pass
}
func CheckFileContentIsScript(content string) bool {
pass, _ := regexp.MatchString(`\[case\]`, content)
return pass
}
func ReadCaseInfo(content string) (string, string) {
myExp := regexp.MustCompile(`(?s)\[case\]((?U:.*pid.*))\n(.*)\[esac\]`)
arr := myExp.FindStringSubmatch(content)
if len(arr) > 2 {
info := strings.TrimSpace(arr[1])
checkpoints := strings.TrimSpace(arr[2])
return info, checkpoints
}
return "", ""
}
func ReadCaseId(content string) string {
myExp := regexp.MustCompile(`(?s)\[case\].*\ncid=((?U:.*))\n.*\[esac\]`)
arr := myExp.FindStringSubmatch(content)
if len(arr) > 1 {
id := strings.TrimSpace(arr[1])
return id
}
return ""
}
func GetDependentExpect(file string) (bool, string) {
expectIndependentFile := strings.Replace(file, path.Ext(file), ".exp", -1)
if fileUtils.FileExist(expectIndependentFile) {
expectIndependentContent := fileUtils.ReadFile(expectIndependentFile)
return true, expectIndependentContent
}
return false, ""
}
package main
import (
"flag"
configUtils "github.com/easysoft/zendata/src/utils/config"
logUtils "github.com/easysoft/zendata/src/utils/log"
"github.com/easysoft/zendata/src/utils/vari"
"github.com/fatih/color"
"os"
"os/signal"
"syscall"
)
var (
language string
file string
count string
field string
parse string
out bool
table string
help bool
flagSet *flag.FlagSet
)
func main() {
channel := make(chan os.Signal)
signal.Notify(channel, os.Interrupt, syscall.SIGTERM)
go func() {
<-channel
cleanup()
os.Exit(0)
}()
flagSet = flag.NewFlagSet("zdata", flag.ContinueOnError)
flagSet.StringVar(&file, "f", "", "")
flagSet.StringVar(&file, "file", "", "")
flagSet.StringVar(&file, "c", "", "")
flagSet.StringVar(&file, "count", "", "")
flagSet.StringVar(&file, "field", "", "")
flagSet.StringVar(&file, "p", "", "")
flagSet.StringVar(&file, "parse", "", "")
flagSet.StringVar(&file, "o", "", "")
flagSet.StringVar(&file, "out", "", "")
flagSet.StringVar(&file, "t", "", "")
flagSet.StringVar(&file, "table", "", "")
flagSet.StringVar(&file, "h", "", "")
flagSet.StringVar(&file, "help", "", "")
if len(os.Args) == 1 {
os.Args = append(os.Args, "run", ".")
}
switch os.Args[1] {
case "checkout", "co":
if err := flagSet.Parse(os.Args[2:]); err == nil {
}
case "help", "-h":
logUtils.PrintUsage()
default: // run
if len(os.Args) > 1 {
args := []string{os.Args[0], "run"}
args = append(args, os.Args[1:]...)
//run(args)
} else {
logUtils.PrintUsage()
}
}
}
func init() {
cleanup()
vari.RunFromCui = false
configUtils.InitConfig()
}
func cleanup() {
color.Unset()
}
\ No newline at end of file
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册