Introduce optional weight function for commands and other improvements (#1618)

* Generator erlang-proper

Used to generate PropEr generators for property-based testing

* Rearrange *_statem.erl functions and remove *_statem.hrl

* Optional weight function

* Optional argument to override generated values

* Export function with arity 1

* Move syntactic sugar to header file

* Only provide body when its allowed

* The weight function should not depend on the state
This commit is contained in:
Juan Facorro 2018-12-10 05:41:12 +02:00 committed by William Cheng
parent 5b91d40a8a
commit 87bc9d436f
17 changed files with 332 additions and 314 deletions

View File

@ -18,13 +18,20 @@
Method = {{httpMethod}}, Method = {{httpMethod}},
Host = application:get_env({{packageName}}, host, "http://localhost:8080"), Host = application:get_env({{packageName}}, host, "http://localhost:8080"),
Path = ["{{{replacedPathName}}}"], Path = ["{{{replacedPathName}}}"],
{{#isBodyAllowed}}
Body = {{^formParams.isEmpty}}{form, [{{#formParams}}{{#required}}{{^-first}}, {{/-first}}{<<"{{{baseName}}}">>, {{paramName}}{{/required}}{{/formParams}}]++{{packageName}}_utils:optional_params([{{#formParams}}{{^required}}{{^-first}}, {{/-first}}'{{{baseName}}}'{{/required}}{{/formParams}}], _OptionalParams)}{{/formParams.isEmpty}}{{#formParams.isEmpty}}{{#bodyParams.isEmpty}}[]{{/bodyParams.isEmpty}}{{^bodyParams.isEmpty}}{{#bodyParams}}{{paramName}}{{/bodyParams}}{{/bodyParams.isEmpty}}{{/formParams.isEmpty}}, Body = {{^formParams.isEmpty}}{form, [{{#formParams}}{{#required}}{{^-first}}, {{/-first}}{<<"{{{baseName}}}">>, {{paramName}}{{/required}}{{/formParams}}]++{{packageName}}_utils:optional_params([{{#formParams}}{{^required}}{{^-first}}, {{/-first}}'{{{baseName}}}'{{/required}}{{/formParams}}], _OptionalParams)}{{/formParams.isEmpty}}{{#formParams.isEmpty}}{{#bodyParams.isEmpty}}[]{{/bodyParams.isEmpty}}{{^bodyParams.isEmpty}}{{#bodyParams}}{{paramName}}{{/bodyParams}}{{/bodyParams.isEmpty}}{{/formParams.isEmpty}},
ContentType = {{#hasConsumes}}hd([{{#consumes}}{{^-first}}, {{/-first}}"{{mediaType}}"{{/consumes}}]){{/hasConsumes}}{{^hasConsumes}}<<"text/plain">>{{/hasConsumes}}, ContentType = {{#hasConsumes}}hd([{{#consumes}}{{^-first}}, {{/-first}}"{{mediaType}}"{{/consumes}}]){{/hasConsumes}}{{^hasConsumes}}"text/plain"{{/hasConsumes}},
{{/isBodyAllowed}}
{{^queryParams.isEmpty}} {{^queryParams.isEmpty}}
QueryString = [{{#queryParams}}{{^-first}}, {{/-first}}<<"{{{baseName}}}=">>, {{{paramName}}}, <<"&">>{{/queryParams}}], QueryString = [{{#queryParams}}{{^-first}}, {{/-first}}<<"{{{baseName}}}=">>, {{{paramName}}}, <<"&">>{{/queryParams}}],
{{/queryParams.isEmpty}} {{/queryParams.isEmpty}}
{{#isBodyAllowed}}
{{packageName}}_utils:request(Method, [Host, ?BASE_URL, Path{{^queryParams.isEmpty}}, <<"?">>, QueryString{{/queryParams.isEmpty}}], jsx:encode(Body), ContentType). {{packageName}}_utils:request(Method, [Host, ?BASE_URL, Path{{^queryParams.isEmpty}}, <<"?">>, QueryString{{/queryParams.isEmpty}}], jsx:encode(Body), ContentType).
{{/isBodyAllowed}}
{{^isBodyAllowed}}
{{packageName}}_utils:request(Method, [Host, ?BASE_URL, Path{{^queryParams.isEmpty}}, <<"?">>, QueryString{{/queryParams.isEmpty}}]).
{{/isBodyAllowed}}
{{/operation}} {{/operation}}
{{/operations}} {{/operations}}

View File

@ -5,7 +5,9 @@
-include("{{packageName}}.hrl"). -include("{{packageName}}.hrl").
-export([{{classname}}/0]). -export([{{classname}}/0]).
{{^isEnum}}{{^isArrayModel}}
-export([{{classname}}/1]).
{{/isArrayModel}}{{/isEnum}}
-export_type([{{classname}}/0]). -export_type([{{classname}}/0]).
-type {{classname}}() ::{{#isEnum}} -type {{classname}}() ::{{#isEnum}}
@ -15,12 +17,23 @@
| {{/-first}}{'{{name}}', {{dataType}} }{{/vars}} | {{/-first}}{'{{name}}', {{dataType}} }{{/vars}}
].{{/isArrayModel}}{{/isEnum}} ].{{/isArrayModel}}{{/isEnum}}
{{classname}}() ->{{#isEnum}} {{#isEnum}}
{{classname}}() ->
elements([{{#allowableValues.values}}{{^-first}}, {{/-first}}<<"{{.}}">>{{/allowableValues.values}}]). elements([{{#allowableValues.values}}{{^-first}}, {{/-first}}<<"{{.}}">>{{/allowableValues.values}}]).
{{/isEnum}}{{#isArrayModel}} {{/isEnum}}
list({{arrayModelType}}{{#minItems}}, {{minItems}}{{#maxItems}}, {{maxItems}}{{/maxItems}}{{/minItems}}).{{/isArrayModel}}{{^isEnum}}{{^isArrayModel}} {{#isArrayModel}}
[ {{#vars}}{{^-first}} {{classname}}() ->
, {{/-first}}{'{{baseName}}', {{#isString}}{{#isEnum}}elements([{{#allowableValues.values}}{{^-first}}, {{/-first}}<<"{{.}}">>{{/allowableValues.values}}]){{/isEnum}}{{^isEnum}}binary({{#minLength}}{{minLength}}{{#maxLength}}, {{maxLength}}{{/maxLength}}{{/minLength}}){{/isEnum}}{{/isString}}{{^isString}}{{baseType}}{{/isString}} }{{/vars}} list({{arrayModelType}}{{#minItems}}, {{minItems}}{{#maxItems}}, {{maxItems}}{{/maxItems}}{{/minItems}}).
].{{/isArrayModel}}{{/isEnum}} {{/isArrayModel}}
{{^isEnum}}{{^isArrayModel}}
{{classname}}() ->
{{classname}}([]).
{{classname}}(Fields) ->
Default = [ {{#vars}}{{^-first}}
, {{/-first}}{'{{baseName}}', {{#isString}}{{#isEnum}}elements([{{#allowableValues.values}}{{^-first}}, {{/-first}}<<"{{.}}">>{{/allowableValues.values}}]){{/isEnum}}{{^isEnum}}binary({{#minLength}}{{minLength}}{{#maxLength}}, {{maxLength}}{{/maxLength}}{{/minLength}}){{/isEnum}}{{/isString}}{{^isString}}{{baseType}}{{/isString}} }{{/vars}}
],
lists:ukeymerge(1, lists:sort(Fields), lists:sort(Default)).
{{/isArrayModel}}{{/isEnum}}
{{/model}} {{/model}}
{{/models}} {{/models}}

View File

@ -4,4 +4,4 @@
{shell, [{apps, [{{packageName}}]}]}. {shell, [{apps, [{{packageName}}]}]}.
{plugins, [rebar3_proper]}. {plugins, [rebar3_proper]}.

View File

@ -1,25 +1,49 @@
%%============================================================================== %%==============================================================================
%% Setup %% Syntactic sugar
%%============================================================================== %%==============================================================================
setup() -> ok. command(State) ->
Funs0 = [ {F, list_to_atom(atom_to_list(F) ++ "_args")}
|| {F, _} <- ?MODULE:module_info(exports)
],
%%============================================================================== Funs1 = [ X || {_, FArgs} = X <- Funs0,
%% Cleanup erlang:function_exported(?MODULE, FArgs, 1)
%%============================================================================== ],
cleanup() -> ok. WeightFun = case erlang:function_exported(?MODULE, weight, 1) of
true -> fun ?MODULE:weight/1;
false -> fun(_) -> 1 end
end,
%%============================================================================== proper_types:frequency([ { WeightFun(F)
%% Initial State , {call, ?MODULE, F, ?MODULE:FArgs(State)}
%%============================================================================== }
|| {F, FArgs} <- Funs1
]).
initial_state() -> #{}. precondition(S, {call, M, F, Args}) ->
Pre = list_to_atom(atom_to_list(F) ++ "_pre"),
case erlang:function_exported(M, Pre, 1) of
true -> M:Pre(S);
false -> true
end
andalso
case erlang:function_exported(M, Pre, 2) of
true -> M:Pre(S, Args);
false -> true
end.
%%============================================================================== next_state(S, Res, {call, M, F, Args}) ->
%% State transitions callbacks Next = list_to_atom(atom_to_list(F) ++ "_next"),
%% case erlang:function_exported(M, Next, 3) of
%% operation_pre(State) -> true. true -> M:Next(S, Res, Args);
%% operation_next(State, Result, Args) -> State. false -> S
%% operation_post(State, Args, Result) -> true. end.
%%==============================================================================
postcondition(S, {call, M, F, Args}, Res) ->
Post = list_to_atom(atom_to_list(F) ++ "_post"),
case erlang:function_exported(M, Post, 3) of
true -> M:Post(S, Args, Res);
false -> true
end.

View File

@ -9,66 +9,7 @@
-compile(export_all). -compile(export_all).
-compile(nowarn_export_all). -compile(nowarn_export_all).
%%============================================================================== -include("{{packageName}}_statem.hrl").
%% PropEr callbacks
%%==============================================================================
command(State) ->
Funs0 = [ {F, list_to_atom(atom_to_list(F) ++ "_args")}
|| {F, _} <- ?MODULE:module_info(exports)
],
Funs1 = [ X || {_, FArgs} = X <- Funs0,
erlang:function_exported(?MODULE, FArgs, 1)
],
proper_types:oneof([ {call, ?MODULE, F, ?MODULE:FArgs(State)}
|| {F, FArgs} <- Funs1
]).
precondition(S, {call, M, F, Args}) ->
Pre = list_to_atom(atom_to_list(F) ++ "_pre"),
case erlang:function_exported(M, Pre, 1) of
true -> M:Pre(S);
false -> true
end
andalso
case erlang:function_exported(M, Pre, 2) of
true -> M:Pre(S, Args);
false -> true
end.
next_state(S, Res, {call, M, F, Args}) ->
Next = list_to_atom(atom_to_list(F) ++ "_next"),
case erlang:function_exported(M, Next, 3) of
true -> M:Next(S, Res, Args);
false -> S
end.
postcondition(S, {call, M, F, Args}, Res) ->
Post = list_to_atom(atom_to_list(F) ++ "_post"),
case erlang:function_exported(M, Post, 3) of
true -> M:Post(S, Args, Res);
false -> true
end.
{{#operations}}
{{#operation}}
%%==============================================================================
%% {{operationId}}
%%==============================================================================
{{operationId}}({{#allParams}}{{#required}}{{^-first}}, {{/-first}}{{paramName}}{{/required}}{{/allParams}}) ->
{{classname}}_api:{{operationId}}({{#allParams}}{{#required}}{{^-first}}, {{/-first}}{{paramName}}{{/required}}{{/allParams}}).
{{operationId}}_args(S) ->
Args = [{{#allParams}}{{#required}}{{^-first}}, {{/-first}}{{dataType}}{{/required}}{{/allParams}}],
case erlang:function_exported(?MODULE, '{{operationId}}_args_custom', 2) of
true -> ?MODULE:{{operationId}}_args_custom(S, Args);
false -> Args
end.
{{/operation}}
{{/operations}}
%%============================================================================== %%==============================================================================
%% The statem's property %% The statem's property
@ -99,7 +40,34 @@ prop_main() ->
). ).
%%============================================================================== %%==============================================================================
%% Include file with setup, cleanup, initial_state %% Setup
%% and state transitions callbacks
%%============================================================================== %%==============================================================================
-include("{{classname}}_statem.hrl").
setup() -> ok.
%%==============================================================================
%% Cleanup
%%==============================================================================
cleanup() -> ok.
%%==============================================================================
%% Initial State
%%==============================================================================
initial_state() -> #{}.
{{#operations}}
{{#operation}}
%%==============================================================================
%% {{operationId}}
%%==============================================================================
{{operationId}}({{#allParams}}{{#required}}{{^-first}}, {{/-first}}{{paramName}}{{/required}}{{/allParams}}) ->
{{classname}}_api:{{operationId}}({{#allParams}}{{#required}}{{^-first}}, {{/-first}}{{paramName}}{{/required}}{{/allParams}}).
{{operationId}}_args(_S) ->
[{{#allParams}}{{#required}}{{^-first}}, {{/-first}}{{dataType}}{{/required}}{{/allParams}}].
{{/operation}}
{{/operations}}

View File

@ -0,0 +1,2 @@
_build/
rebar.lock

View File

@ -1 +1 @@
3.3.0-SNAPSHOT 3.3.2-SNAPSHOT

View File

@ -4,4 +4,4 @@
{shell, [{apps, [petstore]}]}. {shell, [{apps, [petstore]}]}.
{plugins, [rebar3_proper]}. {plugins, [rebar3_proper]}.

View File

@ -4,6 +4,8 @@
-export([petstore_api_response/0]). -export([petstore_api_response/0]).
-export([petstore_api_response/1]).
-export_type([petstore_api_response/0]). -export_type([petstore_api_response/0]).
-type petstore_api_response() :: -type petstore_api_response() ::
@ -12,8 +14,14 @@
| {'message', binary() } | {'message', binary() }
]. ].
petstore_api_response() -> petstore_api_response() ->
[ {'code', integer() } petstore_api_response([]).
, {'type', binary() }
, {'message', binary() } petstore_api_response(Fields) ->
]. Default = [ {'code', integer() }
, {'type', binary() }
, {'message', binary() }
],
lists:ukeymerge(1, lists:sort(Fields), lists:sort(Default)).

View File

@ -4,6 +4,8 @@
-export([petstore_category/0]). -export([petstore_category/0]).
-export([petstore_category/1]).
-export_type([petstore_category/0]). -export_type([petstore_category/0]).
-type petstore_category() :: -type petstore_category() ::
@ -11,7 +13,13 @@
| {'name', binary() } | {'name', binary() }
]. ].
petstore_category() -> petstore_category() ->
[ {'id', integer() } petstore_category([]).
, {'name', binary() }
]. petstore_category(Fields) ->
Default = [ {'id', integer() }
, {'name', binary() }
],
lists:ukeymerge(1, lists:sort(Fields), lists:sort(Default)).

View File

@ -4,6 +4,8 @@
-export([petstore_order/0]). -export([petstore_order/0]).
-export([petstore_order/1]).
-export_type([petstore_order/0]). -export_type([petstore_order/0]).
-type petstore_order() :: -type petstore_order() ::
@ -15,11 +17,17 @@
| {'complete', boolean() } | {'complete', boolean() }
]. ].
petstore_order() -> petstore_order() ->
[ {'id', integer() } petstore_order([]).
, {'petId', integer() }
, {'quantity', integer() } petstore_order(Fields) ->
, {'shipDate', datetime() } Default = [ {'id', integer() }
, {'status', elements([<<"placed">>, <<"approved">>, <<"delivered">>]) } , {'petId', integer() }
, {'complete', boolean() } , {'quantity', integer() }
]. , {'shipDate', datetime() }
, {'status', elements([<<"placed">>, <<"approved">>, <<"delivered">>]) }
, {'complete', boolean() }
],
lists:ukeymerge(1, lists:sort(Fields), lists:sort(Default)).

View File

@ -4,6 +4,8 @@
-export([petstore_pet/0]). -export([petstore_pet/0]).
-export([petstore_pet/1]).
-export_type([petstore_pet/0]). -export_type([petstore_pet/0]).
-type petstore_pet() :: -type petstore_pet() ::
@ -15,11 +17,17 @@
| {'status', binary() } | {'status', binary() }
]. ].
petstore_pet() -> petstore_pet() ->
[ {'id', integer() } petstore_pet([]).
, {'category', petstore_category:petstore_category() }
, {'name', binary() } petstore_pet(Fields) ->
, {'photoUrls', list(binary()) } Default = [ {'id', integer() }
, {'tags', list(petstore_tag:petstore_tag()) } , {'category', petstore_category:petstore_category() }
, {'status', elements([<<"available">>, <<"pending">>, <<"sold">>]) } , {'name', binary() }
]. , {'photoUrls', list(binary()) }
, {'tags', list(petstore_tag:petstore_tag()) }
, {'status', elements([<<"available">>, <<"pending">>, <<"sold">>]) }
],
lists:ukeymerge(1, lists:sort(Fields), lists:sort(Default)).

View File

@ -4,6 +4,8 @@
-export([petstore_tag/0]). -export([petstore_tag/0]).
-export([petstore_tag/1]).
-export_type([petstore_tag/0]). -export_type([petstore_tag/0]).
-type petstore_tag() :: -type petstore_tag() ::
@ -11,7 +13,13 @@
| {'name', binary() } | {'name', binary() }
]. ].
petstore_tag() -> petstore_tag() ->
[ {'id', integer() } petstore_tag([]).
, {'name', binary() }
]. petstore_tag(Fields) ->
Default = [ {'id', integer() }
, {'name', binary() }
],
lists:ukeymerge(1, lists:sort(Fields), lists:sort(Default)).

View File

@ -4,6 +4,8 @@
-export([petstore_user/0]). -export([petstore_user/0]).
-export([petstore_user/1]).
-export_type([petstore_user/0]). -export_type([petstore_user/0]).
-type petstore_user() :: -type petstore_user() ::
@ -17,13 +19,19 @@
| {'userStatus', integer() } | {'userStatus', integer() }
]. ].
petstore_user() -> petstore_user() ->
[ {'id', integer() } petstore_user([]).
, {'username', binary() }
, {'firstName', binary() } petstore_user(Fields) ->
, {'lastName', binary() } Default = [ {'id', integer() }
, {'email', binary() } , {'username', binary() }
, {'password', binary() } , {'firstName', binary() }
, {'phone', binary() } , {'lastName', binary() }
, {'userStatus', integer() } , {'email', binary() }
]. , {'password', binary() }
, {'phone', binary() }
, {'userStatus', integer() }
],
lists:ukeymerge(1, lists:sort(Fields), lists:sort(Default)).

View File

@ -21,7 +21,7 @@ create_user(PetstoreUser) ->
Host = application:get_env(petstore, host, "http://localhost:8080"), Host = application:get_env(petstore, host, "http://localhost:8080"),
Path = ["/user"], Path = ["/user"],
Body = PetstoreUser, Body = PetstoreUser,
ContentType = <<"text/plain">>, ContentType = "text/plain",
petstore_utils:request(Method, [Host, ?BASE_URL, Path], jsx:encode(Body), ContentType). petstore_utils:request(Method, [Host, ?BASE_URL, Path], jsx:encode(Body), ContentType).
@ -34,7 +34,7 @@ create_users_with_array_input(PetstoreUserArray) ->
Host = application:get_env(petstore, host, "http://localhost:8080"), Host = application:get_env(petstore, host, "http://localhost:8080"),
Path = ["/user/createWithArray"], Path = ["/user/createWithArray"],
Body = PetstoreUserArray, Body = PetstoreUserArray,
ContentType = <<"text/plain">>, ContentType = "text/plain",
petstore_utils:request(Method, [Host, ?BASE_URL, Path], jsx:encode(Body), ContentType). petstore_utils:request(Method, [Host, ?BASE_URL, Path], jsx:encode(Body), ContentType).
@ -47,7 +47,7 @@ create_users_with_list_input(PetstoreUserArray) ->
Host = application:get_env(petstore, host, "http://localhost:8080"), Host = application:get_env(petstore, host, "http://localhost:8080"),
Path = ["/user/createWithList"], Path = ["/user/createWithList"],
Body = PetstoreUserArray, Body = PetstoreUserArray,
ContentType = <<"text/plain">>, ContentType = "text/plain",
petstore_utils:request(Method, [Host, ?BASE_URL, Path], jsx:encode(Body), ContentType). petstore_utils:request(Method, [Host, ?BASE_URL, Path], jsx:encode(Body), ContentType).
@ -59,10 +59,8 @@ delete_user(Username) ->
Method = delete, Method = delete,
Host = application:get_env(petstore, host, "http://localhost:8080"), Host = application:get_env(petstore, host, "http://localhost:8080"),
Path = ["/user/", Username, ""], Path = ["/user/", Username, ""],
Body = [],
ContentType = <<"text/plain">>,
petstore_utils:request(Method, [Host, ?BASE_URL, Path], jsx:encode(Body), ContentType). petstore_utils:request(Method, [Host, ?BASE_URL, Path]).
%% @doc Get user by user name %% @doc Get user by user name
%% %%
@ -72,10 +70,8 @@ get_user_by_name(Username) ->
Method = get, Method = get,
Host = application:get_env(petstore, host, "http://localhost:8080"), Host = application:get_env(petstore, host, "http://localhost:8080"),
Path = ["/user/", Username, ""], Path = ["/user/", Username, ""],
Body = [],
ContentType = <<"text/plain">>,
petstore_utils:request(Method, [Host, ?BASE_URL, Path], jsx:encode(Body), ContentType). petstore_utils:request(Method, [Host, ?BASE_URL, Path]).
%% @doc Logs user into the system %% @doc Logs user into the system
%% %%
@ -85,11 +81,9 @@ login_user(Username, Password) ->
Method = get, Method = get,
Host = application:get_env(petstore, host, "http://localhost:8080"), Host = application:get_env(petstore, host, "http://localhost:8080"),
Path = ["/user/login"], Path = ["/user/login"],
Body = [],
ContentType = <<"text/plain">>,
QueryString = [<<"username=">>, Username, <<"&">>, <<"password=">>, Password, <<"&">>], QueryString = [<<"username=">>, Username, <<"&">>, <<"password=">>, Password, <<"&">>],
petstore_utils:request(Method, [Host, ?BASE_URL, Path, <<"?">>, QueryString], jsx:encode(Body), ContentType). petstore_utils:request(Method, [Host, ?BASE_URL, Path, <<"?">>, QueryString]).
%% @doc Logs out current logged in user session %% @doc Logs out current logged in user session
%% %%
@ -99,10 +93,8 @@ logout_user() ->
Method = get, Method = get,
Host = application:get_env(petstore, host, "http://localhost:8080"), Host = application:get_env(petstore, host, "http://localhost:8080"),
Path = ["/user/logout"], Path = ["/user/logout"],
Body = [],
ContentType = <<"text/plain">>,
petstore_utils:request(Method, [Host, ?BASE_URL, Path], jsx:encode(Body), ContentType). petstore_utils:request(Method, [Host, ?BASE_URL, Path]).
%% @doc Updated user %% @doc Updated user
%% This can only be done by the logged in user. %% This can only be done by the logged in user.
@ -113,7 +105,7 @@ update_user(Username, PetstoreUser) ->
Host = application:get_env(petstore, host, "http://localhost:8080"), Host = application:get_env(petstore, host, "http://localhost:8080"),
Path = ["/user/", Username, ""], Path = ["/user/", Username, ""],
Body = PetstoreUser, Body = PetstoreUser,
ContentType = <<"text/plain">>, ContentType = "text/plain",
petstore_utils:request(Method, [Host, ?BASE_URL, Path], jsx:encode(Body), ContentType). petstore_utils:request(Method, [Host, ?BASE_URL, Path], jsx:encode(Body), ContentType).

View File

@ -9,160 +9,7 @@
-compile(export_all). -compile(export_all).
-compile(nowarn_export_all). -compile(nowarn_export_all).
%%============================================================================== -include("petstore_statem.hrl").
%% PropEr callbacks
%%==============================================================================
command(State) ->
Funs0 = [ {F, list_to_atom(atom_to_list(F) ++ "_args")}
|| {F, _} <- ?MODULE:module_info(exports)
],
Funs1 = [ X || {_, FArgs} = X <- Funs0,
erlang:function_exported(?MODULE, FArgs, 1)
],
proper_types:oneof([ {call, ?MODULE, F, ?MODULE:FArgs(State)}
|| {F, FArgs} <- Funs1
]).
precondition(S, {call, M, F, Args}) ->
Pre = list_to_atom(atom_to_list(F) ++ "_pre"),
case erlang:function_exported(M, Pre, 1) of
true -> M:Pre(S);
false -> true
end
andalso
case erlang:function_exported(M, Pre, 2) of
true -> M:Pre(S, Args);
false -> true
end.
next_state(S, Res, {call, M, F, Args}) ->
Next = list_to_atom(atom_to_list(F) ++ "_next"),
case erlang:function_exported(M, Next, 3) of
true -> M:Next(S, Res, Args);
false -> S
end.
postcondition(S, {call, M, F, Args}, Res) ->
Post = list_to_atom(atom_to_list(F) ++ "_post"),
case erlang:function_exported(M, Post, 3) of
true -> M:Post(S, Args, Res);
false -> true
end.
%%==============================================================================
%% create_user
%%==============================================================================
create_user(PetstoreUser) ->
petstore_api:create_user(PetstoreUser).
create_user_args(S) ->
Args = [petstore_user:petstore_user()],
case erlang:function_exported(?MODULE, 'create_user_args_custom', 2) of
true -> ?MODULE:create_user_args_custom(S, Args);
false -> Args
end.
%%==============================================================================
%% create_users_with_array_input
%%==============================================================================
create_users_with_array_input(PetstoreUserArray) ->
petstore_api:create_users_with_array_input(PetstoreUserArray).
create_users_with_array_input_args(S) ->
Args = [list(petstore_user:petstore_user())],
case erlang:function_exported(?MODULE, 'create_users_with_array_input_args_custom', 2) of
true -> ?MODULE:create_users_with_array_input_args_custom(S, Args);
false -> Args
end.
%%==============================================================================
%% create_users_with_list_input
%%==============================================================================
create_users_with_list_input(PetstoreUserArray) ->
petstore_api:create_users_with_list_input(PetstoreUserArray).
create_users_with_list_input_args(S) ->
Args = [list(petstore_user:petstore_user())],
case erlang:function_exported(?MODULE, 'create_users_with_list_input_args_custom', 2) of
true -> ?MODULE:create_users_with_list_input_args_custom(S, Args);
false -> Args
end.
%%==============================================================================
%% delete_user
%%==============================================================================
delete_user(Username) ->
petstore_api:delete_user(Username).
delete_user_args(S) ->
Args = [binary()],
case erlang:function_exported(?MODULE, 'delete_user_args_custom', 2) of
true -> ?MODULE:delete_user_args_custom(S, Args);
false -> Args
end.
%%==============================================================================
%% get_user_by_name
%%==============================================================================
get_user_by_name(Username) ->
petstore_api:get_user_by_name(Username).
get_user_by_name_args(S) ->
Args = [binary()],
case erlang:function_exported(?MODULE, 'get_user_by_name_args_custom', 2) of
true -> ?MODULE:get_user_by_name_args_custom(S, Args);
false -> Args
end.
%%==============================================================================
%% login_user
%%==============================================================================
login_user(Username, Password) ->
petstore_api:login_user(Username, Password).
login_user_args(S) ->
Args = [binary(), binary()],
case erlang:function_exported(?MODULE, 'login_user_args_custom', 2) of
true -> ?MODULE:login_user_args_custom(S, Args);
false -> Args
end.
%%==============================================================================
%% logout_user
%%==============================================================================
logout_user() ->
petstore_api:logout_user().
logout_user_args(S) ->
Args = [],
case erlang:function_exported(?MODULE, 'logout_user_args_custom', 2) of
true -> ?MODULE:logout_user_args_custom(S, Args);
false -> Args
end.
%%==============================================================================
%% update_user
%%==============================================================================
update_user(Username, PetstoreUser) ->
petstore_api:update_user(Username, PetstoreUser).
update_user_args(S) ->
Args = [binary(), petstore_user:petstore_user()],
case erlang:function_exported(?MODULE, 'update_user_args_custom', 2) of
true -> ?MODULE:update_user_args_custom(S, Args);
false -> Args
end.
%%============================================================================== %%==============================================================================
%% The statem's property %% The statem's property
@ -193,7 +40,100 @@ prop_main() ->
). ).
%%============================================================================== %%==============================================================================
%% Include file with setup, cleanup, initial_state %% Setup
%% and state transitions callbacks
%%============================================================================== %%==============================================================================
-include("petstore_statem.hrl").
setup() -> ok.
%%==============================================================================
%% Cleanup
%%==============================================================================
cleanup() -> ok.
%%==============================================================================
%% Initial State
%%==============================================================================
initial_state() -> #{}.
%%==============================================================================
%% create_user
%%==============================================================================
create_user(PetstoreUser) ->
petstore_api:create_user(PetstoreUser).
create_user_args(_S) ->
[petstore_user:petstore_user()].
%%==============================================================================
%% create_users_with_array_input
%%==============================================================================
create_users_with_array_input(PetstoreUserArray) ->
petstore_api:create_users_with_array_input(PetstoreUserArray).
create_users_with_array_input_args(_S) ->
[list(petstore_user:petstore_user())].
%%==============================================================================
%% create_users_with_list_input
%%==============================================================================
create_users_with_list_input(PetstoreUserArray) ->
petstore_api:create_users_with_list_input(PetstoreUserArray).
create_users_with_list_input_args(_S) ->
[list(petstore_user:petstore_user())].
%%==============================================================================
%% delete_user
%%==============================================================================
delete_user(Username) ->
petstore_api:delete_user(Username).
delete_user_args(_S) ->
[binary()].
%%==============================================================================
%% get_user_by_name
%%==============================================================================
get_user_by_name(Username) ->
petstore_api:get_user_by_name(Username).
get_user_by_name_args(_S) ->
[binary()].
%%==============================================================================
%% login_user
%%==============================================================================
login_user(Username, Password) ->
petstore_api:login_user(Username, Password).
login_user_args(_S) ->
[binary(), binary()].
%%==============================================================================
%% logout_user
%%==============================================================================
logout_user() ->
petstore_api:logout_user().
logout_user_args(_S) ->
[].
%%==============================================================================
%% update_user
%%==============================================================================
update_user(Username, PetstoreUser) ->
petstore_api:update_user(Username, PetstoreUser).
update_user_args(_S) ->
[binary(), petstore_user:petstore_user()].

View File

@ -1,25 +1,49 @@
%%============================================================================== %%==============================================================================
%% Setup %% Syntactic sugar
%%============================================================================== %%==============================================================================
setup() -> ok. command(State) ->
Funs0 = [ {F, list_to_atom(atom_to_list(F) ++ "_args")}
|| {F, _} <- ?MODULE:module_info(exports)
],
%%============================================================================== Funs1 = [ X || {_, FArgs} = X <- Funs0,
%% Cleanup erlang:function_exported(?MODULE, FArgs, 1)
%%============================================================================== ],
cleanup() -> ok. WeightFun = case erlang:function_exported(?MODULE, weight, 1) of
true -> fun ?MODULE:weight/1;
false -> fun(_) -> 1 end
end,
%%============================================================================== proper_types:frequency([ { WeightFun(F)
%% Initial State , {call, ?MODULE, F, ?MODULE:FArgs(State)}
%%============================================================================== }
|| {F, FArgs} <- Funs1
]).
initial_state() -> #{}. precondition(S, {call, M, F, Args}) ->
Pre = list_to_atom(atom_to_list(F) ++ "_pre"),
case erlang:function_exported(M, Pre, 1) of
true -> M:Pre(S);
false -> true
end
andalso
case erlang:function_exported(M, Pre, 2) of
true -> M:Pre(S, Args);
false -> true
end.
%%============================================================================== next_state(S, Res, {call, M, F, Args}) ->
%% State transitions callbacks Next = list_to_atom(atom_to_list(F) ++ "_next"),
%% case erlang:function_exported(M, Next, 3) of
%% operation_pre(State) -> true. true -> M:Next(S, Res, Args);
%% operation_next(State, Result, Args) -> State. false -> S
%% operation_post(State, Args, Result) -> true. end.
%%==============================================================================
postcondition(S, {call, M, F, Args}, Res) ->
Post = list_to_atom(atom_to_list(F) ++ "_post"),
case erlang:function_exported(M, Post, 3) of
true -> M:Post(S, Args, Res);
false -> true
end.