result.go 11.5 KB
Newer Older
aaronchen2k2k's avatar
aaronchen2k2k 已提交
1
package execHelper
aaronchen2k2k's avatar
aaronchen2k2k 已提交
2 3 4

import (
	"fmt"
5 6 7
	"regexp"
	"strconv"
	"strings"
Z
zhaoke 已提交
8
	"sync"
9

Z
zhaoke 已提交
10 11 12 13
	i118Utils "github.com/easysoft/zentaoatf/pkg/lib/i118"
	logUtils "github.com/easysoft/zentaoatf/pkg/lib/log"
	stringUtils "github.com/easysoft/zentaoatf/pkg/lib/string"

aaronchen2k2k's avatar
aaronchen2k2k 已提交
14 15 16 17 18
	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"
	scriptHelper "github.com/easysoft/zentaoatf/internal/pkg/helper/script"
	websocketHelper "github.com/easysoft/zentaoatf/internal/pkg/helper/websocket"
aaronchen2k2k's avatar
aaronchen2k2k 已提交
19
	"github.com/kataras/iris/v12"
aaronchen2k2k's avatar
report  
aaronchen2k2k 已提交
20
	"github.com/kataras/iris/v12/websocket"
aaronchen2k2k's avatar
aaronchen2k2k 已提交
21 22 23
	"github.com/mattn/go-runewidth"
)

Z
zhaoke 已提交
24
func CheckCaseResult(execParams commDomain.ExecParams, logs string, wsMsg *websocket.Message, errOutput string, lock *sync.Mutex) {
Z
zhaoke 已提交
25
	steps := scriptHelper.GetStepAndExpectMap(execParams.ScriptFile)
aaronchen2k2k's avatar
report  
aaronchen2k2k 已提交
26

Z
zhaoke 已提交
27
	isIndependent, expectIndependentContent := scriptHelper.GetDependentExpect(execParams.ScriptFile)
aaronchen2k2k's avatar
aaronchen2k2k 已提交
28
	if isIndependent {
aaronchen2k2k's avatar
aaronchen2k2k 已提交
29
		scriptHelper.GetExpectMapFromIndependentFile(&steps, expectIndependentContent, false)
aaronchen2k2k's avatar
aaronchen2k2k 已提交
30 31 32
	}

	skip := false
Z
zhaoke 已提交
33
	skip, actualArr := scriptHelper.ReadLogArr(logs)
34 35 36
	if len(actualArr) == 0 {
		skip, actualArr = scriptHelper.ReadLogArrOld(logs)
	}
aaronchen2k2k's avatar
aaronchen2k2k 已提交
37

Z
zhaoke 已提交
38
	language := langHelper.GetLangByFile(execParams.ScriptFile)
Z
zhaoke 已提交
39
	ValidateCaseResult(execParams, language, steps, skip, actualArr, wsMsg, errOutput, lock)
aaronchen2k2k's avatar
aaronchen2k2k 已提交
40 41
}

