Packages

Property-based testing and metamorphic testing combinator library for Gleam

Current section

Files

Jump to
metamon src metamon@transform@dict.erl
Raw

src/metamon@transform@dict.erl

-module(metamon@transform@dict).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/metamon/transform/dict.gleam").
-export([insert/2, remove/1, shuffle_keys/1]).
-if(?OTP_RELEASE >= 27).
-define(MODULEDOC(Str), -moduledoc(Str)).
-define(DOC(Str), -doc(Str)).
-else.
-define(MODULEDOC(Str), -compile([])).
-define(DOC(Str), -compile([])).
-endif.
?MODULEDOC(
" Standard `Transform(Dict(k, v))` constructors used in metamorphic\n"
" relations. The classic use case is asserting that a function is\n"
" invariant under key reordering: `f(shuffle_keys(d)) == f(d)`.\n"
).
-file("src/metamon/transform/dict.gleam", 11).
?DOC(" Insert / overwrite the binding `key -> value`.\n").
-spec insert(HKL, HKM) -> metamon@transform:transform(gleam@dict:dict(HKL, HKM)).
insert(Key, Value) ->
metamon@transform:new(
<<"dict.insert"/utf8>>,
fun(D) -> gleam@dict:insert(D, Key, Value) end
).
-file("src/metamon/transform/dict.gleam", 16).
?DOC(" Delete `key` from the dict if present.\n").
-spec remove(HKQ) -> metamon@transform:transform(gleam@dict:dict(HKQ, any())).
remove(Key) ->
metamon@transform:new(
<<"dict.remove"/utf8>>,
fun(D) -> gleam@dict:delete(D, Key) end
).
-file("src/metamon/transform/dict.gleam", 55).
-spec fold_assign_keys(
list({HLK, HLL}),
metamon@generator@seed:seed(),
integer(),
list({integer(), {HLK, HLL}})
) -> {metamon@generator@seed:seed(), list({integer(), {HLK, HLL}})}.
fold_assign_keys(Pairs, S, Length, Acc) ->
case Pairs of
[] ->
{S, Acc};
[First | Rest] ->
{Key, Next_state} = metamon@generator@seed:next_int_in(
S,
0,
Length * 100
),
fold_assign_keys(Rest, Next_state, Length, [{Key, First} | Acc])
end.
-file("src/metamon/transform/dict.gleam", 74).
-spec count(list(any()), integer()) -> integer().
count(Items, Acc) ->
case Items of
[] ->
Acc;
[_ | Rest] ->
count(Rest, Acc + 1)
end.
-file("src/metamon/transform/dict.gleam", 70).
-spec list_length(list(any())) -> integer().
list_length(Items) ->
count(Items, 0).
-file("src/metamon/transform/dict.gleam", 89).
-spec insert_sorted({integer(), HLW}, list({integer(), HLW})) -> list({integer(),
HLW}).
insert_sorted(Item, Sorted) ->
case Sorted of
[] ->
[Item];
[First | Rest] ->
case erlang:element(1, Item) =< erlang:element(1, First) of
true ->
[Item, First | Rest];
false ->
[First | insert_sorted(Item, Rest)]
end
end.
-file("src/metamon/transform/dict.gleam", 81).
-spec sort_by_first_int(list({integer(), HLT})) -> list({integer(), HLT}).
sort_by_first_int(Items) ->
case Items of
[] ->
[];
[First | Rest] ->
insert_sorted(First, sort_by_first_int(Rest))
end.
-file("src/metamon/transform/dict.gleam", 100).
-spec drop_first_int(list({integer(), HLZ})) -> list(HLZ).
drop_first_int(Items) ->
case Items of
[] ->
[];
[First | Rest] ->
[erlang:element(2, First) | drop_first_int(Rest)]
end.
-file("src/metamon/transform/dict.gleam", 43).
-spec assign_keys_and_sort(
list({HLG, HLH}),
metamon@generator@seed:seed(),
integer()
) -> list({HLG, HLH}).
assign_keys_and_sort(Pairs, S, Length) ->
Folded = fold_assign_keys(Pairs, S, Length, []),
{_, With_keys} = Folded,
_pipe = With_keys,
_pipe@1 = sort_by_first_int(_pipe),
drop_first_int(_pipe@1).
-file("src/metamon/transform/dict.gleam", 31).
-spec reorder_dict(gleam@dict:dict(HLA, HLB), metamon@generator@seed:seed()) -> gleam@dict:dict(HLA, HLB).
reorder_dict(D, S) ->
Pairs = maps:to_list(D),
Length = list_length(Pairs),
case Length =< 1 of
true ->
D;
false ->
Shuffled = assign_keys_and_sort(Pairs, S, Length),
maps:from_list(Shuffled)
end.
-file("src/metamon/transform/dict.gleam", 25).
?DOC(
" Re-insert all entries in a deterministically shuffled order. The\n"
" resulting dict is `==`-equal to the input (since `Dict` ignores\n"
" insertion order), but any consumer that observes traversal order\n"
" will see a different sequence — exactly the property metamorphic\n"
" tests want to catch.\n"
).
-spec shuffle_keys(integer()) -> metamon@transform:transform(gleam@dict:dict(any(), any())).
shuffle_keys(Seed_value) ->
metamon@transform:new(
<<<<"dict.shuffle_keys("/utf8,
(erlang:integer_to_binary(Seed_value))/binary>>/binary,
")"/utf8>>,
fun(D) -> reorder_dict(D, metamon@generator@seed:seed(Seed_value)) end
).