package scriptHelper import ( "fmt" "html" "io/ioutil" "path" "path/filepath" "regexp" "strconv" "strings" commConsts "github.com/easysoft/zentaoatf/internal/pkg/consts" commDomain "github.com/easysoft/zentaoatf/internal/pkg/domain" langHelper "github.com/easysoft/zentaoatf/internal/pkg/helper/lang" "github.com/easysoft/zentaoatf/pkg/consts" commonUtils "github.com/easysoft/zentaoatf/pkg/lib/common" fileUtils "github.com/easysoft/zentaoatf/pkg/lib/file" ) func ReplaceCaseDesc(desc, file string) { content := fileUtils.ReadFile(file) lang := langHelper.GetLangByFile(file) regStr := fmt.Sprintf(`(?smU)%s((?U:.*pid.*))\n(.*)%s`, commConsts.LangCommentsRegxMap[lang][0], commConsts.LangCommentsRegxMap[lang][1]) re, _ := regexp.Compile(regStr) newDesc := fmt.Sprintf("\n%s\n\n"+desc+"\n\n%s", commConsts.LangCommentsTagMap[lang][0], commConsts.LangCommentsTagMap[lang][1]) out := re.ReplaceAllString(content, newDesc) fileUtils.WriteFile(file, out) } func GetStepAndExpectMap(file string) (steps []commDomain.ZentaoCaseStep) { if !fileUtils.FileExist(file) { return } lang := langHelper.GetLangByFile(file) txt := fileUtils.ReadFile(file) _, checkpoints := ReadCaseInfo(txt, lang) lines := strings.Split(checkpoints, "\n") groupArr := getStepNestedArr(lines) _, steps = getSortedTextFromNestedSteps(groupArr) isIndependent, expectIndependentContent := GetDependentExpect(file) if isIndependent { GetExpectMapFromIndependentFile(&steps, expectIndependentContent, false) } return } 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(lines []string) (ret []commDomain.ZtfStep) { parent := commDomain.ZtfStep{} increase := 0 for index := 0; index < len(lines); index++ { line := lines[index] lineTrim := strings.TrimSpace(line) if lineTrim == "" || lineTrim == ">>" { continue } if strings.Index(line, " ") != 0 { parent, increase = parserNextLines(line, lines[index+1:]) index += increase if strings.TrimSpace(parent.Expect) == "" && strings.Index(line, ">>") > -1 { parent.Expect = commConsts.ExpectResultPass } ret = append(ret, parent) } else { // 有缩进 child := commDomain.ZtfStep{} child, increase = parserNextLines(line, lines[index+1:]) index += increase if parent.Desc != "" { if strings.TrimSpace(child.Expect) == "" && strings.Index(line, ">>") > -1 { child.Expect = commConsts.ExpectResultPass } ret[len(ret)-1].Children = append(ret[len(ret)-1].Children, child) } } } return } func parserNextLines(str string, nextLines []string) (ret commDomain.ZtfStep, increase int) { arr := strings.Split(str, ">>") desc := strings.TrimSpace(arr[0]) expect := "" if len(arr) > 1 { expect = strings.TrimSpace(arr[1]) } if strings.Index(str, ">>") < 0 || expect != "" { // no >> or single line expect ret = commDomain.ZtfStep{Desc: desc, Expect: expect} return } if strings.Index(str, ">>") > -1 { // will test if it has multi-line expect for index, line := range nextLines { if strings.TrimSpace(line) == ">>" { increase = index break } if strings.Index(line, ">>") > -1 { expect = "" break } if len(expect) > 0 { expect += "\r\n" } expect += strings.TrimSpace(line) } if increase == 0 { // multi-line expect = "" } } ret = commDomain.ZtfStep{Desc: desc, Expect: expect} return } func loadMultiLineSteps(arr []string) []commDomain.ZtfStep { childs := make([]commDomain.ZtfStep, 0) child := commDomain.ZtfStep{} 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 = commDomain.ZtfStep{} 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 = "" } exp += temp + "\n" idx++ } } } return childs } func loadSingleLineSteps(arr []string) []commDomain.ZtfStep { children := make([]commDomain.ZtfStep, 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]) } child := commDomain.ZtfStep{Desc: sections[0], Expect: expect} children = append(children, child) } return children } 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 []commDomain.ZtfStep) (ret string, steps []commDomain.ZentaoCaseStep) { arr := make([]string, 0) for _, group := range groups { step := commDomain.ZentaoCaseStep{} stepType := commConsts.Item if len(group.Children) > 0 { stepType = commConsts.Group } step.Type = stepType stepTxt := strings.TrimSpace(group.Desc) step.Desc = stepTxt expectTxt := strings.TrimSpace(group.Expect) expectTxt = strings.TrimRight(expectTxt, "]]") expectTxt = strings.TrimSpace(expectTxt) step.Expect = expectTxt steps = append(steps, step) if expectTxt != "" { expectTxt = ">> " + expectTxt } arr = append(arr, fmt.Sprintf(" %s %s", stepTxt, expectTxt)) for _, child := range group.Children { stepChild := commDomain.ZentaoCaseStep{} stepChild.Type = commConsts.Item stepTxt := strings.TrimSpace(child.Desc) stepChild.Desc = stepTxt expectTxt := strings.TrimSpace(child.Expect) stepChild.Expect = expectTxt steps = append(steps, stepChild) if expectTxt != "" { expectTxt = ">> " + expectTxt } arr = append(arr, fmt.Sprintf(" %s %s", stepTxt, expectTxt)) } } ret = strings.Join(arr, "\n") return } 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(steps *[]commDomain.ZentaoCaseStep, content string, withEmptyExpect bool) { expectArr := ReadExpectIndependentArr(content) index := 0 for idx, _ := range *steps { if len(expectArr) > index && (*steps)[idx].Expect == "pass" { // not set step that has no expect (*steps)[idx].Expect = strings.Join(expectArr[index], "\r\n") index++ } else { if withEmptyExpect { (*steps)[idx].Expect = "" } } } return } func GetCaseContent(stepObj commDomain.ZtfStep, seq string, independentFile bool, isChild bool) ( stepContent, expectContent string) { step := strings.TrimSpace(stepObj.Desc) expect := strings.TrimSpace(stepObj.Expect) stepStr := getStepContent(step, isChild) expectStr := getExpectContent(expect, isChild, independentFile) if !independentFile { stepContent = stepStr + expectStr } else { stepContent = stepStr if stepObj.Children == nil || len(stepObj.Children) == 0 { stepContent += " >>" } } expectContent = expectStr stepContent = html.UnescapeString(stepContent) expectContent = html.UnescapeString(expectContent) return } func getStepContent(str string, isChild bool) (ret string) { str = strings.TrimSpace(str) rpl := "\n" if isChild { rpl = "\n" + " " } ret = strings.ReplaceAll(str, "\r\n", rpl) if isChild { ret = " " + ret } return } func getExpectContent(str string, isChild bool, independentFile bool) (ret string) { str = strings.TrimSpace(str) if str == "" { return } isSingleLine := strings.Count(str, "\r\n") == 0 if isSingleLine { if independentFile { ret = str } else { ret = " >> " + str } } else { // multi-line rpl := "\r\n" space := " " spaceBeforeTerminator := "" spaceBeforeText := space if isChild { spaceBeforeTerminator = space spaceBeforeText = strings.Repeat(space, 2) } if independentFile { //>> // expect 1.2 line 1 // expect 1.2 line 2 //>> ret = ">>\n" + space + strings.ReplaceAll(str, rpl, rpl+space) + "\n>>" } else { //step 1.2 >> // expect 1.2 line 1 // expect 1.2 line 2 //>> ret = " >> \n" + spaceBeforeText + strings.ReplaceAll(str, rpl, rpl+spaceBeforeText) + "\n" + spaceBeforeTerminator + ">>" } } return } func IsMultiLine(step commDomain.ZtfStep) bool { if strings.Index(step.Desc, "\n") > -1 || strings.Index(step.Expect, "\n") > -1 { return true } return false } 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) (pass bool, caseId, productId int, title string, timeout int64) { content := fileUtils.ReadFile(file) isOldFormat := strings.Index(content, "[esac]") > -1 pass = CheckFileContentIsScript(content) if !pass { return false, caseId, productId, title, timeout } caseInfo := "" lang := langHelper.GetLangByFile(file) regStr := "" if isOldFormat { regStr = `(?s)\[case\](.*)\[esac\]` } else { regStr = fmt.Sprintf(`(?sm)%s((?U:.*pid.*))\n(.*)%s`, commConsts.LangCommentsRegxMap[lang][0], commConsts.LangCommentsRegxMap[lang][1]) } myExp := regexp.MustCompile(regStr) arr := myExp.FindStringSubmatch(content) if len(arr) > 1 { caseInfo = arr[1] } caseInfo += "\n" 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]*timeout=\s*([^\n]*?)\s*\n`) arr = myExp.FindStringSubmatch(caseInfo) if len(arr) > 1 { timeout, _ = strconv.ParseInt(arr[1], 10, 64) } 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=([^\n]*?)\n`) arr = myExp.FindStringSubmatch(caseInfo) if len(arr) > 1 { title = strings.TrimSpace(arr[1]) } if caseId <= 0 { pass = false } return } func ReadExpectIndependentArr(content string) [][]string { //正常显示6 //E2.16 //>> // E2.2 - 16 // E2.2 - 26 //>> //>> // E3 - 16 // E3 - 26 //>> lines := strings.Split(content, "\n") ret := make([][]string, 0) var cpArr []string currModel := "" idx := 0 for idx < len(lines) { line := strings.TrimSpace(lines[idx]) if line == ">>" { // more than one line currModel = "multi" cpArr = make([]string, 0) } else if currModel == "multi" { // in >> and >> in multi line mode cpArr = append(cpArr, line) if idx == len(lines)-1 || strings.Index(lines[idx+1], ">>") > -1 { // end multi line temp := make([]string, 0) temp = append(temp, strings.Join(cpArr, "\r\n")) ret = append(ret, temp) cpArr = make([]string, 0) currModel = "" idx += 1 } } else { currModel = "single" line = strings.TrimSpace(line) cpArr = append(cpArr, line) ret = append(ret, cpArr) cpArr = make([]string, 0) } idx += 1 } return ret } func ReadLogArr(content string) (isSkip bool, ret [][]string) { lines := strings.Split(content, "\n") ret = make([][]string, 0) var cpArr []string model := "" for idx := 0; idx < len(lines); idx++ { line := strings.TrimSpace(lines[idx]) if line == "skip" { isSkip = true return } if line == ">>" { // more than one line model = "multi" cpArr = make([]string, 0) } else if model == "multi" { // in >> and >> in multi line mode cpArr = append(cpArr, line) if idx == len(lines)-1 || strings.Index(lines[idx+1], ">>") > -1 { temp := make([]string, 0) temp = append(temp, cpArr...) ret = append(ret, temp) cpArr = make([]string, 0) idx = idx + 1 model = "" } } else { model = "single" line = strings.TrimSpace(line) cpArr = append(cpArr, line) ret = append(ret, cpArr) cpArr = make([]string, 0) } } return } func CheckFileIsScript(path string) bool { content := fileUtils.ReadFile(path) pass := CheckFileContentIsScript(content) return pass } func CheckFileContentIsScript(content string) bool { pass, _ := regexp.MatchString(`cid\b*=`, content) return pass } func ReadCaseInfo(content, lang string) (info, checkpoints string) { regStr := fmt.Sprintf(`(?smU)%s((?U:.*pid.*))\n(.*)%s`, commConsts.LangCommentsRegxMap[lang][0], commConsts.LangCommentsRegxMap[lang][1]) myExp := regexp.MustCompile(regStr) arr := myExp.FindStringSubmatch(content) if len(arr) > 2 { info = strings.TrimSpace(arr[1]) checkpoints = strings.TrimSpace(arr[2]) return } return } func ReadCaseId(content string) string { myExp := regexp.MustCompile(`(?s).*\ncid=((?U:.*))\n.*`) arr := myExp.FindStringSubmatch(content) if len(arr) > 1 { id := strings.TrimSpace(arr[1]) return id } return "" } func GetDependentExpect(file string) (bool, string) { dir := fileUtils.AddFilePathSepIfNeeded(filepath.Dir(file)) name := strings.Replace(filepath.Base(file), path.Ext(file), ".exp", -1) expectIndependentFile := dir + name if !fileUtils.FileExist(expectIndependentFile) { expectIndependentFile = dir + "." + name } if fileUtils.FileExist(expectIndependentFile) { expectIndependentContent := fileUtils.ReadFile(expectIndependentFile) return true, expectIndependentContent } return false, "" } func GetScriptByIdsInDir(dirPth string, idMap *map[int]string) error { dirPth = fileUtils.AbsolutePath(dirPth) sep := consts.FilePthSep if commonUtils.IgnoreZtfFile(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) } else { regx := langHelper.GetSupportLanguageExtRegx() pass, _ := regexp.MatchString("^*.\\."+regx+"$", name) if !pass { continue } path := dirPth + name pass, id, _, _, _ := GetCaseInfo(path) if pass { (*idMap)[id] = path } } } return nil } func GetCaseIdsInSuiteFile(name string, ids *[]int) { 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 { *ids = append(*ids, id) } } }