Z
zhaoke 已提交
42 43
func ValidateCaseResult(execParams commDomain.ExecParams, langType string,
	steps []commDomain.ZentaoCaseStep, skip bool, actualArr [][]string,
Z
zhaoke 已提交
44
	wsMsg *websocket.Message, errOutput string, lock *sync.Mutex) {
aaronchen2k2k's avatar
aaronchen2k2k 已提交
45

Z
zhaoke 已提交
46
	key := stringUtils.Md5(execParams.ScriptFile)
aaronchen2k2k's avatar
aaronchen2k2k 已提交
47

Z
zhaoke 已提交
48
	_, caseId, productId, title, _ := scriptHelper.GetCaseInfo(execParams.ScriptFile)
aaronchen2k2k's avatar
aaronchen2k2k 已提交
49

Z
zhaoke 已提交
50
	stepLogs, caseResult := getStepLogs(skip, steps, actualArr, langType, errOutput)
aaronchen2k2k's avatar
aaronchen2k2k 已提交
51

Z
zhaoke 已提交
52 53 54
	if lock != nil {
		lock.Lock()
	}
aaronchen2k2k's avatar
aaronchen2k2k 已提交
55

Z
zhaoke 已提交
56
	incrReportNum(caseResult, execParams.Report)
aaronchen2k2k's avatar
aaronchen2k2k 已提交
57

Z
zhaoke 已提交
58
	relativePath := strings.TrimPrefix(execParams.ScriptFile, commConsts.WorkDir)
aaronchen2k2k's avatar
aaronchen2k2k 已提交
59
	csResult := commDomain.FuncResult{Id: caseId, ProductId: productId, Title: title,
Z
zhaoke 已提交
60 61
		Key: key, Path: execParams.ScriptFile, RelativePath: relativePath, Status: caseResult, Steps: stepLogs}
	execParams.Report.FuncResult = append(execParams.Report.FuncResult, csResult)
aaronchen2k2k's avatar
aaronchen2k2k 已提交
62

63 64 65
	if lock != nil {
		lock.Unlock()
	}
aaronchen2k2k's avatar
aaronchen2k2k 已提交
66

Z
zhaoke 已提交
67
	width := strconv.Itoa(len(strconv.Itoa(len(execParams.CasesToRun))))
aaronchen2k2k's avatar
aaronchen2k2k 已提交
68

Z
zhaoke 已提交
69
	path := relativePath
Z
zhaoke 已提交
70
	csTitle := csResult.Title
Z
zhaoke 已提交
71
	lenp := runewidth.StringWidth(csResult.Path)
Z
zhaoke 已提交
72
	lent := runewidth.StringWidth(csTitle)
aaronchen2k2k's avatar
aaronchen2k2k 已提交
73

Z
zhaoke 已提交
74 75
	if execParams.PathMaxWidth > lenp {
		postFix := strings.Repeat(" ", execParams.PathMaxWidth-lenp)
aaronchen2k2k's avatar
aaronchen2k2k 已提交
76
		path += postFix
Z
zhaoke 已提交
77
		relativePath += postFix
aaronchen2k2k's avatar
aaronchen2k2k 已提交
78 79
	}

Z
zhaoke 已提交
80 81
	if execParams.TitleMaxWidth > lent {
		postFix := strings.Repeat(" ", execParams.TitleMaxWidth-lent)
Z
zhaoke 已提交
82 83 84
		csTitle += postFix
	}

Z
zhaoke 已提交
85
	format := "(%" + width + "d/%d) [%s] [%s] [%s] [%ss]"
aaronchen2k2k's avatar
aaronchen2k2k 已提交
86

Z
zhaoke 已提交
87
	statusWithColor, status := GenStatusTxt(csResult.Status)
Z
zhaoke 已提交
88

Z
zhaoke 已提交
89 90
	msg := fmt.Sprintf(format, len(execParams.Report.FuncResult), len(execParams.CasesToRun), status, path, csTitle, execParams.Secs)
	msgWithColor := fmt.Sprintf(format, len(execParams.Report.FuncResult), len(execParams.CasesToRun), statusWithColor, path, csTitle, execParams.Secs)
aaronchen2k2k's avatar
report  
aaronchen2k2k 已提交
91

aaronchen2k2k's avatar
aaronchen2k2k 已提交
92
	// print each case result
93
	if commConsts.ExecFrom == commConsts.FromClient {
aaronchen2k2k's avatar
aaronchen2k2k 已提交
94
		msgCategory := commConsts.Result
aaronchen2k2k's avatar
aaronchen2k2k 已提交
95
		if csResult.Status == commConsts.FAIL {
aaronchen2k2k's avatar
aaronchen2k2k 已提交
96
			msgCategory = commConsts.Error
aaronchen2k2k's avatar
aaronchen2k2k 已提交
97
		}
aaronchen2k2k's avatar
aaronchen2k2k 已提交
98

aaronchen2k2k's avatar
aaronchen2k2k 已提交
99 100 101
		totalStepCount := len(csResult.Steps)
		passStepCount := 0
		failStepCount := 0
aaronchen2k2k's avatar
aaronchen2k2k 已提交
102

aaronchen2k2k's avatar
aaronchen2k2k 已提交
103 104 105 106 107 108
		failedCheckpoints := make([]string, 0)
		passStepCount, failStepCount = appendFailedStepResult(csResult, &failedCheckpoints)

		arr := []string{i118Utils.Sprintf("steps_result_msg", totalStepCount, passStepCount, failStepCount)}
		arr = append(arr, failedCheckpoints...)
		msg = strings.Join(arr, "<br/>")
aaronchen2k2k's avatar
aaronchen2k2k 已提交
109 110 111

		websocketHelper.SendExecMsg(msg, "", msgCategory,
			iris.Map{"key": key, "status": csResult.Status}, wsMsg)
m0_58228130's avatar
ztfTest  
m0_58228130 已提交
112
	}
Z
zhaoke 已提交
113
	logUtils.ExecConsole(-1, msgWithColor)
aaronchen2k2k's avatar
report  
aaronchen2k2k 已提交
114
	logUtils.ExecResult(msg)
aaronchen2k2k's avatar
aaronchen2k2k 已提交
115 116
}

