this repo has no description
2
fork

Configure Feed

Select the types of activity you want to include in your feed.

Add BTree model fuzzer

garrison 105778bf 1d9d954e

+125
+97
lib/btree/fuzz/model_fuzz.ex
··· 1 + defmodule Hobbes.BTree.Fuzz.ModelFuzz do 2 + alias Hobbes.BTree 3 + 4 + import Hobbes.FuzzUtils 5 + 6 + defmodule State do 7 + @enforce_keys [ 8 + :btree, 9 + :opts, 10 + ] 11 + defstruct @enforce_keys 12 + end 13 + 14 + def run(seed, opts) do 15 + :rand.seed(:exsss, seed) 16 + 17 + opts = validate_opts(opts, [ 18 + :iterations, 19 + :key_bits, 20 + batch_size_range: 1..100, 21 + btree_opts: [], 22 + seed: seed, 23 + ]) 24 + :ok = fuzz(opts) 25 + 26 + :ok 27 + end 28 + 29 + defp fuzz(opts) do 30 + state = %State{ 31 + btree: BTree.new(opts.btree_opts), 32 + opts: opts, 33 + } 34 + 35 + Enum.reduce(1..opts.iterations, state, fn iteration, state -> 36 + try do 37 + fuzz_iteration(state) 38 + rescue e in [ExUnit.AssertionError] -> 39 + e = Map.update!(e, :message, &(&1 <> " (at seed=#{opts.seed}, iteration=#{inspect(iteration)})")) 40 + reraise e, __STACKTRACE__ 41 + end 42 + end) 43 + :ok 44 + end 45 + 46 + defp fuzz_iteration(%State{} = state) do 47 + op = random_op() 48 + execute(op, state) 49 + end 50 + 51 + defp random_op do 52 + case Enum.random(1..1) do 53 + 1 -> :apply_batch 54 + end 55 + end 56 + 57 + defp execute(:apply_batch, %State{} = state) do 58 + %{ 59 + btree: btree, 60 + opts: opts, 61 + } = state 62 + 63 + mutations = make_batch(opts) 64 + :ok = BTree.Writer.apply_batch(btree, mutations) 65 + 66 + state 67 + end 68 + 69 + defp make_batch(opts) do 70 + opt_key_bits = opts.key_bits 71 + batch_size = Enum.random(opts.batch_size_range) 72 + 73 + do_make_batch(opt_key_bits, [], batch_size) 74 + end 75 + 76 + defp do_make_batch(_opt_key_bits, acc, 0), do: acc 77 + defp do_make_batch(opt_key_bits, acc, count) do 78 + acc = [make_mutation(opt_key_bits) | acc] 79 + count = count - 1 80 + do_make_batch(opt_key_bits, acc, count) 81 + end 82 + 83 + defp make_mutation(opt_key_bits) do 84 + case Enum.random(1..1) do 85 + 1 -> 86 + key = make_key(opt_key_bits) 87 + value = String.duplicate(String.reverse(key), 2) 88 + {:write, key, value} 89 + end 90 + end 91 + 92 + defp make_key(bits) do 93 + :rand.uniform((2 ** bits) - 1) 94 + |> :binary.encode_unsigned() 95 + |> Base.encode16(case: :lower) 96 + end 97 + end
+14
lib/fuzz_utils.ex
··· 1 + defmodule Hobbes.FuzzUtils do 2 + @spec validate_opts(keyword, [atom | {atom, term}]) :: map 3 + def validate_opts(opts, expected) do 4 + Map.new(expected, fn 5 + {key, default} -> 6 + {key, Keyword.get(opts, key, default)} 7 + key -> 8 + case Keyword.fetch(opts, key) do 9 + {:ok, value} -> {key, value} 10 + :error -> raise "Key #{inspect(key)} was not found in #{inspect(opts)}" 11 + end 12 + end) 13 + end 14 + end
+14
test/btree_test.exs
··· 5 5 6 6 @moduletag :btree 7 7 8 + defmodule ModelFuzzTest do 9 + use ExUnit.Case, async: true 10 + @moduletag :btree 11 + @moduletag :btree_fuzz 12 + @moduletag :btree_model 13 + 14 + test "fuzz" do 15 + Hobbes.BTree.Fuzz.ModelFuzz.run(100, [ 16 + iterations: 100, 17 + key_bits: 32, 18 + ]) 19 + end 20 + end 21 + 8 22 describe "BTree" do 9 23 test "insert" do 10 24 btree = BTree.new()