From 5cc3b7ebef51a96c10fcc5fbca77200208477e64 Mon Sep 17 00:00:00 2001 From: Uvarov Michael Date: Tue, 4 Dec 2012 12:03:48 +0400 Subject: [PATCH 1/5] Add basic unicode generators. --- include/proper.hrl | 8 +++++++ src/proper_unicode.erl | 48 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 56 insertions(+) create mode 100644 src/proper_unicode.erl diff --git a/include/proper.hrl b/include/proper.hrl index 2c47fdc6..3828eee5 100644 --- a/include/proper.hrl +++ b/include/proper.hrl @@ -89,6 +89,14 @@ command_names/1, zip/2, run_parallel_commands/2, run_parallel_commands/3]). + +%%------------------------------------------------------------------------------ +%% Unicode functions +%%------------------------------------------------------------------------------ + +-import(proper_unicode, [unicode_char/0, unicode_string/0, unicode_string/1, + unicode_binary/0, unicode_binary/1]). + -endif. diff --git a/src/proper_unicode.erl b/src/proper_unicode.erl new file mode 100644 index 00000000..72364625 --- /dev/null +++ b/src/proper_unicode.erl @@ -0,0 +1,48 @@ +%% @doc This module provides basic unicode generators. +%% @end +%% @todo add `unicode_characters'. +-module(proper_unicode). +-export([unicode_char/0, + unicode_string/0, + unicode_string/1, + unicode_binary/0, + unicode_binary/1]). + +-define(PROPER_NO_IMPORTS, true). +-import(proper_types, [char/0, list/1, vector/2]). +-include_lib("proper/include/proper.hrl"). + + +-type generator() :: term(). + +-spec unicode_char() -> generator(). +unicode_char() -> + ?SUCHTHAT(C, char(), (C < 16#D800 orelse C > 16#DFFF) andalso C =/= 16#FFFF + andalso C =/= 16#FFFE). + +-spec unicode_string() -> generator(). +unicode_string() -> + list(unicode_char()). + + +%% @doc Generate `Len' code points as a list. +-spec unicode_string(Len) -> generator() + when Len :: non_neg_integer(). + +unicode_string(Len) -> + vector(Len, unicode_char()). + + +%% @doc Generate `Len' code points as a binary. +-spec unicode_binary() -> generator(). +unicode_binary() -> + ?LET(S, unicode_string(), unicode:characters_to_binary(S)). + + +%% @doc Generate a unicode characters of length `Len'. +-spec unicode_binary(Len) -> generator() + when Len :: non_neg_integer(). + +unicode_binary(Len) -> + ?LET(S, unicode_string(Len), unicode:characters_to_binary(S)). + From 3bbff403fa4be8793dd0691333f3f36896452158 Mon Sep 17 00:00:00 2001 From: Uvarov Michael Date: Mon, 17 Dec 2012 18:06:45 +0400 Subject: [PATCH 2/5] Add maybe_improper_list/2. Add tests for unicode generators. --- include/proper.hrl | 6 +- src/proper_types.erl | 9 ++- src/proper_unicode.erl | 100 ++++++++++++++++++++++++++++------ test/proper_unicode_tests.erl | 83 ++++++++++++++++++++++++++++ 4 files changed, 179 insertions(+), 19 deletions(-) create mode 100644 test/proper_unicode_tests.erl diff --git a/include/proper.hrl b/include/proper.hrl index 3828eee5..54456675 100644 --- a/include/proper.hrl +++ b/include/proper.hrl @@ -46,7 +46,8 @@ -import(proper_types, [integer/2, float/2, atom/0, binary/0, binary/1, bitstring/0, bitstring/1, list/1, vector/2, union/1, weighted_union/1, tuple/1, loose_tuple/1, exactly/1, - fixed_list/1, function/2, any/0]). + fixed_list/1, function/2, any/0, improper_list/2, + maybe_improper_list/2]). %%------------------------------------------------------------------------------ @@ -95,7 +96,8 @@ %%------------------------------------------------------------------------------ -import(proper_unicode, [unicode_char/0, unicode_string/0, unicode_string/1, - unicode_binary/0, unicode_binary/1]). + unicode_binary/0, unicode_binary/1, unicode_binary/2, + unicode_characters/0, unicode_characters/1]). -endif. diff --git a/src/proper_types.erl b/src/proper_types.erl index 7b523e0b..5f7eb83d 100644 --- a/src/proper_types.erl +++ b/src/proper_types.erl @@ -160,6 +160,7 @@ -export([le/2]). -export_type([type/0, raw_type/0, extint/0, extnum/0]). +-export([improper_list/2, maybe_improper_list/2]). -include("proper_internal.hrl"). @@ -189,7 +190,6 @@ %% any: %% doesn't cover functions and improper lists - %%------------------------------------------------------------------------------ %% Type declaration macros %%------------------------------------------------------------------------------ @@ -1110,6 +1110,13 @@ any() -> {generator, fun proper_gen:any_gen/1} ]). +-spec improper_list(T, T) -> T when T :: proper_types:type(). +improper_list(T,S) -> + ?LET({Contents, Terminator}, {list(T), S}, Contents ++ Terminator). + +-spec maybe_improper_list(T, T) -> T when T :: proper_types:type(). +maybe_improper_list(T, S) -> + union([list(T), improper_list(T, S)]). %%------------------------------------------------------------------------------ %% Type aliases diff --git a/src/proper_unicode.erl b/src/proper_unicode.erl index 72364625..202c9f1a 100644 --- a/src/proper_unicode.erl +++ b/src/proper_unicode.erl @@ -6,10 +6,14 @@ unicode_string/0, unicode_string/1, unicode_binary/0, - unicode_binary/1]). + unicode_binary/1, + unicode_binary/2, + unicode_characters/0, + unicode_characters/1]). -define(PROPER_NO_IMPORTS, true). --import(proper_types, [char/0, list/1, vector/2]). +-import(proper_types, [char/0, list/1, vector/2, + maybe_improper_list/2, resize/2, frequency/1]). -include_lib("proper/include/proper.hrl"). @@ -17,32 +21,96 @@ -spec unicode_char() -> generator(). unicode_char() -> + ?SIZED(Size, begin +% io:format(user, "Call ~p~n", [Size]), + frequency([{10, low_char()}, + {4, high_char()}, + {2, max_char()}]) end). + +low_char() -> + proper_char(). + +high_char() -> + ?SIZED(Size, resize(Size * Size, proper_char())). + +max_char() -> + ?SIZED(Size, resize(Size * Size * Size * Size, proper_char())). + +proper_char() -> ?SUCHTHAT(C, char(), (C < 16#D800 orelse C > 16#DFFF) andalso C =/= 16#FFFF - andalso C =/= 16#FFFE). + andalso C =/= 16#FFFE). + +%% @doc Generate a list of unicode code points. -spec unicode_string() -> generator(). unicode_string() -> - list(unicode_char()). + unicode_string(undefined). -%% @doc Generate `Len' code points as a list. --spec unicode_string(Len) -> generator() - when Len :: non_neg_integer(). +%% @doc Generate a list of unicode code points of length `Size'. +-spec unicode_string(non_neg_integer() | undefined) -> generator(). +unicode_string(undefined) -> + list(unicode_char()); -unicode_string(Len) -> - vector(Len, unicode_char()). +unicode_string(Size) -> + vector(Size, unicode_char()). -%% @doc Generate `Len' code points as a binary. +%% @doc Generate an unicode binary. -spec unicode_binary() -> generator(). unicode_binary() -> - ?LET(S, unicode_string(), unicode:characters_to_binary(S)). + unicode_binary(undefined, unicode). + + +%% @doc Generate an unicode binary binary. +-spec unicode_binary(Size | Encoding) -> generator() when + Size :: non_neg_integer() | undefined, + Encoding :: unicode:encoding(). + +unicode_binary(Size) when is_integer(Size) -> + unicode_binary(Size, unicode); + +unicode_binary(Encoding) -> + unicode_binary(undefined, Encoding). + + +%% @doc Generate an unicode binary. +-spec unicode_binary(Size, Encoding) -> generator() when + Size :: non_neg_integer() | undefined, + Encoding :: unicode:encoding(). + +unicode_binary(Size, Encoding) -> + ?LET(Str, unicode_string(Size), + unicode:characters_to_binary(Str, unicode, Encoding)). + + +-spec unicode_characters() -> generator(). + +unicode_characters() -> + unicode_characters(unicode). + + +%% `unicode_characters()' should not return a single `unicode_char()'. +-spec unicode_characters(Encoding) -> generator() when + Encoding :: unicode:encoding(). + +unicode_characters(Encoding) -> + ?SIZED(Size, + frequency([{1, unicode_string()}, + {1, unicode_binary(Encoding)}, + {5, ?LAZY(resize(Size div 2, + unicode_characters1(Encoding)))} + ])). + +unicode_characters1(Encoding) -> + ?SIZED(Size, unicode_characters1(Size, Encoding)). -%% @doc Generate a unicode characters of length `Len'. --spec unicode_binary(Len) -> generator() - when Len :: non_neg_integer(). -unicode_binary(Len) -> - ?LET(S, unicode_string(Len), unicode:characters_to_binary(S)). +unicode_characters1(0, _Encoding) -> + list(unicode_char()); +unicode_characters1(Size, Encoding) -> + Chars = ?LAZY(resize(Size, unicode_characters(Encoding))), + maybe_improper_list(frequency([{10,unicode_char()}, {1, Chars}]), + unicode_binary(Encoding)). diff --git a/test/proper_unicode_tests.erl b/test/proper_unicode_tests.erl new file mode 100644 index 00000000..4cd63f02 --- /dev/null +++ b/test/proper_unicode_tests.erl @@ -0,0 +1,83 @@ +-module(proper_unicode_tests). + +%% ------------------------------------------------------------------ +%% Tests +%% ------------------------------------------------------------------ + +-include_lib("proper/include/proper.hrl"). +-include_lib("eunit/include/eunit.hrl"). + + +%% ------------------------------------------------------------------ +%% Call test generators +%% ------------------------------------------------------------------ + +prop_unicode_char() -> + ?FORALL(Char, unicode_char(), + begin +% io:format(user, "~p~n", [Char]), + true + end). + +prop_unicode_binary() -> + ?FORALL(Bin, unicode_binary(), + begin + equals(Bin, unicode:characters_to_binary( + unicode:characters_to_list(Bin))) + end). + +%% Check a binary generator with fixed length. +prop_sized_unicode_binary() -> + ?FORALL({Len, Bin}, ?LET(Len, byte(), {Len, unicode_binary(Len)}), + begin + equals(Len, length(unicode:characters_to_list(Bin))) + end). + +prop_unicode_string() -> + ?FORALL(Str, unicode_string(), + begin +% io:format(user, "~p~n", [Str]), + equals(Str, unicode:characters_to_list( + unicode:characters_to_binary(Str))) + end). + + +prop_unicode_characters() -> + ?FORALL(Chars, unicode_characters(), + begin +% io:format(user, "~p~n", [Chars]), + is_binary(unicode:characters_to_binary(Chars)) + end). + + +encoding() -> + [unicode, utf8, utf16, {utf16, little}, {utf16, big}, utf32, + {utf32, little}, {utf32, big}]. + + +prop_unicode_external_characters() -> + ?FORALL({Encoding, Chars}, + oneof([{Encoding, unicode_characters(Encoding)} + || Encoding <- encoding()]), + begin + List = unicode:characters_to_list(Chars, Encoding), +% [erlang:error(1) || +% length(List) > 2, hd(tl(List)) > 1000000], +% io:format(user, "~p~n", [Chars]), + is_binary(unicode:characters_to_binary(Chars, Encoding)) + end). + + +%% ------------------------------------------------------------------- +%% Property Testing +%% ------------------------------------------------------------------- + +run_property_testing_test_() -> + {timeout, 60, fun run_property_testing_case/0}. + +run_property_testing_case() -> + EunitLeader = erlang:group_leader(), + erlang:group_leader(whereis(user), self()), + Res = proper:module(?MODULE, []), + erlang:group_leader(EunitLeader, self()), + ?assertEqual([], Res). From 8186687cc0635b75c7b0a369cbc5c2f0e0ed7aed Mon Sep 17 00:00:00 2001 From: Uvarov Michael Date: Mon, 24 Dec 2012 20:29:34 +0400 Subject: [PATCH 3/5] Clean code. --- test/proper_unicode_tests.erl | 22 +++++++--------------- 1 file changed, 7 insertions(+), 15 deletions(-) diff --git a/test/proper_unicode_tests.erl b/test/proper_unicode_tests.erl index 4cd63f02..bfafe44c 100644 --- a/test/proper_unicode_tests.erl +++ b/test/proper_unicode_tests.erl @@ -26,28 +26,23 @@ prop_unicode_binary() -> unicode:characters_to_list(Bin))) end). + %% Check a binary generator with fixed length. prop_sized_unicode_binary() -> ?FORALL({Len, Bin}, ?LET(Len, byte(), {Len, unicode_binary(Len)}), - begin - equals(Len, length(unicode:characters_to_list(Bin))) - end). + equals(Len, length(unicode:characters_to_list(Bin)))). + +%% Check, that the `characters_to_list/1' does not fail. prop_unicode_string() -> ?FORALL(Str, unicode_string(), - begin -% io:format(user, "~p~n", [Str]), - equals(Str, unicode:characters_to_list( - unicode:characters_to_binary(Str))) - end). + equals(Str, unicode:characters_to_list( + unicode:characters_to_binary(Str)))). prop_unicode_characters() -> ?FORALL(Chars, unicode_characters(), - begin -% io:format(user, "~p~n", [Chars]), - is_binary(unicode:characters_to_binary(Chars)) - end). + is_binary(unicode:characters_to_binary(Chars))). encoding() -> @@ -61,9 +56,6 @@ prop_unicode_external_characters() -> || Encoding <- encoding()]), begin List = unicode:characters_to_list(Chars, Encoding), -% [erlang:error(1) || -% length(List) > 2, hd(tl(List)) > 1000000], -% io:format(user, "~p~n", [Chars]), is_binary(unicode:characters_to_binary(Chars, Encoding)) end). From 67cac31e70fd6a4eb135446fa56004f964ea0522 Mon Sep 17 00:00:00 2001 From: Uvarov Michael Date: Wed, 16 Jan 2013 15:47:34 +0400 Subject: [PATCH 4/5] Add an author tag. --- src/proper_unicode.erl | 14 +++++++++----- test/proper_unicode_tests.erl | 1 + 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/src/proper_unicode.erl b/src/proper_unicode.erl index 202c9f1a..cb827edd 100644 --- a/src/proper_unicode.erl +++ b/src/proper_unicode.erl @@ -1,6 +1,8 @@ %% @doc This module provides basic unicode generators. %% @end %% @todo add `unicode_characters'. +%% +%% @author Uvarov Michael -module(proper_unicode). -export([unicode_char/0, unicode_string/0, @@ -44,13 +46,11 @@ proper_char() -> %% @doc Generate a list of unicode code points. -spec unicode_string() -> generator(). unicode_string() -> - unicode_string(undefined). + list(unicode_char()). %% @doc Generate a list of unicode code points of length `Size'. --spec unicode_string(non_neg_integer() | undefined) -> generator(). -unicode_string(undefined) -> - list(unicode_char()); +-spec unicode_string(non_neg_integer()) -> generator(). unicode_string(Size) -> vector(Size, unicode_char()). @@ -64,7 +64,7 @@ unicode_binary() -> %% @doc Generate an unicode binary binary. -spec unicode_binary(Size | Encoding) -> generator() when - Size :: non_neg_integer() | undefined, + Size :: non_neg_integer(), Encoding :: unicode:encoding(). unicode_binary(Size) when is_integer(Size) -> @@ -79,6 +79,10 @@ unicode_binary(Encoding) -> Size :: non_neg_integer() | undefined, Encoding :: unicode:encoding(). +unicode_binary(undefined, Encoding) -> + ?LET(Str, unicode_string(), + unicode:characters_to_binary(Str, unicode, Encoding)); + unicode_binary(Size, Encoding) -> ?LET(Str, unicode_string(Size), unicode:characters_to_binary(Str, unicode, Encoding)). diff --git a/test/proper_unicode_tests.erl b/test/proper_unicode_tests.erl index bfafe44c..d4961f37 100644 --- a/test/proper_unicode_tests.erl +++ b/test/proper_unicode_tests.erl @@ -1,3 +1,4 @@ +%% @author Uvarov Michael -module(proper_unicode_tests). %% ------------------------------------------------------------------ From c72800cad14c3a05af6e2885ab6f30950891f09e Mon Sep 17 00:00:00 2001 From: Uvarov Michael Date: Wed, 6 Feb 2013 12:51:15 +0400 Subject: [PATCH 5/5] Add a comment and set eunit to be verbose. --- rebar.config | 1 + test/proper_tests.erl | 2 ++ 2 files changed, 3 insertions(+) diff --git a/rebar.config b/rebar.config index d17ac7ca..a715938e 100644 --- a/rebar.config +++ b/rebar.config @@ -28,6 +28,7 @@ "src/proper_transformer.erl", "src/proper_prop_remover.erl", "src/proper_typeserver.erl"]}. +{eunit_opts, [verbose]}. {erl_opts, [debug_info, report_warnings, {warn_format,1}, warn_export_vars, warn_obsolete_guard, warn_unused_import, diff --git a/test/proper_tests.erl b/test/proper_tests.erl index 1bec5d3c..c9d3e4e7 100644 --- a/test/proper_tests.erl +++ b/test/proper_tests.erl @@ -305,6 +305,8 @@ assert_is_pure_function(F) -> %% Unit test arguments %%------------------------------------------------------------------------------ +%% Returns a list of tuples. +%% Each tuple is `{TypeGen, ValidSamples, Target, InvalidSamples, TypeStr}'. simple_types_with_data() -> [{integer(), [-1,0,1,42,-200], 0, [0.3,someatom,<<1>>], "integer()"}, {integer(7,88), [7,8,87,88,23], 7, [1,90,a], "7..88"},