aaronchen2k2k's avatar
aaronchen2k2k 已提交
117 118 119
func ValidateStepResult(langType string, expectLines []string, actualLines []string) (
	stepResult commConsts.ResultStatus, checkpointLogs []commDomain.CheckPointLog) {
	stepResult = commConsts.PASS
aaronchen2k2k's avatar
aaronchen2k2k 已提交
120

aaronchen2k2k's avatar
aaronchen2k2k 已提交
121
	idx := 0
aaronchen2k2k's avatar
aaronchen2k2k 已提交
122 123
	for _, expect := range expectLines {
		log := "N/A"
aaronchen2k2k's avatar
aaronchen2k2k 已提交
124 125
		if len(actualLines) > idx {
			log = strings.TrimSpace(actualLines[idx])
aaronchen2k2k's avatar
aaronchen2k2k 已提交
126 127 128
		}

		expect = strings.TrimSpace(expect)
aaronchen2k2k's avatar
aaronchen2k2k 已提交
129

aaronchen2k2k's avatar
aaronchen2k2k 已提交
130
		var pass bool
Z
zhaoke 已提交
131 132 133
		if len(expect) > 1 && expect[0:1] == "~" && expect[len(expect)-1:] == "~" {
			pass = MatchScene(expect[1:len(expect)-1], log, langType)
		} else if len(expect) >= 2 && expect[:1] == "`" && expect[len(expect)-1:] == "`" {
aaronchen2k2k's avatar
aaronchen2k2k 已提交
134
			expect = expect[1 : len(expect)-1]
aaronchen2k2k's avatar
aaronchen2k2k 已提交
135
			pass = Match(expect, log)
aaronchen2k2k's avatar
aaronchen2k2k 已提交
136
		} else {
aaronchen2k2k's avatar
aaronchen2k2k 已提交
137
			pass = strings.TrimSpace(log) == strings.TrimSpace(expect)
aaronchen2k2k's avatar
aaronchen2k2k 已提交
138 139 140
		}

		if !pass {
aaronchen2k2k's avatar
aaronchen2k2k 已提交
141
			stepResult = commConsts.FAIL
aaronchen2k2k's avatar
aaronchen2k2k 已提交
142 143
		}

aaronchen2k2k's avatar
aaronchen2k2k 已提交
144
		cp := commDomain.CheckPointLog{Numb: idx + 1, Status: stepResult, Expect: expect, Actual: log}
aaronchen2k2k's avatar
aaronchen2k2k 已提交
145 146
		checkpointLogs = append(checkpointLogs, cp)

aaronchen2k2k's avatar
aaronchen2k2k 已提交
147
		idx++
aaronchen2k2k's avatar
aaronchen2k2k 已提交
148 149 150 151 152 153
	}

	return stepResult, checkpointLogs

}

aaronchen2k2k's avatar
aaronchen2k2k 已提交
154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201
func MatchScene(expect, actual, langType string) (pass bool) {
	expect = strings.TrimSpace(expect)
	actual = strings.TrimSpace(actual)

	if len(expect) == 0 {
		pass = actual == ""
		return
	}

	if len(expect) <= 2 {
		return
	}

	// len(expect) > 2
	scene := expect[:2]
	expect = strings.TrimSpace(expect[2:])

	switch scene {
	case "f:":
		return Contain(expect, actual, langType)

	case "m:":
		return Match(expect, actual)

	case "c:":
		return Compare(expect, actual)

	case "l:":
		return Logic(expect, actual, langType)
	}

	return
}

