test(ft): generate segments manually in proptests
In order to avoid spamming proptest logs with HUGE type instances, and make proptests themselves simpler. Proper now should refuse shrinking altogether, instances that cause prop function to timeout do not shrink well for some reason, making proptests stuck.
This commit is contained in:
parent
38bd8a8185
commit
ff327609db
|
@ -52,12 +52,19 @@
|
|||
|
||||
%% Generic Types
|
||||
-export([
|
||||
scaled/2
|
||||
scaled/2,
|
||||
fixedmap/1
|
||||
]).
|
||||
|
||||
%% Iterators
|
||||
-export([nof/1]).
|
||||
|
||||
%% Utilities
|
||||
-export([
|
||||
generate/2,
|
||||
typegen/0
|
||||
]).
|
||||
|
||||
-type proptype() :: proper_types:raw_type().
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
|
@ -679,6 +686,36 @@ limited_list(N, T) ->
|
|||
scaled(F, T) when F > 0 ->
|
||||
?SIZED(S, resize(round(S * F), T)).
|
||||
|
||||
-spec fixedmap(#{_Key => proptype()}) -> proptype().
|
||||
fixedmap(M) ->
|
||||
?LET(PList, maps:to_list(M), maps:from_list(PList)).
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% Utilities
|
||||
%%--------------------------------------------------------------------
|
||||
|
||||
-type typegen() :: {typegen, proper_gen:size(), proper_gen:seed()}.
|
||||
|
||||
-spec typegen() -> proptype().
|
||||
typegen() ->
|
||||
Seed = {non_neg_integer(), non_neg_integer(), non_neg_integer()},
|
||||
{typegen, ?SIZED(S, S), Seed}.
|
||||
|
||||
-spec generate(proptype(), typegen()) -> _Instance.
|
||||
generate(T, {typegen, Size, Seed}) ->
|
||||
% NOTE
|
||||
% We need to run it in a separate process so that it won't erase
|
||||
% any proper state in the current process allocated by the property
|
||||
% being evaluated.
|
||||
{Pid, MRef} = erlang:spawn_monitor(
|
||||
fun() -> exit(proper_gen:pick(T, Size, Seed)) end
|
||||
),
|
||||
receive
|
||||
{'DOWN', MRef, process, Pid, Result} ->
|
||||
{ok, Instance} = Result,
|
||||
Instance
|
||||
end.
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% Internal funcs
|
||||
%%--------------------------------------------------------------------
|
||||
|
|
|
@ -18,80 +18,92 @@
|
|||
|
||||
-include_lib("proper/include/proper.hrl").
|
||||
|
||||
-import(emqx_proper_types, [scaled/2]).
|
||||
-import(emqx_proper_types, [scaled/2, fixedmap/1, typegen/0, generate/2]).
|
||||
|
||||
-define(COVERAGE_TIMEOUT, 5000).
|
||||
|
||||
prop_coverage() ->
|
||||
?FORALL(
|
||||
{Filesize, Segsizes},
|
||||
{filesize_t(), segsizes_t()},
|
||||
?FORALL(
|
||||
Fragments,
|
||||
noshrink(segments_t(Filesize, Segsizes)),
|
||||
?TIMEOUT(
|
||||
?COVERAGE_TIMEOUT,
|
||||
begin
|
||||
ASM1 = append_segments(mk_assembly(Filesize), Fragments),
|
||||
{Time, ASM2} = timer:tc(emqx_ft_assembly, update, [ASM1]),
|
||||
measure(
|
||||
#{"Fragments" => length(Fragments), "Time" => Time},
|
||||
case emqx_ft_assembly:status(ASM2) of
|
||||
complete ->
|
||||
Coverage = emqx_ft_assembly:coverage(ASM2),
|
||||
measure(
|
||||
#{"CoverageLength" => length(Coverage)},
|
||||
is_coverage_complete(Coverage)
|
||||
);
|
||||
{incomplete, {missing, {segment, _, _}}} ->
|
||||
measure("CoverageLength", 0, true)
|
||||
end
|
||||
)
|
||||
end
|
||||
)
|
||||
#{filesize := Filesize, segsizes := Segsizes, typegen := TypeGen},
|
||||
noshrink(
|
||||
fixedmap(#{
|
||||
filesize => filesize_t(),
|
||||
segsizes => segsizes_t(),
|
||||
typegen => typegen()
|
||||
})
|
||||
),
|
||||
?TIMEOUT(
|
||||
?COVERAGE_TIMEOUT,
|
||||
begin
|
||||
Segments = generate(segments_t(Filesize, Segsizes), TypeGen),
|
||||
ASM1 = append_segments(mk_assembly(Filesize), Segments),
|
||||
{Time, ASM2} = timer:tc(emqx_ft_assembly, update, [ASM1]),
|
||||
measure(
|
||||
#{"Segments" => length(Segments), "Time" => Time},
|
||||
case emqx_ft_assembly:status(ASM2) of
|
||||
complete ->
|
||||
Coverage = emqx_ft_assembly:coverage(ASM2),
|
||||
measure(
|
||||
#{"CoverageLength" => length(Coverage)},
|
||||
is_coverage_complete(Coverage)
|
||||
);
|
||||
{incomplete, {missing, {segment, _, _}}} ->
|
||||
measure("CoverageLength", 0, true)
|
||||
end
|
||||
)
|
||||
end
|
||||
)
|
||||
).
|
||||
|
||||
prop_coverage_likely_incomplete() ->
|
||||
?FORALL(
|
||||
{Filesize, Segsizes, Hole},
|
||||
{filesize_t(), segsizes_t(), filesize_t()},
|
||||
?FORALL(
|
||||
Fragments,
|
||||
noshrink(segments_t(Filesize, Segsizes, (Hole rem max(Filesize, 1)))),
|
||||
?TIMEOUT(
|
||||
?COVERAGE_TIMEOUT,
|
||||
begin
|
||||
ASM1 = append_segments(mk_assembly(Filesize), Fragments),
|
||||
{Time, ASM2} = timer:tc(emqx_ft_assembly, update, [ASM1]),
|
||||
measure(
|
||||
#{"Fragments" => length(Fragments), "Time" => Time},
|
||||
case emqx_ft_assembly:status(ASM2) of
|
||||
complete ->
|
||||
% NOTE: this is still possible due to the nature of `SUCHTHATMAYBE`
|
||||
IsComplete = emqx_ft_assembly:coverage(ASM2),
|
||||
collect(complete, is_coverage_complete(IsComplete));
|
||||
{incomplete, {missing, {segment, _, _}}} ->
|
||||
collect(incomplete, true)
|
||||
end
|
||||
)
|
||||
#{filesize := Filesize, segsizes := Segsizes, hole := HoleIn, typegen := TypeGen},
|
||||
noshrink(
|
||||
fixedmap(#{
|
||||
filesize => filesize_t(),
|
||||
segsizes => segsizes_t(),
|
||||
hole => filesize_t(),
|
||||
typegen => typegen()
|
||||
})
|
||||
),
|
||||
?TIMEOUT(?COVERAGE_TIMEOUT, begin
|
||||
Hole = HoleIn rem max(Filesize, 1),
|
||||
Segments = generate(segments_t(Filesize, Segsizes, Hole), TypeGen),
|
||||
ASM1 = append_segments(mk_assembly(Filesize), Segments),
|
||||
{Time, ASM2} = timer:tc(emqx_ft_assembly, update, [ASM1]),
|
||||
measure(
|
||||
#{"Segments" => length(Segments), "Time" => Time},
|
||||
case emqx_ft_assembly:status(ASM2) of
|
||||
complete ->
|
||||
% NOTE: this is still possible due to the nature of `SUCHTHATMAYBE`
|
||||
IsComplete = emqx_ft_assembly:coverage(ASM2),
|
||||
collect(complete, is_coverage_complete(IsComplete));
|
||||
{incomplete, {missing, {segment, _, _}}} ->
|
||||
collect(incomplete, true)
|
||||
end
|
||||
)
|
||||
)
|
||||
end)
|
||||
).
|
||||
|
||||
prop_coverage_complete() ->
|
||||
?FORALL(
|
||||
{Filesize, Segsizes},
|
||||
{filesize_t(), ?SUCHTHAT([BaseSegsize | _], segsizes_t(), BaseSegsize > 0)},
|
||||
?FORALL(
|
||||
{Fragments, RemoteNode},
|
||||
noshrink({segments_t(Filesize, Segsizes), remote_node_t()}),
|
||||
#{filesize := Filesize, segsizes := Segsizes, node := RemoteNode, typegen := TypeGen},
|
||||
noshrink(
|
||||
fixedmap(#{
|
||||
filesize => filesize_t(),
|
||||
segsizes => ?SUCHTHAT([BaseSegsize | _], segsizes_t(), BaseSegsize > 0),
|
||||
node => remote_node_t(),
|
||||
typegen => typegen()
|
||||
})
|
||||
),
|
||||
?TIMEOUT(
|
||||
?COVERAGE_TIMEOUT,
|
||||
begin
|
||||
% Ensure that we have complete coverage
|
||||
Segments = generate(segments_t(Filesize, Segsizes), TypeGen),
|
||||
ASM1 = mk_assembly(Filesize),
|
||||
ASM2 = append_coverage(ASM1, RemoteNode, Filesize, Segsizes),
|
||||
ASM3 = append_segments(ASM2, Fragments),
|
||||
ASM3 = append_segments(ASM2, Segments),
|
||||
{Time, ASM4} = timer:tc(emqx_ft_assembly, update, [ASM3]),
|
||||
measure(
|
||||
#{"CoverageMax" => nsegs(Filesize, Segsizes), "Time" => Time},
|
||||
|
|
Loading…
Reference in New Issue