1%%
2%% %CopyrightBegin%
3%%
4%% Copyright Ericsson AB 2017-2018. All Rights Reserved.
5%%
6%% Licensed under the Apache License, Version 2.0 (the "License");
7%% you may not use this file except in compliance with the License.
8%% You may obtain a copy of the License at
9%%
10%%     http://www.apache.org/licenses/LICENSE-2.0
11%%
12%% Unless required by applicable law or agreed to in writing, software
13%% distributed under the License is distributed on an "AS IS" BASIS,
14%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15%% See the License for the specific language governing permissions and
16%% limitations under the License.
17%%
18%% %CopyrightEnd%
19%%
20
21-module(iovec_SUITE).
22
23-export([all/0, suite/0, init_per_suite/1, end_per_suite/1]).
24
25-export([integer_lists/1, binary_lists/1, empty_lists/1, empty_binary_lists/1,
26         mixed_lists/1, improper_lists/1, illegal_lists/1, cons_bomb/1,
27         sub_binary_lists/1, iolist_to_iovec_idempotence/1,
28         iolist_to_iovec_correctness/1, unaligned_sub_binaries/1,
29         direct_binary_arg/1]).
30
31-include_lib("common_test/include/ct.hrl").
32
33suite() ->
34    [{ct_hooks,[ts_install_cth]},
35     {timetrap, {minutes, 2}}].
36
37all() ->
38    [integer_lists, binary_lists, empty_lists, empty_binary_lists, mixed_lists,
39     sub_binary_lists, illegal_lists, improper_lists, cons_bomb,
40     iolist_to_iovec_idempotence, iolist_to_iovec_correctness,
41     unaligned_sub_binaries, direct_binary_arg].
42
43init_per_suite(Config) ->
44    Config.
45
46end_per_suite(Config) ->
47    application:stop(os_mon),
48    Config.
49
50integer_lists(Config) when is_list(Config) ->
51    Variations = gen_variations([I || I <- lists:seq(1, 255)]),
52    equivalence_test(fun erlang:iolist_to_iovec/1, Variations).
53
54sub_binary_lists(Config) when is_list(Config) ->
55    Parent = <<0:256/unit:8, "gazurka">>,
56    <<0:196/unit:8, Child/binary>> = Parent,
57    equivalence_test(fun erlang:iolist_to_iovec/1, gen_variations(Child)).
58
59binary_lists(Config) when is_list(Config) ->
60    Variations = gen_variations([<<I:8>> || I <- lists:seq(1, 255)]),
61    equivalence_test(fun erlang:iolist_to_iovec/1, Variations).
62
63empty_lists(Config) when is_list(Config) ->
64    Variations = gen_variations([[] || _ <- lists:seq(1, 256)]),
65    equivalence_test(fun erlang:iolist_to_iovec/1, Variations),
66    [] = erlang:iolist_to_iovec([]),
67    ok.
68
69empty_binary_lists(Config) when is_list(Config) ->
70    Variations = gen_variations([<<>> || _ <- lists:seq(1, 8192)]),
71    equivalence_test(fun erlang:iolist_to_iovec/1, Variations),
72    [] = erlang:iolist_to_iovec(Variations),
73    ok.
74
75mixed_lists(Config) when is_list(Config) ->
76    Variations = gen_variations([<<>>, lists:seq(1, 40), <<12, 45, 78>>]),
77    equivalence_test(fun erlang:iolist_to_iovec/1, Variations).
78
79illegal_lists(Config) when is_list(Config) ->
80    BitStrs = gen_variations(["gurka", <<1:1>>, "gaffel"]),
81    BadInts = gen_variations(["gurka", 890, "gaffel"]),
82    Atoms = gen_variations([gurka, "gaffel"]),
83    BadTails = [["test" | 0], ["gurka" | gaffel], ["gaffel" | <<1:1>>]],
84
85    Variations =
86        BitStrs ++ BadInts ++ Atoms ++ BadTails,
87
88    illegality_test(fun erlang:iolist_to_iovec/1, Variations).
89
90improper_lists(Config) when is_list(Config) ->
91    Variations = [
92        [[[[1 | <<2>>] | <<3>>] | <<4>>] | <<5>>],
93        [[<<1>>, 2] | <<3, 4, 5>>],
94        [1, 2, 3 | <<4, 5>>]
95    ],
96    equivalence_test(fun erlang:iolist_to_iovec/1, Variations).
97
98cons_bomb(Config) when is_list(Config) ->
99    IntBase = gen_variations([I || I <- lists:seq(1, 255)]),
100    BinBase = gen_variations([<<I:8>> || I <- lists:seq(1, 255)]),
101    MixBase = gen_variations([<<12, 45, 78>>, lists:seq(1, 255)]),
102
103    Variations = gen_variations([IntBase, BinBase, MixBase], 16),
104    equivalence_test(fun erlang:iolist_to_iovec/1, Variations).
105
106iolist_to_iovec_idempotence(Config) when is_list(Config) ->
107    IntVariations = gen_variations([I || I <- lists:seq(1, 255)]),
108    BinVariations = gen_variations([<<I:8>> || I <- lists:seq(1, 255)]),
109    MixVariations = gen_variations([<<12, 45, 78>>, lists:seq(1, 255)]),
110
111    Variations = [IntVariations, BinVariations, MixVariations],
112    Optimized = erlang:iolist_to_iovec(Variations),
113
114    true = Optimized =:= erlang:iolist_to_iovec(Optimized),
115    ok.
116
117iolist_to_iovec_correctness(Config) when is_list(Config) ->
118    IntVariations = gen_variations([I || I <- lists:seq(1, 255)]),
119    BinVariations = gen_variations([<<I:8>> || I <- lists:seq(1, 255)]),
120    MixVariations = gen_variations([<<12, 45, 78>>, lists:seq(1, 255)]),
121
122    Variations = [IntVariations, BinVariations, MixVariations],
123    Optimized = erlang:iolist_to_iovec(Variations),
124
125    true = is_iolist_equal(Optimized, Variations),
126    ok.
127
128unaligned_sub_binaries(Config) when is_list(Config) ->
129    UnalignedBins = [gen_unaligned_binary(I) || I <- lists:seq(32, 4 bsl 10, 512)],
130    UnalignedVariations = gen_variations(UnalignedBins),
131
132    Optimized = erlang:iolist_to_iovec(UnalignedVariations),
133
134    true = is_iolist_equal(Optimized, UnalignedVariations),
135    ok.
136
137direct_binary_arg(Config) when is_list(Config) ->
138    {'EXIT',{badarg, _}} = (catch erlang:iolist_to_iovec(<<1:1>>)),
139    [<<1>>] = erlang:iolist_to_iovec(<<1>>),
140    [] = erlang:iolist_to_iovec(<<>>),
141    ok.
142
143illegality_test(Fun, Variations) ->
144    [{'EXIT',{badarg, _}} = (catch Fun(Variation)) || Variation <- Variations],
145    ok.
146
147equivalence_test(Fun, [Head | _] = Variations) ->
148    %% Check that each variation is equal to the others, and that the sum of
149    %% them is equal to the input.
150    Comparand = Fun(Head),
151    [true = is_iolist_equal(Comparand, Fun(V)) || V <- Variations],
152    true = is_iolist_equal(Variations, Fun(Variations)),
153    ok.
154
155is_iolist_equal(A, B) ->
156    iolist_to_binary(A) =:= iolist_to_binary(B).
157
158gen_unaligned_binary(Size) ->
159    Bin0 = << <<I>> || I <- lists:seq(1, Size) >>,
160    <<0:3,Bin:Size/binary,31:5>> = id(<<0:3,Bin0/binary,31:5>>),
161    Bin.
162
163id(I) -> I.
164
165%% Generates a bunch of lists whose contents will be equal to Base repeated a
166%% few times. The lists only differ by their structure, so their reduction to
167%% a simpler format should yield the same result.
168gen_variations(Base) ->
169    gen_variations(Base, 12).
170gen_variations(Base, N) ->
171    [gen_flat_list(Base, N),
172     gen_nested_list(Base, N),
173     gen_nasty_list(Base, N)].
174
175gen_flat_list(Base, N) ->
176    lists:flatten(gen_nested_list(Base, N)).
177
178gen_nested_list(Base, N) ->
179    [Base || _ <- lists:seq(1, N)].
180
181gen_nasty_list(Base, N) ->
182    gen_nasty_list_1(gen_nested_list(Base, N), []).
183gen_nasty_list_1([], Result) ->
184    Result;
185gen_nasty_list_1([Head | Base], Result) when is_list(Head) ->
186    gen_nasty_list_1(Base, [[Result], [gen_nasty_list_1(Head, [])]]);
187gen_nasty_list_1([Head | Base], Result) ->
188    gen_nasty_list_1(Base, [[Result], [Head]]).
189