Skip to content

Instantly share code, notes, and snippets.

@ferd
Created June 11, 2011 22:58
Show Gist options
  • Save ferd/1021062 to your computer and use it in GitHub Desktop.
Save ferd/1021062 to your computer and use it in GitHub Desktop.
Demo PropEr suite for rabbitMQ paths
-module(paths).
-include_lib("proper/include/proper.hrl").
%% Just to see a bunch of results without gen_symbolics
%prop_test_output() ->
% numtests(2, ?FORALL(X, expanded_paths(),
% collect(fun print/1,
% X,
% true))).
%
%print(X) ->
% io:format("~p~n", [X]).
%% Checks that the patterns for paths (a.#.*.c) match or do not
%% match a given expanded path.
prop_expands() ->
?FORALL(Paths, expanded_paths(),
lists:all(fun(X) -> X end, %% checks if all true
[equals(?YOUR_FUN(Pattern, Expanded), Expected) ||
{Pattern, Expanded, Expected} <- Paths])).
%%% GENERATORS
%% Gives a list of paths that were expanded, some of them to fail on purpose,
%% some of them not to.
expanded_paths() ->
?LET(P, path(),
begin
B = list_to_binary(P),
?LET({{Expanded1, IsRight1},{Expanded2, IsRight2}},
{wrong(P), right(P)},
[{B, list_to_binary(Expanded1), IsRight1},
{B, list_to_binary(Expanded2), IsRight2}])
end).
%% Tries to make a pattern wrong. Will not always suceed because a pattern
%% like "#" can be anything at all.
%%
%% Returns {Str, ShouldMatchOriginal}.
wrong(Path) ->
?LET(P, Path, wrong(P, true, [])).
%% Will expand the patterns according to the rules so they should always match
%%
%% Returns {Str, ShouldMatchOriginal}.
right(Path) ->
?LET(P, Path, {right1(P), true}).
%% Here's why some patterns will always succeed even if we try to make them
%% wrong. In a given strign S, we could add segments, but some subpatterns
%% would have a chance to fix the problem we created. See a.*.#, which means
%% 'at least two segments' but still matches (albeit wrongly) a.b if we drop
%% a section of the text, replace it by one, or add two of them. It can
%% technically be done, but we would need a strong lookahead for that.
%% This is especially the case of .#., which we will have to simply ignore.
%%
%% Returns {Str, ShouldMatchOriginal}.
wrong([], Bool, Acc) ->
{lists:reverse(Acc), Bool};
wrong("*.#." ++ Rest, Bool, Acc) -> %% the # messes stuff up, can't invalidate
wrong(Rest, Bool, Acc);
wrong("*.#", Bool, Acc) -> %% same as above, end of string
{lists:reverse(Acc), Bool};
wrong("*." ++ Rest, _Bool, Acc) ->
wrong(Rest, false, Acc);
wrong(".*", _Bool, Acc) ->
{lists:reverse(Acc), false};
wrong(".#." ++ Rest, Bool, Acc) -> %% can't make this one wrong
wrong(Rest, Bool, Acc);
wrong("#." ++ Rest, Bool, Acc) -> %% same, start of string
wrong(Rest, Bool, Acc);
wrong(".#", Bool, Acc) -> %% same as above, end of string
{lists:reverse(Acc), Bool};
wrong([Char|Rest], Bool, Acc) when Char =/= $*, Char =/= $# ->
wrong(Rest, Bool, [Char|Acc]).
%% Returns an expanded string according to the rules
right1([]) -> [];
right1("*" ++ Rest) ->
?LET(S, segment(), S++right1(Rest));
right1("#" ++ Rest) ->
?LET(X,
union([
"",
?LET(S, segment(), S),
?LET({A,B}, {segment(), segment()}, A++[$.]++B),
?LET({A,B,C}, {segment(), segment(), segment()}, A++[$.]++B++[$.]++C)
]),
X ++ right1(Rest));
right1([Char|Rest]) ->
[Char|right1(Rest)].
%% Building a basic pattern/path string
path() ->
?LET(Base, ?LAZY(weighted_union([{3,a()}, {1,b()}])),
?LET({H,T}, {union(["*.","#.",""]), union([".*",".#",""])},
H ++ Base ++ T)).
a() ->
?LET({X,Y}, {segment(), ?LAZY(union([b(), markers()]))},
X ++ [$.] ++ Y).
b() ->
?LET({X,Y}, {segment(), ?LAZY(union([b(), c()]))},
X ++ [$.] ++ Y).
c() ->
segment().
segment() ->
?SUCHTHAT(
X,
list(union([choose($a,$z), choose($A,$Z), choose($0,$9)])),
length(X) =/= 0
).
markers() ->
?LET(S, ?LAZY(union([[$#, $., c()], [$*, $., b()]])), lists:flatten(S)).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment