Skip to content

Commit

Permalink
fix AlmostAllPermutations strategy and release and sequence for now
Browse files Browse the repository at this point in the history
  • Loading branch information
zimmski committed Nov 30, 2014
1 parent c41a417 commit e3c23f0
Show file tree
Hide file tree
Showing 5 changed files with 209 additions and 57 deletions.
27 changes: 27 additions & 0 deletions fuzz/strategy/allpermutations_test.go
Expand Up @@ -519,6 +519,33 @@ func TestAllPermutationsStrategy(t *testing.T) {
},
)
}
{
// check if the strategy really works as expected
validateTavorAllPermutations(
t,
`
START = +2(?(1)?(2))
`,
[]string{
"",
"1",
"2",
"12",
"1",
"11",
"21",
"121",
"2",
"12",
"22",
"122",
"12",
"112",
"212",
"1212",
},
)
}
}

func validateTavorAllPermutations(t *testing.T, format string, expect []string) {
Expand Down
137 changes: 88 additions & 49 deletions fuzz/strategy/almostallpermutations.go
@@ -1,24 +1,41 @@
package strategy

import (
"github.com/zimmski/container/list/linkedlist"

"github.com/zimmski/tavor/log"
"github.com/zimmski/tavor/rand"
"github.com/zimmski/tavor/token"
)

type almostAllPermutationsLevel struct {
token token.Token
parent token.Token
tokenIndex int
permutation uint
}

func (l almostAllPermutationsLevel) token() token.Token {
if l.tokenIndex == -1 {
return l.parent
}

switch t := l.parent.(type) {
case token.ForwardToken:
return t.Get()
case token.ListToken:
tt, _ := t.Get(l.tokenIndex)

return tt
}

return nil
}

// AlmostAllPermutationsStrategy implements a fuzzing strategy that generates "almost" all possible permutations of a token graph.
// Every iteration of the strategy generates a new permutation. The generation is deterministic. This strategy does not cover all repititional permutations which can be helpful when less permutations are needed but a almost complete permutation coverage is still needed. For example the definition +2(?(1)?(2)) does not result in 16 permutations but instead it results in only 7.
type AlmostAllPermutationsStrategy struct {
root token.Token

resetedLookup map[token.Token]uint
overextended bool
}

// NewAlmostAllPermutationsStrategy returns a new instance of the Almost All Permutations fuzzing strategy
Expand All @@ -27,6 +44,7 @@ func NewAlmostAllPermutationsStrategy(tok token.Token) *AlmostAllPermutationsStr
root: tok,

resetedLookup: make(map[token.Token]uint),
overextended: false,
}

