Sign Up
Log In
Log In
or
Sign Up
Places
All Projects
Status Monitor
Collapse sidebar
home:Ledest:erlang:24
erlang
4221-Property-based-tests-for-the-base64-module...
Overview
Repositories
Revisions
Requests
Users
Attributes
Meta
File 4221-Property-based-tests-for-the-base64-module.patch of Package erlang
From c43bac27631e9bc56732b76d442e3d133ebcd742 Mon Sep 17 00:00:00 2001 From: Maria Scott <maria-12648430@hnc-agency.org> Date: Mon, 16 May 2022 21:27:46 +0200 Subject: [PATCH] Property-based tests for the base64 module --- lib/stdlib/test/Makefile | 1 + .../test/base64_property_test_SUITE.erl | 86 ++++ lib/stdlib/test/property_test/base64_prop.erl | 429 ++++++++++++++++++ 3 files changed, 516 insertions(+) create mode 100644 lib/stdlib/test/base64_property_test_SUITE.erl create mode 100644 lib/stdlib/test/property_test/base64_prop.erl diff --git a/lib/stdlib/test/Makefile b/lib/stdlib/test/Makefile index 9e6df4c542..f030235046 100644 --- a/lib/stdlib/test/Makefile +++ b/lib/stdlib/test/Makefile @@ -8,6 +8,7 @@ include $(ERL_TOP)/make/$(TARGET)/otp.mk MODULES= \ array_SUITE \ base64_SUITE \ + base64_property_test_SUITE \ beam_lib_SUITE \ binary_module_SUITE \ binref \ diff --git a/lib/stdlib/test/base64_property_test_SUITE.erl b/lib/stdlib/test/base64_property_test_SUITE.erl new file mode 100644 index 0000000000..3802b68ce3 --- /dev/null +++ b/lib/stdlib/test/base64_property_test_SUITE.erl @@ -0,0 +1,86 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2021. All Rights Reserved. +%% +%% 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. +%% +%% %CopyrightEnd% +%% +-module(base64_property_test_SUITE). + +-include_lib("common_test/include/ct.hrl"). +-compile([export_all, nowarn_export_all]). + +all() -> + [ + encode_case, + encode_to_string_case, + decode_case, + decode_malformed_case, + decode_noisy_case, + decode_to_string_case, + decode_to_string_malformed_case, + decode_to_string_noisy_case, + mime_decode_case, + mime_decode_malformed_case, + mime_decode_to_string_case, + mime_decode_to_string_malformed_case + ]. + +init_per_suite(Config) -> + ct_property_test:init_per_suite(Config). + +end_per_suite(Config) -> + Config. + +encode_case(Config) -> + do_proptest(prop_encode, Config). + +encode_to_string_case(Config) -> + do_proptest(prop_encode_to_string, Config). + +decode_case(Config) -> + do_proptest(prop_decode, Config). + +decode_malformed_case(Config) -> + do_proptest(prop_decode_malformed, Config). + +decode_noisy_case(Config) -> + do_proptest(prop_decode_noisy, Config). + +decode_to_string_case(Config) -> + do_proptest(prop_decode_to_string, Config). + +decode_to_string_malformed_case(Config) -> + do_proptest(prop_decode_to_string_malformed, Config). + +decode_to_string_noisy_case(Config) -> + do_proptest(prop_decode_to_string_noisy, Config). + +mime_decode_case(Config) -> + do_proptest(prop_mime_decode, Config). + +mime_decode_malformed_case(Config) -> + do_proptest(prop_mime_decode_malformed, Config). + +mime_decode_to_string_case(Config) -> + do_proptest(prop_mime_decode_to_string, Config). + +mime_decode_to_string_malformed_case(Config) -> + do_proptest(prop_mime_decode_to_string_malformed, Config). + +do_proptest(Prop, Config) -> + ct_property_test:quickcheck( + base64_prop:Prop(), + Config). diff --git a/lib/stdlib/test/property_test/base64_prop.erl b/lib/stdlib/test/property_test/base64_prop.erl new file mode 100644 index 0000000000..6ab7e4c68a --- /dev/null +++ b/lib/stdlib/test/property_test/base64_prop.erl @@ -0,0 +1,429 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2021. All Rights Reserved. +%% +%% 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. +%% +%% %CopyrightEnd% +%% +-module(base64_prop). + +-compile([export_all, nowarn_export_all]). + +-proptest(eqc). +-proptest([triq, proper]). + +-ifndef(EQC). +-ifndef(PROPER). +-ifndef(TRIQ). +-define(EQC, true). +-endif. +-endif. +-endif. + +-ifdef(EQC). +-include_lib("eqc/include/eqc.hrl"). +-define(MOD_eqc,eqc). + +-else. +-ifdef(PROPER). +-include_lib("proper/include/proper.hrl"). +-define(MOD_eqc,proper). + +-else. +-ifdef(TRIQ). +-define(MOD_eqc,triq). +-include_lib("triq/include/triq.hrl"). + +-endif. +-endif. +-endif. + +%%%%%%%%%%%%%%%%%% +%%% Properties %%% +%%%%%%%%%%%%%%%%%% + +prop_encode() -> + ?FORALL( + Str, + oneof([list(byte()), binary()]), + begin + Enc = base64:encode(Str), + Dec = base64:decode(Enc), + is_b64_binary(Enc) andalso str_equals(Str, Dec) + end + ). + +prop_encode_to_string() -> + ?FORALL( + Str, + oneof([list(byte()), binary()]), + begin + Enc = base64:encode_to_string(Str), + Dec = base64:decode_to_string(Enc), + is_b64_string(Enc) andalso str_equals(Str, Dec) + end + ). + +prop_decode() -> + ?FORALL( + {NormalizedB64, WspedB64}, + wsped_b64(), + begin + Dec = base64:decode(WspedB64), + Enc = base64:encode(Dec), + is_binary(Dec) andalso b64_equals(NormalizedB64, Enc) + end + ). + +prop_decode_malformed() -> + common_decode_malformed(wsped_b64(), fun base64:decode/1). + +prop_decode_noisy() -> + common_decode_noisy(fun base64:decode/1). + +prop_decode_to_string() -> + ?FORALL( + {NormalizedB64, WspedB64}, + wsped_b64(), + begin + Dec = base64:decode_to_string(WspedB64), + Enc = base64:encode(Dec), + is_bytelist(Dec) andalso b64_equals(NormalizedB64, Enc) + end + ). + +prop_decode_to_string_malformed() -> + common_decode_malformed(wsped_b64(), fun base64:decode_to_string/1). + +prop_decode_to_string_noisy() -> + common_decode_noisy(fun base64:decode_to_string/1). + +prop_mime_decode() -> + ?FORALL( + {NormalizedB64, NoisyB64}, + noisy_b64(), + begin + Dec = base64:mime_decode(NoisyB64), + Enc = base64:encode(Dec), + is_binary(Dec) andalso b64_equals(NormalizedB64, Enc) + end + ). + +prop_mime_decode_malformed() -> + common_decode_malformed(noisy_b64(), fun base64:mime_decode/1). + +prop_mime_decode_to_string() -> + ?FORALL( + {NormalizedB64, NoisyB64}, + noisy_b64(), + begin + Dec = base64:mime_decode_to_string(NoisyB64), + Enc = base64:encode(Dec), + is_bytelist(Dec) andalso b64_equals(NormalizedB64, Enc) + end + ). + +prop_mime_decode_to_string_malformed() -> + common_decode_malformed(noisy_b64(), fun base64:mime_decode_to_string/1). + +common_decode_noisy(Fn) -> + ?FORALL( + {_, NoisyB64}, + ?SUCHTHAT({NormalizedB64, NoisyB64}, noisy_b64(), NormalizedB64 =/= NoisyB64), + try + Fn(NoisyB64) + of + _ -> + false + catch + error:_ -> + true + end + ). + +common_decode_malformed(Gen, Fn) -> + ?FORALL( + MalformedB64, + ?LET( + {{NormalizedB64, NoisyB64}, Malformings}, + { + Gen, + oneof( + [ + [b64_char()], + [b64_char(), b64_char()], + [b64_char(), b64_char(), b64_char()] + ] + ) + }, + {NormalizedB64, insert_noise(NoisyB64, Malformings)} + ), + try + Fn(MalformedB64) + of + _ -> + false + catch + error:_ -> + true + end + ). + +%%%%%%%%%%%%%%%%%% +%%% Generators %%% +%%%%%%%%%%%%%%%%%% + +%% Generate a single character from the base64 alphabet. +b64_char() -> + oneof(b64_chars()). + +%% Generate a string of characters from the base64 alphabet, +%% including padding if needed. +b64_string() -> + ?LET( + {L, Filler}, + {list(b64_char()), b64_char()}, + case length(L) rem 4 of + 0 -> L; + 1 -> L ++ [Filler, $=, $=]; + 2 -> L ++ [$=, $=]; + 3 -> L ++ [$=] + end + ). + +%% Generate a binary of characters from the base64 alphabet, +%% including padding if needed. +b64_binary() -> + ?LET( + L, + b64_string(), + list_to_binary(L) + ). + +%% Generate a string or binary of characters from the +%% base64 alphabet, including padding if needed. +b64() -> + oneof([b64_string(), b64_binary()]). + +%% Generate a string or binary of characters from the +%% base64 alphabet, including padding if needed, with +%% whitespaces inserted at random indexes. +wsped_b64() -> + ?LET( + {B64, Wsps}, + {b64(), list(oneof([$\t, $\r, $\n, $\s]))}, + {B64, insert_noise(B64, Wsps)} + ). + +%% Generate a single character outside of the base64 alphabet. +%% As whitespaces are allowed but ignored in base64, this generator +%% will produce no whitespaces, either. +non_b64_char() -> + oneof(lists:seq(16#00, 16#FF) -- b64_allowed_chars()). + +%% Generate a string or binary of characters from the +%% base64 alphabet, including padding if needed, with +%% whitespaces and non-base64 ("invalid") characters +%% inserted at random indexes. +noisy_b64() -> + ?LET( + {{B64, WspedB64}, Noise}, + {wsped_b64(), non_empty(list(non_b64_char()))}, + {B64, insert_noise(WspedB64, Noise)} + ). + +%%%%%%%%%%%%%%% +%%% Helpers %%% +%%%%%%%%%%%%%%% + +%% The characters of the base64 alphabet. +%% "=" is not included, as it is special in that it +%% may only appear at the end of a base64 encoded string +%% for padding. +b64_chars() -> + lists:seq($0, $9) ++ + lists:seq($a, $z) ++ + lists:seq($A, $Z) ++ + [$+, $/]. + +%% In addition to the above, the whitespace characters +%% HTAB, CR, LF and SP are allowed to appear in a base64 +%% encoded string and should be ignored. +b64_allowed_chars() -> + [$\t, $\r, $\n, $\s | b64_chars()]. + +%% Insert the given list of noise characters at random +%% places into the given base64 string. +insert_noise(B64, []) -> + B64; +insert_noise([], Noise) -> + Noise; +insert_noise(<<>>, Noise) -> + list_to_binary(Noise); +insert_noise([B|Bs] = B64, [N|Ns] = Noise) -> + case rand:uniform(2) of + 1 -> + [B|insert_noise(Bs, Noise)]; + 2 -> + [N|insert_noise(B64, Ns)] + end; +insert_noise(<<B, Bs/binary>> = B64, [N|Ns] = Noise) -> + case rand:uniform(2) of + 1 -> + <<B, (insert_noise(Bs, Noise))/binary>>; + 2 -> + <<N, (insert_noise(B64, Ns))/binary>> + end. + +%% Check if the given character is in the base64 alphabet. +%% This does not include the padding character "=". +is_b64_char($+) -> + true; +is_b64_char($/) -> + true; +is_b64_char(C) when C >= $0, C =< $9 -> + true; +is_b64_char(C) when C >= $A, C =< $Z -> + true; +is_b64_char(C) when C >= $a, C =< $z -> + true; +is_b64_char(_) -> + false. + +%% Check if the given argument is a base64 binary, +%% ie that it consists of quadruplets of characters +%% from the base64 alphabet, whereas the last quadruplet +%% may be padded with one or two "="s +is_b64_binary(B) -> + is_b64_binary(B, 0). + +is_b64_binary(<<>>, N) -> + N rem 4 =:= 0; +is_b64_binary(<<$=>>, N) -> + N rem 4 =:= 3; +is_b64_binary(<<$=, $=>>, N) -> + N rem 4 =:= 2; +is_b64_binary(<<C, More/binary>>, N) -> + case is_b64_char(C) of + true -> + is_b64_binary(More, N + 1); + false -> + false + end. + +%% Check if the given argument is a base64 string +%% (see is_b64_binary/1) +is_b64_string(S) -> + is_b64_binary(list_to_binary(S)). + +%% Check if the argument is a list of bytes. +is_bytelist(L) -> + lists:all( + fun (B) -> + is_integer(B) andalso B >= 16#00 andalso B =< 16#FF + end, + L + ). + +%% Check two byte-lists or binaries for equality. +str_equals(Str1, Str2) when is_list(Str1) -> + str_equals(list_to_binary(Str1), Str2); +str_equals(Str1, Str2) when is_list(Str2) -> + str_equals(Str1, list_to_binary(Str2)); +str_equals(Str1, Str2) when is_binary(Str1), is_binary(Str2) -> + Str1 =:= Str2. + +%% Check two base64-encoded byte-lists or binaries for equality. +%% Assumes that the given arguments are in a normalized form, +%% ie that they consist only of characters from the base64 +%% alphabet and possible padding ("="). +b64_equals(L, B) when is_list(L) -> + b64_equals(list_to_binary(L), B); +b64_equals(B, L) when is_list(L) -> + b64_equals(B, list_to_binary(L)); +b64_equals(B1, B2) when is_binary(B1), is_binary(B2) -> + b64_equals1(B1, B2). + +b64_equals1(<<Eq:4/bytes>>, <<Eq:4/bytes>>) -> + is_b64_binary(Eq); +b64_equals1(<<Eq:4/bytes, More1/binary>>, <<Eq:4/bytes, More2/binary>>) -> + case lists:all(fun is_b64_char/1, binary_to_list(Eq)) of + true -> + b64_equals1(More1, More2); + false -> + false + end; +b64_equals1(<<Eq, B1, $=, $=>>, <<Eq, B2, $=, $=>>) -> + %% If the encoded string ends with "==", there exist multiple + %% possibilities for the character preceding the "==" as only the + %% 3rd and 4th bits of the encoded byte represented by that + %% character are significant. + %% + %% For example, all of the encoded strings "QQ==", "QR==", ..., "QZ==" + %% decode to the string "A", since all the bytes represented by Q to Z + %% are the same in the significant 3rd and 4th bit. + case is_b64_char(Eq) of + true -> + Normalize = fun + (C) when C >= $A, C =< $P -> $A; + (C) when C >= $Q, C =< $Z -> $Q; + (C) when C >= $a, C =< $f -> $Q; + (C) when C >= $g, C =< $v -> $g; + (C) when C >= $w, C =< $z -> $w; + (C) when C >= $0, C =< $9 -> $w; + ($+) -> $w; + ($/) -> $w + end, + Normalize(B1) =:= Normalize(B2); + false -> + false + end; +b64_equals1(<<Eq:2/bytes, B1, $=>>, <<Eq:2/bytes, B2, $=>>) -> + %% Similar to the above, but with the encoded string ending with a + %% single "=" the 3rd to 6th bits of the encoded byte are significant, + %% such that, for example, all the encoded strings "QUE=" to "QUH=" + %% decode to the same string "AA". + <<Eq1, Eq2>> = Eq, + case is_b64_char(Eq1) andalso is_b64_char(Eq2) of + true -> + Normalize = fun + (C) when C >= $A, C =< $D -> $A; + (C) when C >= $E, C =< $H -> $E; + (C) when C >= $I, C =< $L -> $I; + (C) when C >= $M, C =< $P -> $M; + (C) when C >= $Q, C =< $T -> $Q; + (C) when C >= $U, C =< $X -> $U; + (C) when C >= $Y, C =< $Z -> $Y; + (C) when C >= $a, C =< $b -> $Y; + (C) when C >= $c, C =< $f -> $c; + (C) when C >= $g, C =< $j -> $g; + (C) when C >= $k, C =< $n -> $k; + (C) when C >= $o, C =< $r -> $o; + (C) when C >= $s, C =< $v -> $s; + (C) when C >= $w, C =< $z -> $w; + (C) when C >= $0, C =< $3 -> $0; + (C) when C >= $4, C =< $7 -> $4; + (C) when C >= $8, C =< $9 -> $8; + ($+) -> $8; + ($/) -> $8 + end, + Normalize(B1) =:= Normalize(B2); + false -> + false + end; +b64_equals1(<<>>, <<>>) -> + true; +b64_equals1(_, _) -> + false. -- 2.35.3
Locations
Projects
Search
Status Monitor
Help
OpenBuildService.org
Documentation
API Documentation
Code of Conduct
Contact
Support
@OBShq
Terms
openSUSE Build Service is sponsored by
The Open Build Service is an
openSUSE project
.
Sign Up
Log In
Places
Places
All Projects
Status Monitor