126 lines
2.7 KiB
Go
126 lines
2.7 KiB
Go
package main
|
|
|
|
import (
|
|
"regexp"
|
|
"strings"
|
|
)
|
|
|
|
func matchLine(pattern, actual string) bool {
|
|
if !strings.Contains(pattern, "...") {
|
|
return pattern == actual
|
|
}
|
|
|
|
parts := strings.Split(pattern, "...")
|
|
escaped := make([]string, len(parts))
|
|
for i, p := range parts {
|
|
escaped[i] = regexp.QuoteMeta(p)
|
|
}
|
|
re := regexp.MustCompile("^" + strings.Join(escaped, ".*") + "$")
|
|
return re.MatchString(actual)
|
|
}
|
|
|
|
func matchOutput(expected, actual []string) bool {
|
|
memo := make(map[[2]int]int8) // 0=unknown, 1=true, -1=false
|
|
return doMatch(expected, 0, actual, 0, memo)
|
|
}
|
|
|
|
func doMatch(expected []string, ei int, actual []string, ai int, memo map[[2]int]int8) bool {
|
|
key := [2]int{ei, ai}
|
|
if v := memo[key]; v != 0 {
|
|
return v == 1
|
|
}
|
|
|
|
result := doMatchInner(expected, ei, actual, ai, memo)
|
|
|
|
if result {
|
|
memo[key] = 1
|
|
} else {
|
|
memo[key] = -1
|
|
}
|
|
return result
|
|
}
|
|
|
|
func doMatchInner(expected []string, ei int, actual []string, ai int, memo map[[2]int]int8) bool {
|
|
if ei == len(expected) && ai == len(actual) {
|
|
return true
|
|
}
|
|
if ei == len(expected) {
|
|
return false
|
|
}
|
|
|
|
exp := expected[ei]
|
|
|
|
// Multi-line wildcard
|
|
if exp == "..." {
|
|
for skip := ai; skip <= len(actual); skip++ {
|
|
if doMatch(expected, ei+1, actual, skip, memo) {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
if ai == len(actual) {
|
|
return false
|
|
}
|
|
|
|
if matchLine(exp, actual[ai]) {
|
|
return doMatch(expected, ei+1, actual, ai+1, memo)
|
|
}
|
|
|
|
return false
|
|
}
|
|
|
|
func diff(expected, actual []string) []DiffLine {
|
|
var result []DiffLine
|
|
ei, ai := 0, 0
|
|
|
|
for ei < len(expected) || ai < len(actual) {
|
|
if ei < len(expected) && expected[ei] == "..." {
|
|
nextExp := ""
|
|
hasNext := ei+1 < len(expected)
|
|
if hasNext {
|
|
nextExp = expected[ei+1]
|
|
}
|
|
|
|
if !hasNext {
|
|
result = append(result, DiffLine{Kind: "context", Text: "..."})
|
|
for ai < len(actual) {
|
|
result = append(result, DiffLine{Kind: "context", Text: actual[ai]})
|
|
ai++
|
|
}
|
|
break
|
|
}
|
|
|
|
result = append(result, DiffLine{Kind: "context", Text: "..."})
|
|
ei++
|
|
for ai < len(actual) && !matchLine(nextExp, actual[ai]) {
|
|
result = append(result, DiffLine{Kind: "context", Text: actual[ai]})
|
|
ai++
|
|
}
|
|
continue
|
|
}
|
|
|
|
if ei < len(expected) && ai < len(actual) {
|
|
if matchLine(expected[ei], actual[ai]) {
|
|
result = append(result, DiffLine{Kind: "equal", Text: actual[ai]})
|
|
ei++
|
|
ai++
|
|
} else {
|
|
result = append(result, DiffLine{Kind: "expected", Text: expected[ei]})
|
|
result = append(result, DiffLine{Kind: "actual", Text: actual[ai]})
|
|
ei++
|
|
ai++
|
|
}
|
|
} else if ei < len(expected) {
|
|
result = append(result, DiffLine{Kind: "expected", Text: expected[ei]})
|
|
ei++
|
|
} else {
|
|
result = append(result, DiffLine{Kind: "actual", Text: actual[ai]})
|
|
ai++
|
|
}
|
|
}
|
|
|
|
return result
|
|
}
|