func Contain(expect, actual string, langType string) bool {
	if strings.Contains(expect, "*") {
		expectArr := strings.Split(expect, "*")
		repeatCount, _ := strconv.Atoi(string(expectArr[1]))
		return strings.Count(actual, expectArr[0]) >= repeatCount
	}
	if expect[0:1] == "(" && expect[len(expect)-1:] == ")" && strings.Contains(expect, ",") {
		expect = fmt.Sprintf("^%s{1}$", strings.ReplaceAll(expect, ",", "|"))
	}

	return Match(expect, actual)
}

func Match(expect string, actual string) bool {
aaronchen2k2k's avatar
aaronchen2k2k 已提交
202 203 204 205 206 207 208 209 210 211 212 213 214
	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", `(0X|0x)?[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
}
Z
zhaoke 已提交
215

aaronchen2k2k's avatar
aaronchen2k2k 已提交
216
func Compare(expect string, actual string) bool {
Z
zhaoke 已提交
217
	if len(expect) > 2 && stringUtils.FindInArr(expect[:2], []string{">=", "<=", "<>", "!="}) {
aaronchen2k2k's avatar
aaronchen2k2k 已提交
218
		character := expect[:2]
Z
zhaoke 已提交
219
		expectFloat, err := strconv.ParseFloat(strings.TrimSpace(expect[2:]), 64)
aaronchen2k2k's avatar
aaronchen2k2k 已提交
220 221 222
		if err != nil {
			return false
		}
Z
zhaoke 已提交
223
		actualFloat, err := strconv.ParseFloat(strings.TrimSpace(actual), 64)
aaronchen2k2k's avatar
aaronchen2k2k 已提交
224 225 226 227
		if err != nil {
			return false
		}

Z
zhaoke 已提交
228 229 230
		compareResult := CompareFloat(actualFloat, expectFloat, character)

		return compareResult
Z
zhaoke 已提交
231 232
	}

aaronchen2k2k's avatar
aaronchen2k2k 已提交
233
	if strings.Contains(expect, "-") && strings.Count(expect, "-") == 1 {
Z
zhaoke 已提交
234
		return CompareRange(expect, actual)
Z
zhaoke 已提交
235 236
	}

aaronchen2k2k's avatar
aaronchen2k2k 已提交
237
	character := expect[:1]
Z
zhaoke 已提交
238
	expectFloat, err := strconv.ParseFloat(strings.TrimSpace(expect[1:]), 64)
aaronchen2k2k's avatar
aaronchen2k2k 已提交
239 240 241
	if err != nil {
		return false
	}
Z
zhaoke 已提交
242
	actualFloat, err := strconv.ParseFloat(strings.TrimSpace(actual), 64)
aaronchen2k2k's avatar
aaronchen2k2k 已提交
243 244 245
	if err != nil {
		return false
	}
Z
zhaoke 已提交
246

aaronchen2k2k's avatar
aaronchen2k2k 已提交
247 248
	switch character {
	case ">":
Z
zhaoke 已提交
249
		return actualFloat > expectFloat
aaronchen2k2k's avatar
aaronchen2k2k 已提交
250
	case "<":
Z
zhaoke 已提交
251
		return actualFloat < expectFloat
aaronchen2k2k's avatar
aaronchen2k2k 已提交
252
	case "=":
Z
zhaoke 已提交
253
		return actualFloat == expectFloat
aaronchen2k2k's avatar
aaronchen2k2k 已提交
254
	}
Z
zhaoke 已提交
255

aaronchen2k2k's avatar
aaronchen2k2k 已提交
256 257 258 259
	if strings.Contains(expect, "-") {
		expectArr := strings.Split(expect, "-")
		expectMin, _ := strconv.ParseFloat(strings.TrimSpace(expectArr[0]), 64)
		expectMax, _ := strconv.ParseFloat(strings.TrimSpace(expectArr[1]), 64)
Z
zhaoke 已提交
260
		return actualFloat >= expectMin && actualFloat <= expectMax
aaronchen2k2k's avatar
aaronchen2k2k 已提交
261 262 263 264 265
	}

	return false
}

Z
zhaoke 已提交
266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306
func CompareFloat(actualFloat, expectFloat float64, symbol string) bool {
	switch symbol {
	case ">=":
		return actualFloat >= expectFloat
	case "<=":
		return actualFloat <= expectFloat
	case "<>":
		return actualFloat != expectFloat
	case "!=":
		return actualFloat != expectFloat
	case ">":
		return actualFloat > expectFloat
	case "<":
		return actualFloat < expectFloat
	case "=":
		return actualFloat == expectFloat
	}

	return false
}

