| %% -*- erlang-indent-level: 4;indent-tabs-mode: nil -*- |
| %% ex: ts=4 sw=4 et |
| %% |
| %% This file is part of Triq - Trifork QuickCheck |
| %% |
| %% Copyright (c) 2016 Tuncer Ayaz |
| %% |
| %% Licensed under the Apache License, Version 2.0 (the "License"); |
| %% you may not use this file except in compliance with the License. |
| %% You may obtain a copy of the License at |
| %% |
| %% http://www.apache.org/licenses/LICENSE-2.0 |
| %% |
| %% Unless required by applicable law or agreed to in writing, software |
| %% distributed under the License is distributed on an "AS IS" BASIS, |
| %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| %% See the License for the specific language governing permissions and |
| %% limitations under the License. |
| %% |
| |
| -module(triq_rand_compat). |
| |
| %% API |
| -export([ init/0 |
| , init/1 |
| ]). |
| |
| -define(DEFAULT_MODNAME, "rnd"). |
| |
| %%%=================================================================== |
| %%% API |
| %%%=================================================================== |
| |
| %%-------------------------------------------------------------------- |
| %% @doc |
| %% Generate, compile and load rnd module. |
| %% @end |
| %%-------------------------------------------------------------------- |
| -spec init() -> {'ok', module()}. |
| init() -> |
| init(?DEFAULT_MODNAME). |
| |
| %%-------------------------------------------------------------------- |
| %% @doc |
| %% Generate, compile and load ModName module. |
| %% @end |
| %%-------------------------------------------------------------------- |
| -spec init(string()) -> {'ok', module()}. |
| init(ModName) -> |
| %% First, select the right module, then generate the appropriate code as a |
| %% string, and finally compile and load it as ModName. |
| Src = select(ModName), |
| {ok, Mod, Bin, []} = compile(Src), |
| {module, Mod} = code:load_binary(Mod, ModName++".erl", Bin), |
| {ok, Mod}. |
| |
| %%%=================================================================== |
| %%% Internal functions |
| %%%=================================================================== |
| |
| %% Select right rand module and return wrapper module's source as string |
| -spec select(string()) -> string(). |
| select(ModName) -> |
| case code:which(rand) of |
| non_existing -> |
| src(ModName, fun funs_random/0); |
| _ -> |
| src(ModName, fun funs_rand/0) |
| end. |
| |
| %% Return module's implementation as a string. |
| -spec src(string(), fun(() -> binary())) -> string(). |
| src(ModName, GenFuns) -> |
| lists:flatten( |
| io_lib:format( |
| <<" |
| -module(~s). |
| -export([ seed/0 |
| , seed/1 |
| , uniform/0 |
| , uniform/1 |
| , uniform_s/1 |
| , uniform_s/2 |
| ]). |
| |
| %% Functions |
| ~s |
| ">> |
| , [ModName, GenFuns()])). |
| |
| %% random.beam wrapper |
| funs_random() -> |
| <<" |
| seed() -> random:seed(). |
| seed(Exp) -> random:seed(Exp). |
| uniform() -> random:uniform(). |
| uniform(N) -> random:uniform(N). |
| uniform_s(St) -> random:uniform_s(St). |
| uniform_s(N, St) -> random:uniform_s(N, St). |
| ">>. |
| |
| %% rand.beam wrapper |
| funs_rand() -> |
| <<" |
| seed() -> rand:seed(exsplus). |
| seed(Exp) -> rand:seed(exsplus, Exp). |
| uniform() -> rand:uniform(). |
| uniform(N) -> rand:uniform(N). |
| uniform_s(St) -> rand:uniform_s(St). |
| uniform_s(N, St) -> rand:uniform_s(N, St). |
| ">>. |
| |
| compile(String) -> |
| Forms = convert(String ++ eof, []), |
| compile:forms(Forms, [return]). |
| |
| %% Parse string into forms for compiler. |
| convert({done, {eof, _EndLocation}, _LeftOverChars}, Acc)-> |
| %% Finished |
| lists:reverse(Acc); |
| convert({done, {error, ErrorInfo, _EndLocation}, _LeftOverChars}, _Acc)-> |
| ErrorInfo; |
| convert({done, {ok, Tokens, _EndLocation}, LeftOverChars}, Acc)-> |
| case erl_parse:parse_form(Tokens) of |
| {ok, AbsForm} -> |
| convert(LeftOverChars, [AbsForm|Acc]); |
| {error, AbsForm} -> |
| convert(LeftOverChars, AbsForm) |
| end; |
| convert({more, Continuation}, Acc)-> |
| convert(erl_scan:tokens(Continuation, [], 1), Acc); |
| convert(String, Acc) -> |
| convert(erl_scan:tokens([], String, 1), Acc). |
| |
| %%%=================================================================== |
| %%% Tests |
| %%%=================================================================== |
| |
| -ifdef(TEST). |
| -include_lib("eunit/include/eunit.hrl"). |
| |
| init_test() -> |
| DefMod = list_to_atom(?DEFAULT_MODNAME), |
| ok = unload(DefMod), |
| ?assertMatch(false, code:is_loaded(DefMod)), |
| ?assertMatch({ok, DefMod}, init()), |
| ?assertMatch({file, _}, code:is_loaded(DefMod)), |
| check_api(DefMod), |
| CustomMod = foornd, |
| CustomName = "foornd", |
| ok = unload(CustomMod), |
| ?assertMatch(false, code:is_loaded(CustomMod)), |
| ?assertMatch({ok, CustomMod}, init(CustomName)), |
| ?assertMatch({file, _}, code:is_loaded(CustomMod)), |
| check_api(CustomMod). |
| |
| unload(Mod) -> |
| case code:is_loaded(Mod) of |
| false -> |
| ok; |
| {file, _} -> |
| code:delete(Mod), |
| code:purge(Mod), |
| ok |
| end. |
| |
| check_api(Mod) -> |
| Exports = [ {seed, 0} |
| , {seed, 1} |
| , {uniform, 0} |
| , {uniform, 1} |
| , {uniform_s, 1} |
| , {uniform_s, 2} |
| , {module_info, 0} |
| , {module_info, 1} |
| ], |
| ?assertMatch(Exports, Mod:module_info(exports)). |
| |
| -endif. |