diff --git a/.gitignore b/.gitignore index 22f8bc9e..1048d440 100644 --- a/.gitignore +++ b/.gitignore @@ -9,7 +9,6 @@ doc/*.css doc/*.html doc/*.png doc/edoc-info -include/compile_flags.hrl .directory .eunit .rebar diff --git a/.travis.yml b/.travis.yml index dfbecab7..57acb61a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,16 +1,24 @@ sudo: false language: erlang -script: "make all" +script: +- | + ~/rebar3 compile + REBAR=~/rebar3 make all +- | + rm -rf _build + ./configure --use-sfmt + make fast && cd deps/sfmt && make && cd - + make all otp_release: - - 20.3 - - 20.0 - - 19.3 - - 19.0 - - 18.3 - - 18.1 - - 17.4 - - 17.0 +- 20.3 +- 20.0 +- 19.3 +- 19.0 +- 18.3 +- 18.1 +- 17.4 +- 17.0 # coverage tests matrix: @@ -42,5 +50,8 @@ matrix: - bash <(curl -s https://codecov.io/bash) cache: - directories: - - .plt + directories: + - .plt + +install: +- curl -#fSLo ~/rebar3 https://s3.amazonaws.com/rebar3/rebar3 && chmod +x ~/rebar3 diff --git a/Makefile b/Makefile index 62b3bb0d..80ee6b88 100644 --- a/Makefile +++ b/Makefile @@ -20,7 +20,7 @@ # Author(s): Manolis Papadakis, Kostis Sagonas # Description: Instructions for make -.PHONY: default fast all get-deps compile dialyzer check_escripts tests doc clean distclean rebuild retest +.PHONY: default fast all get-deps compile dialyzer check_escripts tests doc clean distclean rebuild retest cover ifneq (,$(findstring Windows,$(OS))) SEP := $(strip \) @@ -28,7 +28,8 @@ else SEP := $(strip /) endif -REBAR := .$(SEP)rebar +REBAR ?= .$(SEP)rebar +REBAR3 ?= rebar3 default: fast @@ -36,23 +37,20 @@ fast: get-deps compile all: fast dialyzer doc tests -include/compile_flags.hrl: write_compile_flags - ./write_compile_flags $@ - get-deps: $(REBAR) get-deps -compile: include/compile_flags.hrl +compile: $(REBAR) compile dialyzer: .plt/proper_plt compile - dialyzer -n -nn --plt $< -Wunmatched_returns ebin $(find . -path 'deps/*/ebin/*.beam') + dialyzer -n -nn --plt $< -Wunmatched_returns $(wildcard ebin deps/*/ebin _build/default/lib/*/ebin) .plt/proper_plt: .plt dialyzer --build_plt --output_plt $@ --apps erts kernel stdlib compiler crypto syntax_tools check_escripts: - ./check_escripts.sh make_doc write_compile_flags + ./check_escripts.sh make_doc tests: compile $(REBAR) eunit @@ -60,15 +58,19 @@ tests: compile doc: compile ./make_doc +cover: +# Show compilation warnings then run tests then display coverage info + $(REBAR3) do compile, cover --reset, eunit --cover, cover --verbose + clean: ./clean_temp.sh distclean: clean $(RM) -r .eunit .rebar - $(RM) include/compile_flags.hrl .plt/proper_plt + $(RM) .plt/proper_plt $(REBAR) clean -rebuild: distclean include/compile_flags.hrl +rebuild: distclean $(REBAR) compile retest: compile diff --git a/README.md b/README.md index 0a1ba4f9..06cd3cbd 100644 --- a/README.md +++ b/README.md @@ -96,7 +96,11 @@ Quickstart guide a `make all` call; in that case, you are going to need the `syntax_tools` application and a recent version of `EDoc`). Optionally, sfmt-erlang can be selected as an alternative random number - generator using `./configure --use-sfmt` before running `make`. + generator using `./configure --use-sfmt` before running `make`, or with rebar3: + ```erlang + {deps, [proper,sfmt]}. + {overrides, [{override, proper, [{erl_opts, [{d, 'RNG', sfmt}]}]}]}. + ``` * Add PropEr's base directory to your Erlang library path, using one of the following methods: 1. `ERL_LIBS` environment variable: Add the following line to your shell diff --git a/include/proper_internal.hrl b/include/proper_internal.hrl index 7be80d7b..2d8d254b 100644 --- a/include/proper_internal.hrl +++ b/include/proper_internal.hrl @@ -26,7 +26,6 @@ %%% @doc Internal header file: This header is included in all PropEr source %%% files. --include("compile_flags.hrl"). -include("proper_common.hrl"). @@ -34,21 +33,41 @@ %% Random generator selection %%------------------------------------------------------------------------------ --ifdef(USE_SFMT). --define(RANDOM_MOD, sfmt). --define(SEED_NAME, sfmt_seed). +-ifndef(RNG). +- ifndef(USE_SFMT). +- ifndef(USE_EXSPLUS). +- ifndef(USE_RANDOM). +- ifdef(AT_LEAST_19). +%% for 19.x use 'rand' module +- define(USE_EXSPLUS, true). +- else. +%% for 18.x and older use 'random' module +- define(USE_RANDOM, true). +- endif. +- endif. +- endif. +- endif. +-endif. --else. +-ifdef(USE_EXSPLUS). +-define(RNG, rand). +-define(RNG_SET_SEED(Seed), ?RNG:seed(exsplus,Seed)). +-endif. --ifdef(AT_LEAST_19). --define(RANDOM_MOD, rand). %% for 19.x use the 'rand' module --define(SEED_NAME, rand_seed). --else. --define(RANDOM_MOD, random). --define(SEED_NAME, random_seed). +-ifdef(USE_SFMT). +-define(RNG, sfmt). -endif. + +-ifdef(USE_RANDOM). +-define(RNG, random). -endif. +-define(SEED_NAME, list_to_atom(atom_to_list(?RNG)++"_seed")). +-ifndef(USE_EXSPLUS). +-define(RNG_SET_SEED(Seed), ?RNG:seed(Seed)). +-endif. +-define(RNG_UNIFORM(), ?RNG:uniform()). +-define(RNG_UNIFORM(UpperBound), ?RNG:uniform(UpperBound)). %%------------------------------------------------------------------------------ %% Line annotations diff --git a/mix.exs b/mix.exs index 9133376b..9543fa3f 100644 --- a/mix.exs +++ b/mix.exs @@ -22,7 +22,7 @@ defmodule Proper.Mixfile do end defp package do - [files: ~w(src include rebar.config configure Makefile COPYING README.md THANKS check_escripts.sh clean_doc.sh clean_temp.sh write_compile_flags mix.exs), + [files: ~w(src include rebar.config configure Makefile COPYING README.md THANKS check_escripts.sh clean_doc.sh clean_temp.sh mix.exs), maintainers: ["Manolis Papadakis", "Eirini Arvaniti", "Kostis Sagonas"], licenses: ["GPL"], links: %{"GitHub" => "https://github.com/manopapad/proper"}] diff --git a/rebar.config b/rebar.config index 96ad98e9..818914e4 100644 --- a/rebar.config +++ b/rebar.config @@ -27,6 +27,8 @@ %% WARNING: Our version of rebar does NOT automatically report warnings, %% nor does it add erl_opts to eunit_compile_opts. +{minimum_otp_vsn, "17.0"}. + {erl_first_files, ["src/vararg.erl"]}. {eunit_first_files, ["src/vararg.erl", "src/proper_transformer.erl", @@ -35,9 +37,22 @@ {erl_opts, [debug_info, report_warnings, {warn_format,1}, warn_export_vars, warn_obsolete_guard, warn_unused_import, - warn_missing_spec, warn_untyped_record]}. + warn_missing_spec, warn_untyped_record, + warn_unused_vars, + {platform_define, "^17", 'USE_ERL_SCAN_LINE'}, + {platform_define, "^19|^2", 'AT_LEAST_19'}, + {platform_define, "^2", 'AT_LEAST_20'}, + {platform_define, "^2[1-9]", 'AT_LEAST_21'}]}. -{pre_hooks, [{"(linux|darwin|gnu)", compile, "make include/compile_flags.hrl"}, - {"(freebsd|netbsd|openbsd|solaris|dragonfly)", compile, "gmake include/compile_flags.hrl"}, - {"win32", compile, "escript.exe write_compile_flags include/compile_flags.hrl"}]}. {post_hooks, [{clean, "./clean_doc.sh"}]}. + +{profiles, [{test, [{erl_opts, [nowarn_missing_spec + %% TODO: remove most of these: + ,nowarn_export_all + ,nowarn_untyped_record + ]}]}]}. + +{dialyzer, [{warnings, [unmatched_returns + ]} + ,{plt_extra_apps, [erts, kernel, stdlib, compiler, crypto, syntax_tools]} + ]}. diff --git a/rebar.lock b/rebar.lock new file mode 100644 index 00000000..57afcca0 --- /dev/null +++ b/rebar.lock @@ -0,0 +1 @@ +[]. diff --git a/src/proper_arith.erl b/src/proper_arith.erl index 7f0716aa..c2ea22bf 100644 --- a/src/proper_arith.erl +++ b/src/proper_arith.erl @@ -33,7 +33,7 @@ safe_any/2, safe_zip/2, tuple_map/2, cut_improper_tail/1, head_length/1, find_first/2, filter/2, partition/2, remove/2, insert/3, unflatten/2]). --export([rand_start/1, rand_restart/1, rand_reseed/0, rand_stop/0, +-export([rand_restart/1, rand_reseed/0, rand_stop/0, rand_int/1, rand_int/2, smart_rand_int/3, rand_non_neg_int/1, rand_float/1, rand_float/2, rand_non_neg_float/1, distribute/2, jumble/1, rand_choose/1, freq_choose/1]). @@ -221,47 +221,25 @@ remove_n(N, {List,Acc}) -> %% Random functions %%----------------------------------------------------------------------------- -%% @doc Seeds the random number generator. This function should be run before -%% calling any random function from this module. --spec rand_start(seed()) -> 'ok'. --ifdef(AT_LEAST_19). -rand_start(Seed) -> - _ = rand:seed(exsplus, Seed), - ok. --else. -rand_start(Seed) -> - _ = ?RANDOM_MOD:seed(Seed), - %% TODO: read option for RNG bijections here - ok. --endif. - %% @doc Conditionally seeds the random number generator. This function should %% be run before calling any random function from this module. -spec rand_restart(seed()) -> 'ok'. rand_restart(Seed) -> - case get(?SEED_NAME) of - undefined -> - rand_start(Seed); - _ -> - ok - end. + _ = undefined == get(?SEED_NAME) + %% TODO: read option for RNG bijections here + andalso ?RNG_SET_SEED(Seed), + ok. -spec rand_reseed() -> 'ok'. --ifdef(AT_LEAST_19). -rand_reseed() -> - _ = rand:seed(exsplus, os:timestamp()), - ok. --else. +%% TODO: This should use the pid of the process somehow, in case two +%% spawned functions call it simultaneously? rand_reseed() -> - %% TODO: This should use the pid of the process somehow, in case two - %% spawned functions call it simultaneously? - _ = ?RANDOM_MOD:seed(os:timestamp()), + _ = ?RNG_SET_SEED(os:timestamp()), ok. --endif. -spec rand_stop() -> 'ok'. rand_stop() -> - erase(?SEED_NAME), + _ = erase(?SEED_NAME), ok. -spec rand_int(non_neg_integer()) -> integer(). @@ -283,7 +261,7 @@ bounded_rand_non_neg_int(Const, Lim) when is_integer(Lim), Lim >= 0 -> -spec rand_int(integer(), integer()) -> integer(). rand_int(Low, High) when is_integer(Low), is_integer(High), Low =< High -> - Low + ?RANDOM_MOD:uniform(High - Low + 1) - 1. + Low + ?RNG_UNIFORM(High - Low + 1) - 1. %% When the range is large, skew the distribution to be more like that of an %% unbounded random integer. @@ -301,7 +279,7 @@ wide_range_rand_int(Const, Low, High) when Low >= 0 -> wide_range_rand_int(Const, Low, High) when High =< 0 -> High - bounded_rand_non_neg_int(Const, High - Low); wide_range_rand_int(Const, Low, High) -> - case ?RANDOM_MOD:uniform(2) of + case ?RNG_UNIFORM(2) of 1 -> smart_rand_int(Const, 0, High); 2 -> smart_rand_int(Const, Low, 0) end. @@ -309,21 +287,21 @@ wide_range_rand_int(Const, Low, High) -> -spec rand_float(non_neg_integer()) -> float(). rand_float(Const) -> X = rand_non_neg_float(Const), - case ?RANDOM_MOD:uniform(2) of + case ?RNG_UNIFORM(2) of 1 -> X; 2 -> -X end. -spec rand_non_neg_float(non_neg_integer()) -> float(). rand_non_neg_float(Const) when is_integer(Const), Const >= 0 -> - case ?RANDOM_MOD:uniform() of + case ?RNG_UNIFORM() of 1.0 -> rand_non_neg_float(Const); X -> Const * zero_one_to_zero_inf(X) end. -spec rand_float(float(), float()) -> float(). rand_float(Low, High) when is_float(Low), is_float(High), Low =< High -> - Low + ?RANDOM_MOD:uniform() * (High - Low). + Low + ?RNG_UNIFORM() * (High - Low). -spec zero_one_to_zero_inf(float()) -> float(). %% This function must return only non-negative values and map 0.0 to 0.0, but @@ -351,7 +329,7 @@ distribute_tr(CreditsLeft, PeopleLeft, AccList) -> -spec jumble([T]) -> [T]. %% @doc Produces a random permutation of a list. jumble(List) -> - [X || {_, X} <- lists:sort([{?RANDOM_MOD:uniform(), X} || X <- List])]. + [X || {_, X} <- lists:sort([{?RNG_UNIFORM(), X} || X <- List])]. -spec rand_choose([T,...]) -> {position(),T}. rand_choose(Choices) when Choices =/= [] -> diff --git a/src/proper_gen.erl b/src/proper_gen.erl index 3b1fa16c..51be8f3d 100644 --- a/src/proper_gen.erl +++ b/src/proper_gen.erl @@ -626,23 +626,8 @@ function_body(Args, RetType, {Seed1,Seed2}) -> RetType; _ -> SavedSeed = get(?SEED_NAME), - update_seed({Seed1,Seed2,erlang:phash2(Args,?SEED_RANGE)}), + _ = ?RNG_SET_SEED({Seed1, Seed2, erlang:phash2(Args,?SEED_RANGE)}), Ret = clean_instance(generate(RetType)), put(?SEED_NAME, SavedSeed), proper_symb:internal_eval(Ret) end. - --ifdef(USE_SFMT). -update_seed(Seed) -> - _ = sfmt:seed(Seed), - ok. --else. --ifdef(AT_LEAST_19). -update_seed(Seed) -> - _ = rand:seed(exsplus, Seed), - ok. --else. -update_seed(Seed) -> - put(?SEED_NAME, Seed). --endif. --endif. diff --git a/src/proper_gen_next.erl b/src/proper_gen_next.erl index 2809f217..93f92cdc 100644 --- a/src/proper_gen_next.erl +++ b/src/proper_gen_next.erl @@ -83,7 +83,7 @@ ensure_initialized() -> L = [get(proper_gen_next_cache), get(proper_gen_next_cache_backup), get(proper_gen_next_depth_cache), - get(rand_seed), + get(?SEED_NAME), get('$any_type'), get('$left'), get('$constraint_tries'), @@ -321,14 +321,14 @@ is_list_type(Type) -> has_same_generator(Type, proper_types:list(proper_types:atom())). list_choice(empty, Temp) -> - C = ?RANDOM_MOD:uniform(), + C = ?RNG_UNIFORM(), C_Add = 0.5 * Temp, if C < C_Add -> add; true -> nothing end; list_choice({list, GrowthCoefficient}, Temp) -> - C = ?RANDOM_MOD:uniform(), + C = ?RNG_UNIFORM(), AddCoefficient = 0.3 * GrowthCoefficient, DelCoefficient = 0.3 * (1- GrowthCoefficient), C_Add = AddCoefficient * Temp, @@ -341,7 +341,7 @@ list_choice({list, GrowthCoefficient}, Temp) -> true -> nothing end; list_choice(vector, Temp) -> - C = ?RANDOM_MOD:uniform(), + C = ?RNG_UNIFORM(), C_Mod = 0.5 * Temp, if C < C_Mod -> modify; @@ -354,7 +354,7 @@ list_gen_sa(Type) -> {ok, InternalType} = proper_types:find_prop(internal_type, Type), ElementType = replace_generators(InternalType), fun (Base, Temp) -> - GrowthCoefficient = (?RANDOM_MOD:uniform() * 0.8) + 0.1, + GrowthCoefficient = (?RNG_UNIFORM() * 0.8) + 0.1, list_gen_internal(Base, Temp, InternalType, ElementType, GrowthCoefficient) end. @@ -568,12 +568,12 @@ union_gen_sa(Type) -> end, [], Env) of [] -> %% generate new - Index = trunc(?RANDOM_MOD:uniform() * length(Env)) + 1, + Index = trunc(?RNG_UNIFORM() * length(Env)) + 1, ET = lists:nth(Index, Env), {ok, Value} = proper_gen:safe_generate(ET), Value; PossibleGens -> - C = ?RANDOM_MOD:uniform(), + C = ?RNG_UNIFORM(), C_Kep = 0.3 * ?TEMP(Temp), C_Chg = C_Kep + 0.3 * ?TEMP(Temp), if @@ -582,13 +582,13 @@ union_gen_sa(Type) -> Base; C < C_Chg -> %% change choice - Index = trunc(?RANDOM_MOD:uniform() * length(Env)) + 1, + Index = trunc(?RNG_UNIFORM() * length(Env)) + 1, ET = lists:nth(Index, Env), {ok, Value} = proper_gen:safe_generate(ET), Value; true -> %% modify amongst the possible - Index = trunc(?RANDOM_MOD:uniform() * length(PossibleGens)) + 1, + Index = trunc(?RNG_UNIFORM() * length(PossibleGens)) + 1, ElementGen = lists:nth(Index, PossibleGens), SAGen = replace_generators(ElementGen), SAGen(Base, Temp) @@ -766,11 +766,11 @@ get_size(Type, Temp) -> not_found -> %% use random initial size %% proper:get_size(Type); - trunc(?RANDOM_MOD:uniform() * 21 + 1); + trunc(?RNG_UNIFORM() * 21 + 1); {ok, Base} -> %% alternate base size (max size is not accessible from the generator) OffsetLimit = trunc(21 * Temp + 1), - Offset = trunc(?RANDOM_MOD:uniform() * OffsetLimit + 1), + Offset = trunc(?RNG_UNIFORM() * OffsetLimit + 1), make_inrange(Base, Offset, 1, 42) end, set_cache_size(Type, Size), diff --git a/src/proper_sa.erl b/src/proper_sa.erl index 221825c1..d42ad201 100644 --- a/src/proper_sa.erl +++ b/src/proper_sa.erl @@ -56,8 +56,6 @@ -define(REHEAT_THRESHOLD, 5). -define(RESTART_THRESHOLD, 100). --define(RANDOM_PROBABILITY, (?RANDOM_MOD:uniform())). - -define(SA_DATA, proper_sa_data). -define(SA_REHEAT_COUNTER, proper_sa_reheat_counter). @@ -119,7 +117,7 @@ acceptance_function_standard(EnergyCurrent, EnergyNew, Temperature) -> error:badarith -> 0.0 end, %% if random probability is less, accept - ?RANDOM_PROBABILITY < AcceptanceProbability + ?RNG_UNIFORM() < AcceptanceProbability end. acceptance_function_hillclimbing(EnergyCurrent, EnergyNew, _Temperature) -> diff --git a/test/proper_tests.erl b/test/proper_tests.erl index 9682582e..b9c4a49e 100644 --- a/test/proper_tests.erl +++ b/test/proper_tests.erl @@ -28,7 +28,6 @@ -module(proper_tests). --include("compile_flags.hrl"). -include("proper.hrl"). -include_lib("eunit/include/eunit.hrl"). diff --git a/test/symb_statem_maps.erl b/test/symb_statem_maps.erl index dcf94f36..bec5a541 100644 --- a/test/symb_statem_maps.erl +++ b/test/symb_statem_maps.erl @@ -26,8 +26,6 @@ -module(symb_statem_maps). --include("compile_flags.hrl"). - -include_lib("proper/include/proper.hrl"). -export([command/1, diff --git a/write_compile_flags b/write_compile_flags deleted file mode 100755 index bdf53d06..00000000 --- a/write_compile_flags +++ /dev/null @@ -1,66 +0,0 @@ -#!/usr/bin/env escript - -%%% -*- coding: utf-8 -*- -%%% -*- erlang-indent-level: 2 -*- -%%% ------------------------------------------------------------------- -%%% Copyright 2010-2018 Manolis Papadakis , -%%% Eirini Arvaniti -%%% and Kostis Sagonas -%%% -%%% This file is part of PropEr. -%%% -%%% PropEr is free software: you can redistribute it and/or modify -%%% it under the terms of the GNU General Public License as published by -%%% the Free Software Foundation, either version 3 of the License, or -%%% (at your option) any later version. -%%% -%%% PropEr is distributed in the hope that it will be useful, -%%% but WITHOUT ANY WARRANTY; without even the implied warranty of -%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -%%% GNU General Public License for more details. -%%% -%%% You should have received a copy of the GNU General Public License -%%% along with PropEr. If not, see . - -%%% Author(s): Manolis Papadakis and Kostis Sagonas -%%% Description: Compilation environment setup script: This script computes and -%%% prints out the correct set of compilation flags for the -%%% currently running version of the OTP. - --spec main([file:filename(),...]) -> 'ok'. -main([OutFile]) -> - CurrVer = parse_version_string(erlang:system_info(version)), - case CurrVer >= [6,0] of - true -> - {ok, Handle} = file:open(OutFile, [write]), - ToDefine = - case CurrVer < [10,0] of - true -> []; - false -> ["AT_LEAST_21"] - end ++ - case CurrVer < [9,0] of - true -> []; - false -> ["AT_LEAST_20"] - end ++ - case CurrVer < [8,0] of - true -> []; - false -> ["AT_LEAST_19"] - end ++ - %% older than 18.0 => erl_scan:line() type not marked deprecated - case CurrVer < [7,0] of - true -> ["USE_ERL_SCAN_LINE"]; - false -> [] - end, - Fun = fun(X) -> io:format(Handle, "-define(~s, 1).~n", [X]) end, - lists:foreach(Fun, ToDefine), - ok = file:close(Handle); - false -> - halt(1) %% we do not support releases prior to 17 - end. - -%% the stupid try-catch construct below is due to e.g. the R15A -%% release being denoted as having "5.9.pre" as version info :-( --spec parse_version_string(string()) -> [non_neg_integer()]. -parse_version_string(VerStr) -> - [try list_to_integer(S) - catch _:_ -> 0 end || S <- string:tokens(VerStr, ".")].