diff --git a/README.md b/README.md index 820c644..77a7311 100644 --- a/README.md +++ b/README.md @@ -74,7 +74,7 @@ be overloaded. Which is especially useful when `nil` for `ref` is not an accepta ### Needed config -Compile with at least `nim c --cc:clang -d:useMalloc -t:"-fsanitize=fuzzer,address,undefined" -l:"-fsanitize=fuzzer,address,undefined" -d:nosignalhandler --nomain:on -g`, `--mm:arc|orc` is recommended. +Compile with at least `--cc:clang -d:useMalloc -t:"-fsanitize=fuzzer,address,undefined" -l:"-fsanitize=fuzzer,address,undefined" -d:nosignalhandler --nomain:on -g`, `--mm:arc|orc` is recommended. Sample [nim.cfg](tests/nim.cfg) and [.nimble](https://github.com/planetis-m/fuzz-playground/blob/master/playground.nimble) files diff --git a/drchaos/mutator.nim b/drchaos/mutator.nim index cee313b..5b3ccdb 100644 --- a/drchaos/mutator.nim +++ b/drchaos/mutator.nim @@ -232,7 +232,7 @@ proc mutate*(value: var string; sizeIncreaseHint: int; enforceChanges: bool; r: else: repeatMutate(mutateString(move value, high(int), sizeIncreaseHint, r)) -proc mutate*[S; T: SomeNumber|bool|char](value: var array[S, T]; sizeIncreaseHint: int; +proc mutate*[S; T: ByteSized](value: var array[S, T]; sizeIncreaseHint: int; enforceChanges: bool; r: var Rand) = repeatMutate(mutateArray(value, r)) diff --git a/examples/fuzz_graph.nim b/examples/fuzz_graph.nim index 798e4b6..93e80a0 100644 --- a/examples/fuzz_graph.nim +++ b/examples/fuzz_graph.nim @@ -1,4 +1,5 @@ # This example produces valid graphs, not garbage, without using graph library functions. +import std/[packedsets, deques] when defined(runFuzzTests): const MaxNodes = 8 # User defined, statically limits number of nodes. @@ -21,6 +22,8 @@ type data: T edges: seq[NodeIdx] +proc len*[T](x: Graph[T]): int {.inline.} = x.nodes.len + proc `[]`*[T](x: Graph[T]; idx: Natural): lent T {.inline.} = x.nodes[idx].data proc `[]`*[T](x: var Graph[T]; idx: Natural): var T {.inline.} = x.nodes[idx].data @@ -46,6 +49,23 @@ proc deleteEdge*[T](x: var Graph[T]; `from`, to: Natural) = fromNode.edges.delete(toNodeIdx) x.deleteNode(toNode.int) # sneaky bug +proc breadthFirstSearch[T](x: Graph[T]; `from`: Natural): seq[NodeIdx] = + var queue: Deque[NodeIdx] + queue.addLast(`from`.NodeIdx) + + result = @[`from`.NodeIdx] + var visited: PackedSet[NodeIdx] + visited.incl `from`.NodeIdx + + while queue.len > 0: + let idx = queue.popFirst() + template node: untyped = x.nodes[idx.int] + for toNode in node.edges: + if toNode notin visited: + queue.addLast(toNode) + visited.incl toNode + result.add(toNode) + when defined(runFuzzTests) and isMainModule: import std/random, drchaos/[mutator, common] @@ -60,61 +80,18 @@ when defined(runFuzzTests) and isMainModule: proc mutate(value: var seq[NodeIdx]; sizeIncreaseHint: int; enforceChanges: bool; r: var Rand) = repeatMutateInplace(mutateSeq(value, tmp, MaxEdges, sizeIncreaseHint, r)) - #proc postProcess[T: SomeNumber](x: var seq[Node[T]]; r: var Rand) = - #if x.len >= 8: - #x[0].data = 63 - #x[1].data = 3 - #x[2].data = -56 - #x[3].data = 100 - #x[4].data = -100 - #x[5].data = -78 - #x[6].data = 46 - #x[7].data = 120 + proc postProcess[T: SomeNumber](x: var seq[Node[T]]; r: var Rand) = + for n in x.mitems: + var i = 0 + while i <= n.edges.high: + if n.edges[i].int >= x.len: + delete(n.edges, i) + else: inc i func fuzzTarget(x: Graph[int8]) = when defined(dumpFuzzInput): debugEcho(x) - if x.nodes.len == 8 and - x.nodes[0].data == 63 and - x.nodes[1].data == 3 and - x.nodes[2].data == -56 and - x.nodes[3].data == 100 and - x.nodes[4].data == -100 and - x.nodes[5].data == -78 and - x.nodes[6].data == 46 and - x.nodes[7].data == 120 and - - x.nodes[0].edges.len == 2 and - x.nodes[0].edges[0] == 1.NodeIdx and - x.nodes[0].edges[1] == 2.NodeIdx and - x.nodes[1].edges.len == 2 and - x.nodes[1].edges[0] == 3.NodeIdx and - x.nodes[1].edges[1] == 4.NodeIdx and - x.nodes[2].edges.len == 2 and - x.nodes[2].edges[0] == 5.NodeIdx and - x.nodes[2].edges[1] == 6.NodeIdx and - x.nodes[3].edges.len == 1 and - x.nodes[3].edges[0] == 7.NodeIdx and - x.nodes[4].edges.len == 0 and - x.nodes[5].edges.len == 0 and - x.nodes[6].edges.len == 0 and - x.nodes[7].edges.len == 0: - doAssert false - # Here you could call library functions and check invariants. - # Such as when removing edges, the number of nodes should remain the same. - #var x = x - #let oldLen = x.nodes.len - #x.deleteEdge(1, 2) - #doAssert oldLen == x.nodes.len + if x.len > 0: + let nodesExplored = breadthFirstSearch(x, `from` = 0) + assert nodesExplored[0] == 0.NodeIdx defaultMutator(fuzzTarget) - - #(nodes: @[ - #(data: 63, edges: @[1, 2]), - #(data: 3, edges: @[3, 4]), - #(data: -56, edges: @[5, 6]), - #(data: 100, edges: @[7]), - #(data: -100, edges: @[]), - #(data: -78, edges: @[]), - #(data: 46, edges: @[]), - #(data: 120, edges: @[]) - #]) diff --git a/tests/tarrays3.nim b/tests/tarrays3.nim index 10b4b39..59fd017 100644 --- a/tests/tarrays3.nim +++ b/tests/tarrays3.nim @@ -17,6 +17,6 @@ proc mutate(value: var DiceFace; sizeIncreaseHint: int; enforceChanges: bool; r: repeatMutate(r.sample([df1, df2, df3, df4, df5, df6])) func fuzzTarget(x: array[10, DiceFace]) = - doAssert x != array[10, DiceFace]([0, 32, 2, 4, 8, 4, 32, 8, 16, 2]) + doAssert x != [df1, df6, df2, df3, df4, df3, df6, df4, df5, df2] defaultMutator(fuzzTarget)