Add memoization to match and fix zero-timeout
This commit is contained in:
parent
6df26b90ac
commit
6b23930908
25
match.go
25
match.go
|
|
@ -20,10 +20,27 @@ func matchLine(pattern, actual string) bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
func matchOutput(expected, actual []string) bool {
|
func matchOutput(expected, actual []string) bool {
|
||||||
return doMatch(expected, 0, actual, 0)
|
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) bool {
|
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) {
|
if ei == len(expected) && ai == len(actual) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
@ -36,7 +53,7 @@ func doMatch(expected []string, ei int, actual []string, ai int) bool {
|
||||||
// Multi-line wildcard
|
// Multi-line wildcard
|
||||||
if exp == "..." {
|
if exp == "..." {
|
||||||
for skip := ai; skip <= len(actual); skip++ {
|
for skip := ai; skip <= len(actual); skip++ {
|
||||||
if doMatch(expected, ei+1, actual, skip) {
|
if doMatch(expected, ei+1, actual, skip, memo) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -48,7 +65,7 @@ func doMatch(expected []string, ei int, actual []string, ai int) bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
if matchLine(exp, actual[ai]) {
|
if matchLine(exp, actual[ai]) {
|
||||||
return doMatch(expected, ei+1, actual, ai+1)
|
return doMatch(expected, ei+1, actual, ai+1, memo)
|
||||||
}
|
}
|
||||||
|
|
||||||
return false
|
return false
|
||||||
|
|
|
||||||
|
|
@ -99,6 +99,19 @@ func TestMatchOutputInlineAndMultiline(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestMatchOutputConsecutiveWildcards(t *testing.T) {
|
||||||
|
// This would hang before memoization (exponential backtracking)
|
||||||
|
expected := []string{"...", "...", "...", "...", "end"}
|
||||||
|
actual := make([]string, 200)
|
||||||
|
for i := range actual {
|
||||||
|
actual[i] = "line"
|
||||||
|
}
|
||||||
|
// "end" is not in actual, so this should return false — quickly.
|
||||||
|
if matchOutput(expected, actual) {
|
||||||
|
t.Error("should not match when final line is missing")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestDiffBasic(t *testing.T) {
|
func TestDiffBasic(t *testing.T) {
|
||||||
d := diff([]string{"hello"}, []string{"world"})
|
d := diff([]string{"hello"}, []string{"world"})
|
||||||
if len(d) != 2 {
|
if len(d) != 2 {
|
||||||
|
|
|
||||||
13
run.go
13
run.go
|
|
@ -186,6 +186,7 @@ func runFile(file ShoutFile, opts RunOptions) FileResult {
|
||||||
stdin.Close()
|
stdin.Close()
|
||||||
|
|
||||||
var output []byte
|
var output []byte
|
||||||
|
if totalTimeout > 0 {
|
||||||
select {
|
select {
|
||||||
case r := <-ch:
|
case r := <-ch:
|
||||||
if r.err != nil {
|
if r.err != nil {
|
||||||
|
|
@ -206,6 +207,18 @@ func runFile(file ShoutFile, opts RunOptions) FileResult {
|
||||||
waited = true
|
waited = true
|
||||||
return FileResult{File: file, TmpDir: tmpDir, Error: "Timeout reading output"}
|
return FileResult{File: file, TmpDir: tmpDir, Error: "Timeout reading output"}
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
r := <-ch
|
||||||
|
if r.err != nil {
|
||||||
|
if cmd.Process != nil {
|
||||||
|
_ = syscall.Kill(-cmd.Process.Pid, syscall.SIGKILL)
|
||||||
|
}
|
||||||
|
_ = cmd.Wait()
|
||||||
|
waited = true
|
||||||
|
return FileResult{File: file, TmpDir: tmpDir, Error: r.err.Error()}
|
||||||
|
}
|
||||||
|
output = r.data
|
||||||
|
}
|
||||||
|
|
||||||
_ = cmd.Wait()
|
_ = cmd.Wait()
|
||||||
waited = true
|
waited = true
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user