1: %% -*- coding: utf-8 -*-
    2: %%
    3: %% %CopyrightBegin%
    4: %% 
    5: %% Copyright Ericsson AB 2007-2012. All Rights Reserved.
    6: %% 
    7: %% The contents of this file are subject to the Erlang Public License,
    8: %% Version 1.1, (the "License"); you may not use this file except in
    9: %% compliance with the License. You should have received a copy of the
   10: %% Erlang Public License along with this software. If not, it can be
   11: %% retrieved online at http://www.erlang.org/.
   12: %% 
   13: %% Software distributed under the License is distributed on an "AS IS"
   14: %% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
   15: %% the License for the specific language governing rights and limitations
   16: %% under the License.
   17: %% 
   18: %% %CopyrightEnd%
   19: %%
   20: 
   21: -module(base64_SUITE).
   22: 
   23: -include_lib("common_test/include/ct.hrl").
   24: 
   25: %% Test server specific exports
   26: -export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, 
   27: 	 init_per_group/2,end_per_group/2, 
   28: 	 init_per_testcase/2, end_per_testcase/2]).
   29: 
   30: %% Test cases must be exported.
   31: -export([base64_encode/1, base64_decode/1, base64_otp_5635/1,
   32: 	 base64_otp_6279/1, big/1, illegal/1, mime_decode/1,
   33: 	 mime_decode_to_string/1, roundtrip/1]).
   34: 
   35: init_per_testcase(_, Config) ->
   36:     Dog = test_server:timetrap(?t:minutes(4)),
   37:     NewConfig = lists:keydelete(watchdog, 1, Config),
   38:     [{watchdog, Dog} | NewConfig].
   39: 
   40: end_per_testcase(_, Config) ->
   41:     Dog = ?config(watchdog, Config),
   42:     test_server:timetrap_cancel(Dog),
   43:     ok.
   44: 
   45: %%-------------------------------------------------------------------------
   46: %% Test cases starts here.
   47: %%-------------------------------------------------------------------------
   48: suite() -> [{ct_hooks,[ts_install_cth]}].
   49: 
   50: all() -> 
   51:     [base64_encode, base64_decode, base64_otp_5635,
   52:      base64_otp_6279, big, illegal, mime_decode, mime_decode_to_string,
   53:      roundtrip].
   54: 
   55: groups() -> 
   56:     [].
   57: 
   58: init_per_suite(Config) ->
   59:     Config.
   60: 
   61: end_per_suite(_Config) ->
   62:     ok.
   63: 
   64: init_per_group(_GroupName, Config) ->
   65:     Config.
   66: 
   67: end_per_group(_GroupName, Config) ->
   68:     Config.
   69: 
   70: 
   71: 
   72: %%-------------------------------------------------------------------------
   73: base64_encode(doc) ->
   74:     ["Test base64:encode/1."];
   75: base64_encode(suite) ->
   76:     [];
   77: base64_encode(Config) when is_list(Config) ->
   78:     %% Two pads
   79:     <<"QWxhZGRpbjpvcGVuIHNlc2FtZQ==">> =
   80: 	base64:encode("Aladdin:open sesame"),
   81:     %% One pad
   82:     <<"SGVsbG8gV29ybGQ=">> = base64:encode(<<"Hello World">>),
   83:     %% No pad
   84:     "QWxhZGRpbjpvcGVuIHNlc2Ft" = 
   85: 	base64:encode_to_string("Aladdin:open sesam"),
   86:     
   87:     "MDEyMzQ1Njc4OSFAIzBeJiooKTs6PD4sLiBbXXt9" =
   88: 	base64:encode_to_string(<<"0123456789!@#0^&*();:<>,. []{}">>),
   89:     ok.
   90: %%-------------------------------------------------------------------------
   91: base64_decode(doc) ->
   92:     ["Test base64:decode/1."];
   93: base64_decode(suite) ->
   94:     [];
   95: base64_decode(Config) when is_list(Config) ->
   96:     %% Two pads
   97:     <<"Aladdin:open sesame">> =
   98: 	base64:decode("QWxhZGRpbjpvcGVuIHNlc2FtZQ=="),
   99:     %% One pad
  100:     <<"Hello World">> = base64:decode(<<"SGVsbG8gV29ybGQ=">>),
  101:     %% No pad
  102:     <<"Aladdin:open sesam">> = 
  103: 	base64:decode("QWxhZGRpbjpvcGVuIHNlc2Ft"),
  104: 
  105:     Alphabet = list_to_binary(lists:seq(0, 255)),
  106:     Alphabet = base64:decode(base64:encode(Alphabet)),
  107: 
  108:     %% Encoded base 64 strings may be devided by non base 64 chars.
  109:     %% In this cases whitespaces.
  110:     "0123456789!@#0^&*();:<>,. []{}" =
  111: 	base64:decode_to_string(
  112: 	  "MDEy MzQ1Njc4 \tOSFAIzBeJ \niooKTs6 PD4sLi \r\nBbXXt9"),
  113:     "0123456789!@#0^&*();:<>,. []{}" =
  114: 	base64:decode_to_string(
  115: 	  <<"MDEy MzQ1Njc4 \tOSFAIzBeJ \niooKTs6 PD4sLi \r\nBbXXt9">>),
  116:     ok.
  117: %%-------------------------------------------------------------------------
  118: base64_otp_5635(doc) ->
  119:     ["OTP-5635: Some data doesn't pass through base64:decode/1 "
  120:      "correctly"];
  121: base64_otp_5635(suite) ->
  122:     [];
  123: base64_otp_5635(Config) when is_list(Config) ->
  124:     <<"===">> = base64:decode(base64:encode("===")),
  125:     ok.
  126: %%-------------------------------------------------------------------------
  127: base64_otp_6279(doc) ->
  128:     ["OTP-6279: Guard needed so that function fails in a correct"
  129:      "way for faulty input i.e. function_clause"];
  130: base64_otp_6279(suite) ->
  131:     [];
  132: base64_otp_6279(Config) when is_list(Config) ->
  133:     {'EXIT',{function_clause, _}} = (catch base64:decode("dGVzda==a")),
  134:     ok.
  135: %%-------------------------------------------------------------------------
  136: big(doc) ->
  137:     ["Encode and decode big binaries."];
  138: big(suite) ->
  139:     [];
  140: big(Config) when is_list(Config) ->
  141:     Big = make_big_binary(300000),
  142:     B = base64:encode(Big),
  143:     true = is_binary(B),
  144:     400000 = byte_size(B),
  145:     Big = base64:decode(B),
  146:     Big = base64:mime_decode(B),
  147:     ok.
  148: %%-------------------------------------------------------------------------
  149: illegal(doc) ->
  150:     ["Make sure illegal characters are rejected when decoding."];
  151: illegal(suite) ->
  152:     [];
  153: illegal(Config) when is_list(Config) ->
  154:     {'EXIT',{function_clause, _}} = (catch base64:decode("()")),
  155:     ok.
  156: %%-------------------------------------------------------------------------
  157: %% mime_decode and mime_decode_to_string have different implementations
  158: %% so test both with the same input separately. Both functions have
  159: %% the same implementation for binary/string arguments.
  160: mime_decode(doc) ->
  161:     ["Test base64:mime_decode/1."];
  162: mime_decode(suite) ->
  163:     [];
  164: mime_decode(Config) when is_list(Config) ->
  165:     %% Test correct padding
  166:     <<"one">> = base64:mime_decode(<<"b25l">>),
  167:     <<"on">>  = base64:mime_decode(<<"b24=">>),
  168:     <<"o">>   = base64:mime_decode(<<"bw==">>),
  169:     %% Test 1 extra padding
  170:     <<"one">> = base64:mime_decode(<<"b25l= =">>),
  171:     <<"on">>  = base64:mime_decode(<<"b24== =">>),
  172:     <<"o">>   = base64:mime_decode(<<"bw=== =">>),
  173:     %% Test 2 extra padding
  174:     <<"one">> = base64:mime_decode(<<"b25l===">>),
  175:     <<"on">>  = base64:mime_decode(<<"b24====">>),
  176:     <<"o">>   = base64:mime_decode(<<"bw=====">>),
  177:     %% Test misc embedded padding
  178:     <<"one">> = base64:mime_decode(<<"b2=5l===">>),
  179:     <<"on">>  = base64:mime_decode(<<"b=24====">>),
  180:     <<"o">>   = base64:mime_decode(<<"b=w=====">>),
  181:     %% Test misc white space and illegals with embedded padding
  182:     <<"one">> = base64:mime_decode(<<" b~2=\r\n5()l===">>),
  183:     <<"on">>  = base64:mime_decode(<<"\tb =2\"¤4=¤=   ==">>),
  184:     <<"o">>   = base64:mime_decode(<<"\nb=w=====">>),
  185:     %% Two pads
  186:     <<"Aladdin:open sesame">> =
  187: 	base64:mime_decode("QWxhZGRpbjpvc()GVuIHNlc2FtZQ=="),
  188:     %% One pad to ignore, followed by more text
  189:     <<"Hello World!!">> = base64:mime_decode(<<"SGVsb)(G8gV29ybGQ=h IQ= =">>),
  190:     %% No pad
  191:     <<"Aladdin:open sesam">> =
  192: 	base64:mime_decode("QWxhZGRpbjpvcG¤\")(VuIHNlc2Ft"),
  193:     %% Encoded base 64 strings may be divided by non base 64 chars.
  194:     %% In this cases whitespaces.
  195:     <<"0123456789!@#0^&*();:<>,. []{}">> =
  196: 	base64:mime_decode(
  197: 	  <<"MDEy MzQ1Njc4 \tOSFAIzBeJ \nio)(oKTs6 PD4sLi \r\nBbXXt9">>),
  198:     ok.
  199: 
  200: %%-------------------------------------------------------------------------
  201: 
  202: %% Repeat of mime_decode() tests
  203: mime_decode_to_string(doc) ->
  204:     ["Test base64:mime_decode_to_string/1."];
  205: mime_decode_to_string(suite) ->
  206:     [];
  207: mime_decode_to_string(Config) when is_list(Config) ->
  208:     %% Test correct padding
  209:     "one" = base64:mime_decode_to_string(<<"b25l">>),
  210:     "on"  = base64:mime_decode_to_string(<<"b24=">>),
  211:     "o"   = base64:mime_decode_to_string(<<"bw==">>),
  212:     %% Test 1 extra padding
  213:     "one" = base64:mime_decode_to_string(<<"b25l= =">>),
  214:     "on"  = base64:mime_decode_to_string(<<"b24== =">>),
  215:     "o"   = base64:mime_decode_to_string(<<"bw=== =">>),
  216:     %% Test 2 extra padding
  217:     "one" = base64:mime_decode_to_string(<<"b25l===">>),
  218:     "on"  = base64:mime_decode_to_string(<<"b24====">>),
  219:     "o"   = base64:mime_decode_to_string(<<"bw=====">>),
  220:     %% Test misc embedded padding
  221:     "one" = base64:mime_decode_to_string(<<"b2=5l===">>),
  222:     "on"  = base64:mime_decode_to_string(<<"b=24====">>),
  223:     "o"   = base64:mime_decode_to_string(<<"b=w=====">>),
  224:     %% Test misc white space and illegals with embedded padding
  225:     "one" = base64:mime_decode_to_string(<<" b~2=\r\n5()l===">>),
  226:     "on"  = base64:mime_decode_to_string(<<"\tb =2\"¤4=¤=   ==">>),
  227:     "o"   = base64:mime_decode_to_string(<<"\nb=w=====">>),
  228:     %% Two pads
  229:     "Aladdin:open sesame" =
  230: 	base64:mime_decode_to_string("QWxhZGRpbjpvc()GVuIHNlc2FtZQ=="),
  231:     %% One pad to ignore, followed by more text
  232:     "Hello World!!" = base64:mime_decode_to_string(<<"SGVsb)(G8gV29ybGQ=h IQ= =">>),
  233:     %% No pad
  234:     "Aladdin:open sesam" = 
  235: 	base64:mime_decode_to_string("QWxhZGRpbjpvcG¤\")(VuIHNlc2Ft"),
  236:     %% Encoded base 64 strings may be divided by non base 64 chars.
  237:     %% In this cases whitespaces.
  238:     "0123456789!@#0^&*();:<>,. []{}" =
  239: 	base64:mime_decode_to_string(
  240: 	  <<"MDEy MzQ1Njc4 \tOSFAIzBeJ \nio)(oKTs6 PD4sLi \r\nBbXXt9">>),
  241:     ok.
  242: 
  243: %%-------------------------------------------------------------------------
  244: 
  245: roundtrip(Config) when is_list(Config) ->
  246:     Sizes = lists:seq(1, 255) ++ lists:seq(2400-5, 2440),
  247:     roundtrip_1(Sizes, []).
  248: 
  249: roundtrip_1([NextSize|Sizes], Current) ->
  250:     Len = length(Current),
  251:     io:format("~p", [Len]),
  252:     do_roundtrip(Current),
  253:     Next = random_byte_list(NextSize - Len, Current),
  254:     roundtrip_1(Sizes, Next);
  255: roundtrip_1([], Last) ->
  256:     io:format("~p", [length(Last)]),
  257:     do_roundtrip(Last).
  258: 
  259: do_roundtrip(List) ->
  260:     Bin = list_to_binary(List),
  261:     Base64Bin = base64:encode(List),
  262:     Base64Bin = base64:encode(Bin),
  263:     Base64List = base64:encode_to_string(List),
  264:     Base64Bin = list_to_binary(Base64List),
  265:     Bin = base64:decode(Base64Bin),
  266:     List = base64:decode_to_string(Base64Bin),
  267:     Bin = base64:mime_decode(Base64Bin),
  268:     List = base64:mime_decode_to_string(Base64Bin),
  269:     append_roundtrip(8, Bin, List, Base64Bin),
  270:     prepend_roundtrip(8, Bin, List, Base64List),
  271:     interleaved_ws_roundtrip(Bin, List, Base64List).
  272: 
  273: append_roundtrip(0, _, _, _) -> ok;
  274: append_roundtrip(N, Bin, List, Base64Bin0) ->
  275:     Base64Bin = <<Base64Bin0/binary,"\n">>,
  276:     Bin = base64:decode(Base64Bin),
  277:     List = base64:decode_to_string(Base64Bin),
  278:     Bin = base64:mime_decode(Base64Bin),
  279:     List = base64:mime_decode_to_string(Base64Bin),
  280: 
  281:     Base64List = binary_to_list(Base64Bin),
  282:     Bin = base64:decode(Base64List),
  283:     List = base64:decode_to_string(Base64List),
  284:     Bin = base64:mime_decode(Base64List),
  285:     List = base64:mime_decode_to_string(Base64List),
  286:     append_roundtrip(N-1, Bin, List, Base64Bin).
  287: 
  288: prepend_roundtrip(0, _, _, _) -> ok;
  289: prepend_roundtrip(N, Bin, List, Base64List0) ->
  290:     Base64List = [$\s|Base64List0],
  291:     Bin = base64:decode(Base64List),
  292:     List = base64:decode_to_string(Base64List),
  293:     Bin = base64:mime_decode(Base64List),
  294:     List = base64:mime_decode_to_string(Base64List),
  295: 
  296:     Base64Bin = list_to_binary(Base64List),
  297:     Bin = base64:decode(Base64Bin),
  298:     List = base64:decode_to_string(Base64Bin),
  299:     Bin = base64:mime_decode(Base64Bin),
  300:     List = base64:mime_decode_to_string(Base64Bin),
  301:     prepend_roundtrip(N-1, Bin, List, Base64List).
  302: 
  303: %% Do an exhaustive test of interleaving whitespace (for short strings).
  304: interleaved_ws_roundtrip(Bin, List, Base64List) when byte_size(Bin) =< 6 ->
  305:     interleaved_ws_roundtrip_1(lists:reverse(Base64List), [], Bin, List);
  306: interleaved_ws_roundtrip(_, _, _) -> ok.
  307: 
  308: interleaved_ws_roundtrip_1([H|T], Tail, Bin, List) ->
  309:     interleaved_ws_roundtrip_1(T, [H|Tail], Bin, List),
  310:     interleaved_ws_roundtrip_1(T, [H,$\s|Tail], Bin, List),
  311:     interleaved_ws_roundtrip_1(T, [H,$\s,$\t|Tail], Bin, List),
  312:     interleaved_ws_roundtrip_1(T, [H,$\n,$\t|Tail], Bin, List);
  313: interleaved_ws_roundtrip_1([], Base64List, Bin, List) ->
  314:     Bin = base64:decode(Base64List),
  315:     List = base64:decode_to_string(Base64List),
  316:     Bin = base64:mime_decode(Base64List),
  317:     List = base64:mime_decode_to_string(Base64List),
  318: 
  319:     Base64Bin = list_to_binary(Base64List),
  320:     Bin = base64:decode(Base64Bin),
  321:     List = base64:decode_to_string(Base64Bin),
  322:     Bin = base64:mime_decode(Base64Bin),
  323:     List = base64:mime_decode_to_string(Base64Bin),
  324:     ok.
  325: 
  326: random_byte_list(0, Acc) ->
  327:     Acc;
  328: random_byte_list(N, Acc) -> 
  329:     random_byte_list(N-1, [random:uniform(255)|Acc]).
  330: 
  331: make_big_binary(N) ->
  332:     list_to_binary(mbb(N, [])).
  333: 
  334: mbb(N, Acc) when N > 256 ->
  335:     B = list_to_binary(lists:seq(0, 255)),
  336:     mbb(N - 256, [B | Acc]);
  337: mbb(N, Acc) ->
  338:     B = list_to_binary(lists:seq(0, N-1)),
  339:     lists:reverse(Acc, B).