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 {
|
||||
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) {
|
||||
return true
|
||||
}
|
||||
|
|
@ -36,7 +53,7 @@ func doMatch(expected []string, ei int, actual []string, ai int) bool {
|
|||
// Multi-line wildcard
|
||||
if exp == "..." {
|
||||
for skip := ai; skip <= len(actual); skip++ {
|
||||
if doMatch(expected, ei+1, actual, skip) {
|
||||
if doMatch(expected, ei+1, actual, skip, memo) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
|
@ -48,7 +65,7 @@ func doMatch(expected []string, ei int, actual []string, ai int) bool {
|
|||
}
|
||||
|
||||
if matchLine(exp, actual[ai]) {
|
||||
return doMatch(expected, ei+1, actual, ai+1)
|
||||
return doMatch(expected, ei+1, actual, ai+1, memo)
|
||||
}
|
||||
|
||||
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) {
|
||||
d := diff([]string{"hello"}, []string{"world"})
|
||||
if len(d) != 2 {
|
||||
|
|
|
|||
33
run.go
33
run.go
|
|
@ -186,8 +186,29 @@ func runFile(file ShoutFile, opts RunOptions) FileResult {
|
|||
stdin.Close()
|
||||
|
||||
var output []byte
|
||||
select {
|
||||
case r := <-ch:
|
||||
if totalTimeout > 0 {
|
||||
select {
|
||||
case 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
|
||||
case <-time.After(totalTimeout):
|
||||
if cmd.Process != nil {
|
||||
_ = syscall.Kill(-cmd.Process.Pid, syscall.SIGKILL)
|
||||
}
|
||||
<-ch // drain the reader goroutine so pipe FDs are released
|
||||
_ = cmd.Wait()
|
||||
waited = true
|
||||
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)
|
||||
|
|
@ -197,14 +218,6 @@ func runFile(file ShoutFile, opts RunOptions) FileResult {
|
|||
return FileResult{File: file, TmpDir: tmpDir, Error: r.err.Error()}
|
||||
}
|
||||
output = r.data
|
||||
case <-time.After(totalTimeout):
|
||||
if cmd.Process != nil {
|
||||
_ = syscall.Kill(-cmd.Process.Pid, syscall.SIGKILL)
|
||||
}
|
||||
<-ch // drain the reader goroutine so pipe FDs are released
|
||||
_ = cmd.Wait()
|
||||
waited = true
|
||||
return FileResult{File: file, TmpDir: tmpDir, Error: "Timeout reading output"}
|
||||
}
|
||||
|
||||
_ = cmd.Wait()
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user