Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement sleep failpoint status/counter for linearizability test cases #37

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions runtime/http.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,13 @@ func (*httpHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
lines[i] = fps[i] + "=" + s
}
w.Write([]byte(strings.Join(lines, "\n") + "\n"))
} else if strings.HasSuffix(key, "/count") {
Copy link
Member

@serathius serathius Nov 21, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

API could be improved to something more RESTy, but this should work for now.

fp := key[:len(key)-len("/count")]
count, err := StatusCount(fp)
if err != nil {
http.Error(w, "failed to GET: "+err.Error(), http.StatusNotFound)
}
w.Write([]byte(count))
} else {
status, err := Status(key)
if err != nil {
Expand Down
19 changes: 19 additions & 0 deletions runtime/runtime.go
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,25 @@ func Status(failpath string) (string, error) {
return t.desc, nil
}

// StatusCount outputs how many times current term was executed
func StatusCount(failpath string) (string, error) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would expect that StatusCount function should return an int instead of string. However that can be fixed in future PRs.

failpointsMu.RLock()
fp := failpoints[failpath]
failpointsMu.RUnlock()
if fp == nil {
return "", ErrNoExist
}
fp.mu.RLock()
t := fp.t
fp.mu.RUnlock()
if t == nil {
return "", ErrDisabled
}
count := fp.t.counter
return fmt.Sprint(count), nil

}

func List() []string {
failpointsMu.RLock()
ret := make([]string, 0, len(failpoints))
Expand Down
3 changes: 3 additions & 0 deletions runtime/terms.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ type terms struct {

// mu protects the state of the terms chain
mu sync.Mutex
// counts executions
counter int
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why add this field?

}

// term is an executable unit of the failpoint terms chain
Expand Down Expand Up @@ -102,6 +104,7 @@ func (t *terms) eval() (interface{}, error) {
defer t.mu.Unlock()
for _, term := range t.chain {
if term.mods.allow() {
t.counter++
return term.do(), nil
}
}
Expand Down
141 changes: 141 additions & 0 deletions runtime/termscounter_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
package runtime_test

import (
"strings"
"testing"
"time"

"go.etcd.io/gofail/runtime"
)

var __fp_ExampleString *runtime.Failpoint = runtime.NewFailpoint("runtime_test", "ExampleString") //nolint:stylecheck

func TestTermsCounter(t *testing.T) {
testcases := []struct {
name string
fp string
desc string
runbefore int
runafter int
want string
}{
{
name: "Terms limit Failpoint",
fp: "runtime_test/ExampleString",
desc: `10*sleep(10)->1*return("abc")`,
runafter: 12,
want: "11",
},
{
name: "Inbetween Enabling Failpoint",
fp: "runtime_test/ExampleString",
desc: `10*sleep(10)->1*return("abc")`,
runbefore: 2,
runafter: 3,
want: "3",
},
{
name: "Before Enabling Failpoint",
fp: "runtime_test/ExampleString",
desc: `10*sleep(10)->1*return("abc")`,
runbefore: 2,
runafter: 0,
want: "0",
},
}

for _, tc := range testcases {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you want to use table tests, please use subtests.

t.Run(tc.name, func(t *testing.T) {

for i := 0; i < tc.runbefore; i++ {
exampleFunc()
time.Sleep(10 * time.Millisecond)
}

err := runtime.Enable(tc.fp, tc.desc)
if err != nil {
t.Fatal(err)
}
defer runtime.Disable(tc.fp)
for i := 0; i < tc.runafter; i++ {
exampleFunc()
time.Sleep(10 * time.Millisecond)
}
count, err := runtime.StatusCount(tc.fp)
if err != nil {
t.Fatal(err)
}
if strings.Compare(tc.want, count) != 0 {
t.Fatal("counter is not properly incremented")
}
})
}
}

func TestResetingCounterOnTerm(t *testing.T) {
testcases := []struct {
name string
fp string
desc string
newdesc string
runbefore int
runafter int
want string
}{
{
name: "Change and Reset Counter",
fp: "runtime_test/ExampleString",
desc: `10*sleep(10)->1*return("abc")`,
newdesc: "sleep(10)",
runbefore: 2,
runafter: 3,
want: "3",
},
}

for _, tc := range testcases {
t.Run(tc.name, func(t *testing.T) {
err := runtime.Enable(tc.fp, tc.desc)
if err != nil {
t.Fatal(err)
}

for i := 0; i < tc.runbefore; i++ {
exampleFunc()
time.Sleep(10 * time.Millisecond)
}
err = runtime.Enable(tc.fp, tc.newdesc)
if err != nil {
t.Fatal(err)
}
defer runtime.Disable(tc.fp)

for i := 0; i < tc.runafter; i++ {
exampleFunc()
time.Sleep(10 * time.Millisecond)
}
count, err := runtime.StatusCount(tc.fp)
if err != nil {
t.Fatal(err)
}
if strings.Compare(tc.want, count) != 0 {
t.Fatal("counter is not properly incremented")
}
})
}

}

func exampleFunc() string {
if vExampleString, __fpErr := __fp_ExampleString.Acquire(); __fpErr == nil { //nolint:stylecheck
defer __fp_ExampleString.Release() //nolint:stylecheck
ExampleString, __fpTypeOK := vExampleString.(string) //nolint:stylecheck
if !__fpTypeOK { //nolint:stylecheck
goto __badTypeExampleString //nolint:stylecheck
}
return ExampleString
__badTypeExampleString: //nolint:stylecheck
__fp_ExampleString.BadType(vExampleString, "string") //nolint:stylecheck
}
return "example"
}