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
|
%% Generic Types
|
||||||
-export([
|
-export([
|
||||||
scaled/2
|
scaled/2,
|
||||||
|
fixedmap/1
|
||||||
]).
|
]).
|
||||||
|
|
||||||
%% Iterators
|
%% Iterators
|
||||||
-export([nof/1]).
|
-export([nof/1]).
|
||||||
|
|
||||||
|
%% Utilities
|
||||||
|
-export([
|
||||||
|
generate/2,
|
||||||
|
typegen/0
|
||||||
|
]).
|
||||||
|
|
||||||
-type proptype() :: proper_types:raw_type().
|
-type proptype() :: proper_types:raw_type().
|
||||||
|
|
||||||
%%--------------------------------------------------------------------
|
%%--------------------------------------------------------------------
|
||||||
|
@ -679,6 +686,36 @@ limited_list(N, T) ->
|
||||||
scaled(F, T) when F > 0 ->
|
scaled(F, T) when F > 0 ->
|
||||||
?SIZED(S, resize(round(S * F), T)).
|
?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
|
%% Internal funcs
|
||||||
%%--------------------------------------------------------------------
|
%%--------------------------------------------------------------------
|
||||||
|
|
|
@ -18,80 +18,92 @@
|
||||||
|
|
||||||
-include_lib("proper/include/proper.hrl").
|
-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).
|
-define(COVERAGE_TIMEOUT, 5000).
|
||||||
|
|
||||||
prop_coverage() ->
|
prop_coverage() ->
|
||||||
?FORALL(
|
?FORALL(
|
||||||
{Filesize, Segsizes},
|
#{filesize := Filesize, segsizes := Segsizes, typegen := TypeGen},
|
||||||
{filesize_t(), segsizes_t()},
|
noshrink(
|
||||||
?FORALL(
|
fixedmap(#{
|
||||||
Fragments,
|
filesize => filesize_t(),
|
||||||
noshrink(segments_t(Filesize, Segsizes)),
|
segsizes => segsizes_t(),
|
||||||
?TIMEOUT(
|
typegen => typegen()
|
||||||
?COVERAGE_TIMEOUT,
|
})
|
||||||
begin
|
),
|
||||||
ASM1 = append_segments(mk_assembly(Filesize), Fragments),
|
?TIMEOUT(
|
||||||
{Time, ASM2} = timer:tc(emqx_ft_assembly, update, [ASM1]),
|
?COVERAGE_TIMEOUT,
|
||||||
measure(
|
begin
|
||||||
#{"Fragments" => length(Fragments), "Time" => Time},
|
Segments = generate(segments_t(Filesize, Segsizes), TypeGen),
|
||||||
case emqx_ft_assembly:status(ASM2) of
|
ASM1 = append_segments(mk_assembly(Filesize), Segments),
|
||||||
complete ->
|
{Time, ASM2} = timer:tc(emqx_ft_assembly, update, [ASM1]),
|
||||||
Coverage = emqx_ft_assembly:coverage(ASM2),
|
measure(
|
||||||
measure(
|
#{"Segments" => length(Segments), "Time" => Time},
|
||||||
#{"CoverageLength" => length(Coverage)},
|
case emqx_ft_assembly:status(ASM2) of
|
||||||
is_coverage_complete(Coverage)
|
complete ->
|
||||||
);
|
Coverage = emqx_ft_assembly:coverage(ASM2),
|
||||||
{incomplete, {missing, {segment, _, _}}} ->
|
measure(
|
||||||
measure("CoverageLength", 0, true)
|
#{"CoverageLength" => length(Coverage)},
|
||||||
end
|
is_coverage_complete(Coverage)
|
||||||
)
|
);
|
||||||
end
|
{incomplete, {missing, {segment, _, _}}} ->
|
||||||
)
|
measure("CoverageLength", 0, true)
|
||||||
|
end
|
||||||
|
)
|
||||||
|
end
|
||||||
)
|
)
|
||||||
).
|
).
|
||||||
|
|
||||||
prop_coverage_likely_incomplete() ->
|
prop_coverage_likely_incomplete() ->
|
||||||
?FORALL(
|
?FORALL(
|
||||||
{Filesize, Segsizes, Hole},
|
#{filesize := Filesize, segsizes := Segsizes, hole := HoleIn, typegen := TypeGen},
|
||||||
{filesize_t(), segsizes_t(), filesize_t()},
|
noshrink(
|
||||||
?FORALL(
|
fixedmap(#{
|
||||||
Fragments,
|
filesize => filesize_t(),
|
||||||
noshrink(segments_t(Filesize, Segsizes, (Hole rem max(Filesize, 1)))),
|
segsizes => segsizes_t(),
|
||||||
?TIMEOUT(
|
hole => filesize_t(),
|
||||||
?COVERAGE_TIMEOUT,
|
typegen => typegen()
|
||||||
begin
|
})
|
||||||
ASM1 = append_segments(mk_assembly(Filesize), Fragments),
|
),
|
||||||
{Time, ASM2} = timer:tc(emqx_ft_assembly, update, [ASM1]),
|
?TIMEOUT(?COVERAGE_TIMEOUT, begin
|
||||||
measure(
|
Hole = HoleIn rem max(Filesize, 1),
|
||||||
#{"Fragments" => length(Fragments), "Time" => Time},
|
Segments = generate(segments_t(Filesize, Segsizes, Hole), TypeGen),
|
||||||
case emqx_ft_assembly:status(ASM2) of
|
ASM1 = append_segments(mk_assembly(Filesize), Segments),
|
||||||
complete ->
|
{Time, ASM2} = timer:tc(emqx_ft_assembly, update, [ASM1]),
|
||||||
% NOTE: this is still possible due to the nature of `SUCHTHATMAYBE`
|
measure(
|
||||||
IsComplete = emqx_ft_assembly:coverage(ASM2),
|
#{"Segments" => length(Segments), "Time" => Time},
|
||||||
collect(complete, is_coverage_complete(IsComplete));
|
case emqx_ft_assembly:status(ASM2) of
|
||||||
{incomplete, {missing, {segment, _, _}}} ->
|
complete ->
|
||||||
collect(incomplete, true)
|
% NOTE: this is still possible due to the nature of `SUCHTHATMAYBE`
|
||||||
end
|
IsComplete = emqx_ft_assembly:coverage(ASM2),
|
||||||
)
|
collect(complete, is_coverage_complete(IsComplete));
|
||||||
|
{incomplete, {missing, {segment, _, _}}} ->
|
||||||
|
collect(incomplete, true)
|
||||||
end
|
end
|
||||||
)
|
)
|
||||||
)
|
end)
|
||||||
).
|
).
|
||||||
|
|
||||||
prop_coverage_complete() ->
|
prop_coverage_complete() ->
|
||||||
?FORALL(
|
?FORALL(
|
||||||
{Filesize, Segsizes},
|
#{filesize := Filesize, segsizes := Segsizes, node := RemoteNode, typegen := TypeGen},
|
||||||
{filesize_t(), ?SUCHTHAT([BaseSegsize | _], segsizes_t(), BaseSegsize > 0)},
|
noshrink(
|
||||||
?FORALL(
|
fixedmap(#{
|
||||||
{Fragments, RemoteNode},
|
filesize => filesize_t(),
|
||||||
noshrink({segments_t(Filesize, Segsizes), remote_node_t()}),
|
segsizes => ?SUCHTHAT([BaseSegsize | _], segsizes_t(), BaseSegsize > 0),
|
||||||
|
node => remote_node_t(),
|
||||||
|
typegen => typegen()
|
||||||
|
})
|
||||||
|
),
|
||||||
|
?TIMEOUT(
|
||||||
|
?COVERAGE_TIMEOUT,
|
||||||
begin
|
begin
|
||||||
% Ensure that we have complete coverage
|
% Ensure that we have complete coverage
|
||||||
|
Segments = generate(segments_t(Filesize, Segsizes), TypeGen),
|
||||||
ASM1 = mk_assembly(Filesize),
|
ASM1 = mk_assembly(Filesize),
|
||||||
ASM2 = append_coverage(ASM1, RemoteNode, Filesize, Segsizes),
|
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]),
|
{Time, ASM4} = timer:tc(emqx_ft_assembly, update, [ASM3]),
|
||||||
measure(
|
measure(
|
||||||
#{"CoverageMax" => nsegs(Filesize, Segsizes), "Time" => Time},
|
#{"CoverageMax" => nsegs(Filesize, Segsizes), "Time" => Time},
|
||||||
|
|
Loading…
Reference in New Issue