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 { return doMatch(expected, 0, actual, 0) } func doMatch(expected []string, ei int, actual []string, ai int) 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) { return true } } return false } if ai == len(actual) { return false } if matchLine(exp, actual[ai]) { return doMatch(expected, ei+1, actual, ai+1) } 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 }