func CompareRange(expect string, actual string) bool {
	rangeArr := strings.Split(expect, "-")
	rangeFrom, err := strconv.ParseFloat(strings.TrimSpace(rangeArr[0]), 64)
	if err != nil {
		return false
	}

	rangeTo, err := strconv.ParseFloat(strings.TrimSpace(rangeArr[1]), 64)
	if err != nil {
		return false
	}

	actualFloat, err := strconv.ParseFloat(strings.TrimSpace(actual), 64)
	if err != nil {
		return false
	}

	return actualFloat >= rangeFrom && actualFloat <= rangeTo
}

aaronchen2k2k's avatar
aaronchen2k2k 已提交
307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338
func Logic(expect, actual, langType string) bool {
	openParenthesisCount, closeParenthesisCount := 0, 0
	hasLogicCharacter := false
	for index, v := range expect {
		if v == '(' {
			openParenthesisCount++
		} else if v == ')' {
			closeParenthesisCount++
		}
		if v == '&' || v == '|' {
			hasLogicCharacter = true
		}
		if v == '|' && index > 0 && expect[index-1] != '\\' && (openParenthesisCount == closeParenthesisCount) {
			return MatchScene("l:"+expect[:index], actual, langType) || MatchScene("l:"+expect[index+1:], actual, langType)
		} else if v == '&' && index > 0 && string(expect[index-1]) != "\\" && (openParenthesisCount == closeParenthesisCount) {
			return MatchScene("l:"+expect[:index], actual, langType) && MatchScene("l:"+expect[index+1:], actual, langType)
		}
	}
	if expect[:1] == "(" {
		expect = expect[1:]
	}
	if expect[len(expect)-1:] == ")" {
		expect = expect[:len(expect)-1]
	}
	if !hasLogicCharacter {
		if expect[:1] == "!" {
			return !MatchScene(expect[1:], actual, langType)
		}
		return MatchScene(expect, actual, langType)
	} else {
		return MatchScene("l:"+expect, actual, langType)
	}
Z
zhaoke 已提交
339
}
aaronchen2k2k's avatar
aaronchen2k2k 已提交
340

Z
zhaoke 已提交
341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397
func getStepLogs(skip bool, steps []commDomain.ZentaoCaseStep, actualArr [][]string, langType string, errOutput string) (stepLogs []commDomain.StepLog, caseResult commConsts.ResultStatus) {
	stepLogs = make([]commDomain.StepLog, 0)
	caseResult = commConsts.PASS
	noExpects := true

	if skip {
		caseResult = commConsts.SKIP
		return
	}

	stepIdxToCheck := 0
	for index, step := range steps { // iterate by checkpoints
		stepName := strings.TrimSpace(step.Desc)
		expect := strings.TrimSpace(step.Expect)

		if expect == "" {
			continue
		}

		noExpects = false

		expectLines := strings.Split(expect, "\n")
		var actualLines []string
		if len(actualArr) > stepIdxToCheck {
			actualLines = actualArr[stepIdxToCheck]
		}

		stepResult, checkpointLogs := ValidateStepResult(langType, expectLines, actualLines)
		if errOutput != "" && index == 0 && len(checkpointLogs) > 0 {
			checkpointLogs[0].Actual = errOutput
		}
		stepLog := commDomain.StepLog{Id: strconv.Itoa(stepIdxToCheck + 1), Name: stepName, Status: stepResult, CheckPoints: checkpointLogs}
		stepLogs = append(stepLogs, stepLog)
		if stepResult == commConsts.FAIL {
			caseResult = commConsts.FAIL
		}

		stepIdxToCheck++
	}

	if noExpects {
		caseResult = commConsts.SKIP
	}

	return
}

func incrReportNum(caseResult commConsts.ResultStatus, report *commDomain.ZtfReport) {
	if caseResult == commConsts.FAIL {
		report.Fail = report.Fail + 1
	} else if caseResult == commConsts.PASS {
		report.Pass = report.Pass + 1
	} else if caseResult == commConsts.SKIP {
		report.Skip = report.Skip + 1
	}

	report.Total = report.Total + 1
Z
zhaoke 已提交
398
}