return s
Expand All @@ -40,38 +58,40 @@ func init() {

func (s *AlmostAllPermutationsStrategy) getLevel(root token.Token, fromChildren bool) []almostAllPermutationsLevel {
var level []almostAllPermutationsLevel
var queue = linkedlist.New()

if fromChildren {
switch t := root.(type) {
case token.ForwardToken:
if v := t.Get(); v != nil {
queue.Push(v)
level = append(level, almostAllPermutationsLevel{
parent: root,
tokenIndex: 0,
permutation: 1,
})
}
case token.ListToken:
l := t.Len()

for i := 0; i < l; i++ {
c, _ := t.Get(i)
queue.Push(c)
level = append(level, almostAllPermutationsLevel{
parent: root,
tokenIndex: i,
permutation: 1,
})
}
}
} else {
queue.Push(root)
}

for !queue.Empty() {
v, _ := queue.Shift()
tok, _ := v.(token.Token)

s.setTokenPermutation(tok, 1)

level = append(level, almostAllPermutationsLevel{
token: tok,
parent: root,
tokenIndex: -1,
permutation: 1,
})
}

for _, l := range level {
s.setTokenPermutation(l.token(), 1)
}

return level
}

Expand All @@ -88,6 +108,7 @@ func (s *AlmostAllPermutationsStrategy) Fuzz(r rand.Rand) (chan struct{}, error)
continueFuzzing := make(chan struct{})

s.resetedLookup = make(map[token.Token]uint)
s.overextended = false

go func() {
log.Debug("start almost all permutations routine")
Expand All @@ -104,6 +125,7 @@ func (s *AlmostAllPermutationsStrategy) Fuzz(r rand.Rand) (chan struct{}, error)

token.ResetScope(s.root)
token.ResetResetTokens(s.root)
token.ResetScope(s.root)

log.Debug("done with fuzzing step")

Expand All @@ -125,15 +147,17 @@ func (s *AlmostAllPermutationsStrategy) Fuzz(r rand.Rand) (chan struct{}, error)
func (s *AlmostAllPermutationsStrategy) setTokenPermutation(tok token.Token, permutation uint) {
if per, ok := s.resetedLookup[tok]; ok && per == permutation {
// Permutation already set in this step
} else {
log.Debugf("set %#v(%p) to permutation %d", tok, tok, permutation)

if err := tok.Permutation(permutation); err != nil {
panic(err)
}
return
}

s.resetedLookup[tok] = permutation
log.Debugf("set %#v(%p) to permutation %d of max permutations %d", tok, tok, permutation, tok.Permutations())

if err := tok.Permutation(permutation); err != nil {
panic(err)
}

s.resetedLookup[tok] = permutation
}

func (s *AlmostAllPermutationsStrategy) fuzz(continueFuzzing chan struct{}, level []almostAllPermutationsLevel) bool {
Expand All @@ -144,32 +168,44 @@ func (s *AlmostAllPermutationsStrategy) fuzz(continueFuzzing chan struct{}, leve
STEP:
for {
for i := range level {
if level[i].permutation > level[i].token.Permutations() {
if i <= last {
if level[i].permutation > level[i].token().Permutations() {
if i < last {
log.Debugf("max reached redo everything <= %d and increment next", i)

if level[i].token().Permutations() != 1 {
log.Debug("Let's stay here")

s.overextended = false
}

level[i+1].permutation++
if level[i+1].permutation <= level[i+1].token.Permutations() {
s.setTokenPermutation(level[i+1].token, level[i+1].permutation)
if level[i+1].permutation <= level[i+1].token().Permutations() {
s.setTokenPermutation(level[i+1].token(), level[i+1].permutation)
}
s.getLevel(level[i+1].token, true) // set all children to permutation 1
s.getLevel(level[i+1].token(), true) // set all children to permutation 1
} else {
log.Debug("Overextended our stay, let's get out of here!")

s.overextended = true

break STEP
}

for k := 0; k <= i; k++ {
level[k].permutation = 1
s.setTokenPermutation(level[k].token, 1)
s.getLevel(level[k].token, true) // set all children to permutation 1
s.setTokenPermutation(level[k].token(), 1)
s.getLevel(level[k].token(), true) // set all children to permutation 1
}

continue STEP
}

log.Debugf("permute %d->%#v", i, level[i])

s.setTokenPermutation(level[i].token, level[i].permutation)
s.setTokenPermutation(level[i].token(), level[i].permutation)

if t, ok := level[i].token.(token.OptionalToken); !ok || !t.IsOptional() || level[i].permutation > 1 {
children := s.getLevel(level[i].token, true) // set all children to permutation 1
if t, ok := level[i].token().(token.OptionalToken); !ok || !t.IsOptional() || level[i].permutation > 1 {
children := s.getLevel(level[i].token(), true) // set all children to permutation 1

if len(children) > 0 {
if !s.fuzz(continueFuzzing, children) {
Expand All @@ -183,41 +219,44 @@ STEP:
}
}

if level[0].permutation > level[0].token.Permutations() {
if level[0].permutation > level[0].token().Permutations() {
found := false
for i := 1; i < len(level); i++ {
if level[i].permutation < level[i].token.Permutations() {
if level[i].permutation < level[i].token().Permutations() {
found = true

break
}
}
if !found {
log.Debug("done with fuzzing this level")

break STEP
}
}

token.ResetScope(s.root)
token.ResetResetTokens(s.root)
if !s.overextended {
token.ResetScope(s.root)
token.ResetResetTokens(s.root)
token.ResetScope(s.root)

log.Debug("done with fuzzing step")
log.Debug("done with fuzzing step")

// done with this fuzzing step
continueFuzzing <- struct{}{}
// done with this fuzzing step
continueFuzzing <- struct{}{}

// wait until we are allowed to continue
if _, ok := <-continueFuzzing; !ok {
log.Debug("fuzzing channel closed from outside")
// wait until we are allowed to continue
if _, ok := <-continueFuzzing; !ok {
log.Debug("fuzzing channel closed from outside")

return false
}
return false
}

log.Debug("start fuzzing step")
log.Debug("start fuzzing step")

s.resetedLookup = make(map[token.Token]uint)
s.resetedLookup = make(map[token.Token]uint)
}
}

log.Debug("done with fuzzing this level")

return true
}

0 comments on commit e3c23f0

Please sign in to comment.