summaryrefslogtreecommitdiff
path: root/lib/erl
diff options
context:
space:
mode:
authoralisdair sullivan <alisdairsullivan@yahoo.ca>2014-11-17 20:28:35 -0800
committerNobuaki Sukegawa <nsuke@apache.org>2015-11-01 18:03:40 +0900
commita559f8d903074afa76b4e9255e0d883b7401bf95 (patch)
treeff4bccf00bfeefceab0212aecef2d8e0a5eb9e80 /lib/erl
parent826ea998d5590247a00a3aea09026a9d7518d1f7 (diff)
downloadthrift-a559f8d903074afa76b4e9255e0d883b7401bf95.tar.gz
THRIFT-2856 refactor erlang basic transports and unify interfaces
Client: Erlang Patch: Alisdair Sullivan This closes #288
Diffstat (limited to 'lib/erl')
-rw-r--r--lib/erl/Makefile.am4
-rw-r--r--lib/erl/rebar.test.config5
-rw-r--r--lib/erl/src/thrift.app.src1
-rw-r--r--lib/erl/src/thrift_buffered_transport.erl97
-rw-r--r--lib/erl/src/thrift_file_transport.erl126
-rw-r--r--lib/erl/src/thrift_framed_transport.erl172
-rw-r--r--lib/erl/src/thrift_membuffer_transport.erl83
-rw-r--r--lib/erl/src/thrift_memory_buffer.erl45
-rw-r--r--lib/erl/src/thrift_socket_transport.erl208
-rw-r--r--lib/erl/src/thrift_transport.erl133
-rw-r--r--lib/erl/test/test_membuffer.erl115
-rw-r--r--lib/erl/test/test_thrift_buffered_transport.erl359
-rw-r--r--lib/erl/test/test_thrift_file_transport.erl213
-rw-r--r--lib/erl/test/test_thrift_framed_transport.erl404
-rw-r--r--lib/erl/test/test_thrift_membuffer_transport.erl167
-rw-r--r--lib/erl/test/test_thrift_socket_transport.erl199
16 files changed, 1899 insertions, 432 deletions
diff --git a/lib/erl/Makefile.am b/lib/erl/Makefile.am
index 21d21bff3..d1408587c 100644
--- a/lib/erl/Makefile.am
+++ b/lib/erl/Makefile.am
@@ -45,7 +45,9 @@ all: .generated
./rebar compile
check: .generated
- ./rebar skip_deps=true eunit
+ ./rebar -C rebar.test.config get-deps
+ ./rebar -C rebar.test.config compile
+ ./rebar -C rebar.test.config skip_deps=true eunit
install: all
mkdir -p $(DESTDIR)$(ERLANG_INSTALL_LIB_DIR_thrift) ; \
diff --git a/lib/erl/rebar.test.config b/lib/erl/rebar.test.config
new file mode 100644
index 000000000..204f7ee72
--- /dev/null
+++ b/lib/erl/rebar.test.config
@@ -0,0 +1,5 @@
+{erl_opts, [{platform_define, "^R.*", otp16_or_less}, debug_info]}.
+
+{deps, [
+ {meck, "", {git, "git://github.com/eproxus/meck.git", {tag, "0.8.2"}}}
+]}.
diff --git a/lib/erl/src/thrift.app.src b/lib/erl/src/thrift.app.src
index 28b8cb5ba..1a23f0dd4 100644
--- a/lib/erl/src/thrift.app.src
+++ b/lib/erl/src/thrift.app.src
@@ -37,6 +37,7 @@
thrift_http_transport,
thrift_json_parser,
thrift_json_protocol,
+ thrift_membuffer_transport,
thrift_memory_buffer,
thrift_processor,
thrift_protocol,
diff --git a/lib/erl/src/thrift_buffered_transport.erl b/lib/erl/src/thrift_buffered_transport.erl
index d4d614eb8..e9d3fffa7 100644
--- a/lib/erl/src/thrift_buffered_transport.erl
+++ b/lib/erl/src/thrift_buffered_transport.erl
@@ -21,57 +21,78 @@
-behaviour(thrift_transport).
-%% API
--export([new/1, new_transport_factory/1]).
+%% constructor
+-export([new/1]).
+%% protocol callbacks
+-export([read/2, read_exact/2, write/2, flush/1, close/1]).
+%% legacy api
+-export([new_transport_factory/1]).
+
+
+-record(t_buffered, {
+ wrapped,
+ write_buffer
+}).
+
+-type state() :: #t_buffered{}.
+
+
+-spec new(Transport::thrift_transport:t_transport()) ->
+ thrift_transport:t_transport().
+
+new(Wrapped) ->
+ State = #t_buffered{
+ wrapped = Wrapped,
+ write_buffer = []
+ },
+ thrift_transport:new(?MODULE, State).
-%% thrift_transport callbacks
--export([write/2, read/2, flush/1, close/1]).
--record(buffered_transport, {wrapped, % a thrift_transport
- write_buffer % iolist()
- }).
--type state() :: #buffered_transport{}.
-include("thrift_transport_behaviour.hrl").
-new(WrappedTransport) ->
- State = #buffered_transport{wrapped = WrappedTransport,
- write_buffer = []},
- thrift_transport:new(?MODULE, State).
+%% reads data through from the wrapped transport
+read(State = #t_buffered{wrapped = Wrapped}, Len)
+when is_integer(Len), Len >= 0 ->
+ {NewState, Response} = thrift_transport:read(Wrapped, Len),
+ {State#t_buffered{wrapped = NewState}, Response}.
-%% Writes data into the buffer
-write(State = #buffered_transport{write_buffer = WBuf}, Data) ->
- {State#buffered_transport{write_buffer = [WBuf, Data]}, ok}.
+%% reads data through from the wrapped transport
+read_exact(State = #t_buffered{wrapped = Wrapped}, Len)
+when is_integer(Len), Len >= 0 ->
+ {NewState, Response} = thrift_transport:read_exact(Wrapped, Len),
+ {State#t_buffered{wrapped = NewState}, Response}.
-%% Flushes the buffer through to the wrapped transport
-flush(State = #buffered_transport{write_buffer = WBuf,
- wrapped = Wrapped0}) ->
- {Wrapped1, Response} = thrift_transport:write(Wrapped0, WBuf),
- {Wrapped2, _} = thrift_transport:flush(Wrapped1),
- NewState = State#buffered_transport{write_buffer = [],
- wrapped = Wrapped2},
- {NewState, Response}.
-%% Closes the transport and the wrapped transport
-close(State = #buffered_transport{wrapped = Wrapped0}) ->
- {Wrapped1, Result} = thrift_transport:close(Wrapped0),
- NewState = State#buffered_transport{wrapped = Wrapped1},
- {NewState, Result}.
+write(State = #t_buffered{write_buffer = Buffer}, Data) ->
+ {State#t_buffered{write_buffer = [Buffer, Data]}, ok}.
+
+
+flush(State = #t_buffered{wrapped = Wrapped, write_buffer = Buffer}) ->
+ case iolist_size(Buffer) of
+ %% if write buffer is empty, do nothing
+ 0 -> {State, ok};
+ _ ->
+ {Written, Response} = thrift_transport:write(Wrapped, Buffer),
+ {Flushed, ok} = thrift_transport:flush(Written),
+ {State#t_buffered{wrapped = Flushed, write_buffer = []}, Response}
+ end.
+
+
+close(State = #t_buffered{wrapped = Wrapped}) ->
+ {Closed, Result} = thrift_transport:close(Wrapped),
+ {State#t_buffered{wrapped = Closed}, Result}.
-%% Reads data through from the wrapped transport
-read(State = #buffered_transport{wrapped = Wrapped0}, Len) when is_integer(Len) ->
- {Wrapped1, Response} = thrift_transport:read(Wrapped0, Len),
- NewState = State#buffered_transport{wrapped = Wrapped1},
- {NewState, Response}.
%%--------------------------------------------------------------------
%%% Internal functions
%%--------------------------------------------------------------------
%%%% FACTORY GENERATION %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
new_transport_factory(WrapFactory) ->
- F = fun() ->
- {ok, Wrapped} = WrapFactory(),
- new(Wrapped)
- end,
- {ok, F}.
+ F = fun() ->
+ {ok, Wrapped} = WrapFactory(),
+ new(Wrapped)
+ end,
+ {ok, F}.
+
diff --git a/lib/erl/src/thrift_file_transport.erl b/lib/erl/src/thrift_file_transport.erl
index ba3aa8983..071152b63 100644
--- a/lib/erl/src/thrift_file_transport.erl
+++ b/lib/erl/src/thrift_file_transport.erl
@@ -21,69 +21,95 @@
-behaviour(thrift_transport).
--export([new_reader/1,
- new/1,
- new/2,
- write/2, read/2, flush/1, close/1]).
-
--record(t_file_transport, {device,
- should_close = true,
- mode = write}).
--type state() :: #t_file_transport{}.
--include("thrift_transport_behaviour.hrl").
+%% constructors
+-export([new/1, new/2]).
+%% protocol callbacks
+-export([read/2, read_exact/2, write/2, flush/1, close/1]).
+%% legacy api
+-export([new_reader/1]).
-%%%% CONSTRUCTION %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-new_reader(Filename) ->
- case file:open(Filename, [read, binary, {read_ahead, 1024*1024}]) of
- {ok, IODevice} ->
- new(IODevice, [{should_close, true}, {mode, read}]);
- Error -> Error
- end.
+-record(t_file, {
+ device,
+ should_close = true,
+ mode = write
+}).
-new(Device) ->
- new(Device, []).
+-type state() :: #t_file{}.
+
+
+-spec new(Device::file:io_device()) ->
+ thrift_transport:t_transport().
+
+new(Device) -> new(Device, []).
+
+-spec new(Device::file:io_device(), Opts::list()) ->
+ thrift_transport:t_transport().
-%% Device :: io_device()
-%%
%% Device should be opened in raw and binary mode.
new(Device, Opts) when is_list(Opts) ->
- State = parse_opts(Opts, #t_file_transport{device = Device}),
- thrift_transport:new(?MODULE, State).
+ State = parse_opts(Opts, #t_file{device = Device}),
+ thrift_transport:new(?MODULE, State).
-%% Parse options
-parse_opts([{should_close, Bool} | Rest], State) when is_boolean(Bool) ->
- parse_opts(Rest, State#t_file_transport{should_close = Bool});
-parse_opts([{mode, Mode} | Rest], State)
- when Mode =:= write;
- Mode =:= read ->
- parse_opts(Rest, State#t_file_transport{mode = Mode});
+parse_opts([{should_close, Bool}|Rest], State)
+when is_boolean(Bool) ->
+ parse_opts(Rest, State#t_file{should_close = Bool});
+parse_opts([{mode, Mode}|Rest], State)
+when Mode =:= write; Mode =:= read ->
+ parse_opts(Rest, State#t_file{mode = Mode});
parse_opts([], State) ->
- State.
+ State.
-%%%% TRANSPORT IMPL %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+-include("thrift_transport_behaviour.hrl").
+
-write(This = #t_file_transport{device = Device, mode = write}, Data) ->
- {This, file:write(Device, Data)};
-write(This, _D) ->
- {This, {error, read_mode}}.
+read(State = #t_file{device = Device, mode = read}, Len)
+when is_integer(Len), Len >= 0 ->
+ case file:read(Device, Len) of
+ eof -> {State, {error, eof}};
+ {ok, Result} -> {State, {ok, iolist_to_binary(Result)}}
+ end;
+read(State, _) ->
+ {State, {error, write_mode}}.
-read(This = #t_file_transport{device = Device, mode = read}, Len)
- when is_integer(Len), Len >= 0 ->
- {This, file:read(Device, Len)};
-read(This, _D) ->
- {This, {error, read_mode}}.
+read_exact(State = #t_file{device = Device, mode = read}, Len)
+when is_integer(Len), Len >= 0 ->
+ case file:read(Device, Len) of
+ eof -> {State, {error, eof}};
+ {ok, Result} ->
+ case iolist_size(Result) of
+ X when X < Len -> {State, {error, eof}};
+ _ -> {State, {ok, iolist_to_binary(Result)}}
+ end
+ end;
+read_exact(State, _) ->
+ {State, {error, write_mode}}.
-flush(This = #t_file_transport{device = Device, mode = write}) ->
- {This, file:sync(Device)}.
-close(This = #t_file_transport{device = Device, should_close = SC}) ->
- case SC of
- true ->
- {This, file:close(Device)};
- false ->
- {This, ok}
- end.
+write(State = #t_file{device = Device, mode = write}, Data) ->
+ {State, file:write(Device, Data)};
+write(State, _) ->
+ {State, {error, read_mode}}.
+
+
+flush(State = #t_file{device = Device, mode = write}) ->
+ {State, file:sync(Device)}.
+
+
+close(State = #t_file{device = Device, should_close = SC}) ->
+ case SC of
+ true -> {State, file:close(Device)};
+ false -> {State, ok}
+ end.
+
+
+%% legacy api. left for compatibility
+new_reader(Filename) ->
+ case file:open(Filename, [read, binary, {read_ahead, 1024*1024}]) of
+ {ok, IODevice} -> new(IODevice, [{should_close, true}, {mode, read}]);
+ Error -> Error
+ end.
+
diff --git a/lib/erl/src/thrift_framed_transport.erl b/lib/erl/src/thrift_framed_transport.erl
index eca3cbe41..715f090d9 100644
--- a/lib/erl/src/thrift_framed_transport.erl
+++ b/lib/erl/src/thrift_framed_transport.erl
@@ -21,83 +21,105 @@
-behaviour(thrift_transport).
-%% API
+%% constructor
-export([new/1]).
+%% protocol callbacks
+-export([read/2, read_exact/2, write/2, flush/1, close/1]).
+
+
+-record(t_framed, {
+ wrapped,
+ read_buffer,
+ write_buffer
+}).
+
+-type state() :: #t_framed{}.
+
+
+-spec new(Transport::thrift_transport:t_transport()) ->
+ thrift_transport:t_transport().
+
+new(Wrapped) ->
+ State = #t_framed{
+ wrapped = Wrapped,
+ read_buffer = [],
+ write_buffer = []
+ },
+ thrift_transport:new(?MODULE, State).
-%% thrift_transport callbacks
--export([write/2, read/2, flush/1, close/1]).
--record(framed_transport, {wrapped, % a thrift_transport
- read_buffer, % iolist()
- write_buffer % iolist()
- }).
--type state() :: #framed_transport{}.
-include("thrift_transport_behaviour.hrl").
-new(WrappedTransport) ->
- State = #framed_transport{wrapped = WrappedTransport,
- read_buffer = [],
- write_buffer = []},
- thrift_transport:new(?MODULE, State).
-
-%% Writes data into the buffer
-write(State = #framed_transport{write_buffer = WBuf}, Data) ->
- {State#framed_transport{write_buffer = [WBuf, Data]}, ok}.
-
-%% Flushes the buffer through to the wrapped transport
-flush(State0 = #framed_transport{write_buffer = Buffer,
- wrapped = Wrapped0}) ->
- FrameLen = iolist_size(Buffer),
- Data = [<<FrameLen:32/integer-signed-big>>, Buffer],
-
- {Wrapped1, Response} = thrift_transport:write(Wrapped0, Data),
-
- {Wrapped2, _} = thrift_transport:flush(Wrapped1),
-
- State1 = State0#framed_transport{wrapped = Wrapped2, write_buffer = []},
- {State1, Response}.
-
-%% Closes the transport and the wrapped transport
-close(State = #framed_transport{wrapped = Wrapped0}) ->
- {Wrapped1, Result} = thrift_transport:close(Wrapped0),
- NewState = State#framed_transport{wrapped = Wrapped1},
- {NewState, Result}.
-
-%% Reads data through from the wrapped transport
-read(State0 = #framed_transport{wrapped = Wrapped0, read_buffer = RBuf},
- Len) when is_integer(Len) ->
- {Wrapped1, {RBuf1, RBuf1Size}} =
- %% if the read buffer is empty, read another frame
- %% otherwise, just read from what's left in the buffer
- case iolist_size(RBuf) of
- 0 ->
- %% read the frame length
- case thrift_transport:read(Wrapped0, 4) of
- {WrappedS1,
- {ok, <<FrameLen:32/integer-signed-big, _/binary>>}} ->
- %% then read the data
- case thrift_transport:read(WrappedS1, FrameLen) of
- {WrappedS2, {ok, Bin}} ->
- {WrappedS2, {Bin, erlang:byte_size(Bin)}};
- {WrappedS2, {error, Reason1}} ->
- {WrappedS2, {error, Reason1}}
- end;
- {WrappedS1, {error, Reason2}} ->
- {WrappedS1, {error, Reason2}}
- end;
- Sz ->
- {Wrapped0, {RBuf, Sz}}
- end,
-
- %% pull off Give bytes, return them to the user, leave the rest in the buffer
- case RBuf1 of
- error ->
- { State0#framed_transport {wrapped = Wrapped1, read_buffer = [] },
- {RBuf1, RBuf1Size} };
- _ ->
- Give = min(RBuf1Size, Len),
- <<Data:Give/binary, RBuf2/binary>> = iolist_to_binary(RBuf1),
-
- { State0#framed_transport{wrapped = Wrapped1, read_buffer=RBuf2},
- {ok, Data} }
- end.
+
+read(State = #t_framed{wrapped = Wrapped, read_buffer = Buffer}, Len)
+when is_integer(Len), Len >= 0 ->
+ Binary = iolist_to_binary(Buffer),
+ case Binary of
+ <<>> when Len > 0 ->
+ case next_frame(Wrapped) of
+ {NewState, {ok, Frame}} ->
+ NewBinary = iolist_to_binary([Binary, Frame]),
+ Give = min(iolist_size(NewBinary), Len),
+ {Result, Remaining} = split_binary(NewBinary, Give),
+ {State#t_framed{wrapped = NewState, read_buffer = Remaining}, {ok, Result}};
+ Error -> Error
+ end;
+ %% read of zero bytes
+ <<>> -> {State, {ok, <<>>}};
+ %% read buffer is nonempty
+ _ ->
+ Give = min(iolist_size(Binary), Len),
+ {Result, Remaining} = split_binary(Binary, Give),
+ {State#t_framed{read_buffer = Remaining}, {ok, Result}}
+ end.
+
+
+read_exact(State = #t_framed{wrapped = Wrapped, read_buffer = Buffer}, Len)
+when is_integer(Len), Len >= 0 ->
+ Binary = iolist_to_binary(Buffer),
+ case iolist_size(Binary) of
+ %% read buffer is larger than requested read size
+ X when X >= Len ->
+ {Result, Remaining} = split_binary(Binary, Len),
+ {State#t_framed{read_buffer = Remaining}, {ok, Result}};
+ %% read buffer is insufficient for requested read size
+ _ ->
+ case next_frame(Wrapped) of
+ {NewState, {ok, Frame}} ->
+ read_exact(
+ State#t_framed{wrapped = NewState, read_buffer = [Buffer, Frame]},
+ Len
+ );
+ {NewState, Error} ->
+ {State#t_framed{wrapped = NewState}, Error}
+ end
+ end.
+
+next_frame(Transport) ->
+ case thrift_transport:read_exact(Transport, 4) of
+ {NewState, {ok, <<FrameLength:32/integer-signed-big>>}} ->
+ thrift_transport:read_exact(NewState, FrameLength);
+ Error -> Error
+ end.
+
+
+write(State = #t_framed{write_buffer = Buffer}, Data) ->
+ {State#t_framed{write_buffer = [Buffer, Data]}, ok}.
+
+
+flush(State = #t_framed{write_buffer = Buffer, wrapped = Wrapped}) ->
+ case iolist_size(Buffer) of
+ %% if write buffer is empty, do nothing
+ 0 -> {State, ok};
+ FrameLen ->
+ Data = [<<FrameLen:32/integer-signed-big>>, Buffer],
+ {Written, Response} = thrift_transport:write(Wrapped, Data),
+ {Flushed, ok} = thrift_transport:flush(Written),
+ {State#t_framed{wrapped = Flushed, write_buffer = []}, Response}
+ end.
+
+
+close(State = #t_framed{wrapped = Wrapped}) ->
+ {Closed, Result} = thrift_transport:close(Wrapped),
+ {State#t_framed{wrapped = Closed}, Result}.
+
diff --git a/lib/erl/src/thrift_membuffer_transport.erl b/lib/erl/src/thrift_membuffer_transport.erl
new file mode 100644
index 000000000..be9acb23a
--- /dev/null
+++ b/lib/erl/src/thrift_membuffer_transport.erl
@@ -0,0 +1,83 @@
+%%
+%% Licensed to the Apache Software Foundation (ASF) under one
+%% or more contributor license agreements. See the NOTICE file
+%% distributed with this work for additional information
+%% regarding copyright ownership. The ASF licenses this file
+%% to you 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(thrift_membuffer_transport).
+
+-behaviour(thrift_transport).
+
+%% constructors
+-export([new/0, new/1]).
+%% protocol callbacks
+-export([read/2, read_exact/2, write/2, flush/1, close/1]).
+
+
+-record(t_membuffer, {
+ buffer = []
+}).
+
+-type state() :: #t_membuffer{}.
+
+
+-spec new() -> thrift_transport:t_transport().
+
+new() -> new([]).
+
+-spec new(Buf::iodata()) -> thrift_transport:t_transport().
+
+new(Buf) when is_list(Buf) ->
+ State = #t_membuffer{buffer = Buf},
+ thrift_transport:new(?MODULE, State);
+new(Buf) when is_binary(Buf) ->
+ State = #t_membuffer{buffer = [Buf]},
+ thrift_transport:new(?MODULE, State).
+
+
+-include("thrift_transport_behaviour.hrl").
+
+
+read(State = #t_membuffer{buffer = Buf}, Len)
+when is_integer(Len), Len >= 0 ->
+ Binary = iolist_to_binary(Buf),
+ Give = min(iolist_size(Binary), Len),
+ {Result, Remaining} = split_binary(Binary, Give),
+ {State#t_membuffer{buffer = Remaining}, {ok, Result}}.
+
+
+read_exact(State = #t_membuffer{buffer = Buf}, Len)
+when is_integer(Len), Len >= 0 ->
+ Binary = iolist_to_binary(Buf),
+ case iolist_size(Binary) of
+ X when X >= Len ->
+ {Result, Remaining} = split_binary(Binary, Len),
+ {State#t_membuffer{buffer = Remaining}, {ok, Result}};
+ _ ->
+ {State, {error, eof}}
+ end.
+
+
+write(State = #t_membuffer{buffer = Buf}, Data)
+when is_list(Data); is_binary(Data) ->
+ {State#t_membuffer{buffer = [Buf, Data]}, ok}.
+
+
+flush(State) -> {State, ok}.
+
+
+close(State) -> {State, ok}.
+
diff --git a/lib/erl/src/thrift_memory_buffer.erl b/lib/erl/src/thrift_memory_buffer.erl
index 53abbc431..6a59ea56d 100644
--- a/lib/erl/src/thrift_memory_buffer.erl
+++ b/lib/erl/src/thrift_memory_buffer.erl
@@ -21,42 +21,27 @@
-behaviour(thrift_transport).
-%% API
--export([new/0, new/1, new_transport_factory/0]).
+%% constructors
+-export([new/0, new/1]).
+%% protocol callbacks
+-export([read/2, write/2, flush/1, close/1]).
+%% legacy api
+-export([new_transport_factory/0]).
-%% thrift_transport callbacks
--export([write/2, read/2, flush/1, close/1]).
--record(memory_buffer, {buffer}).
--type state() :: #memory_buffer{}.
--include("thrift_transport_behaviour.hrl").
+%% wrapper around thrift_membuffer_transport for legacy reasons
-new() ->
- State = #memory_buffer{buffer = []},
- thrift_transport:new(?MODULE, State).
+new() -> thrift_membuffer_transport:new().
-new (Buf) when is_list (Buf) ->
- State = #memory_buffer{buffer = Buf},
- thrift_transport:new(?MODULE, State);
-new (Buf) ->
- State = #memory_buffer{buffer = [Buf]},
- thrift_transport:new(?MODULE, State).
+new(State) -> thrift_membuffer_transport:new(State).
-new_transport_factory() ->
- {ok, fun() -> new() end}.
+new_transport_factory() -> {ok, fun() -> new() end}.
-%% Writes data into the buffer
-write(State = #memory_buffer{buffer = Buf}, Data) ->
- {State#memory_buffer{buffer = [Buf, Data]}, ok}.
+write(State, Data) -> thrift_membuffer_transport:write(State, Data).
-flush(State = #memory_buffer {buffer = Buf}) ->
- {State#memory_buffer{buffer = []}, Buf}.
+read(State, Data) -> thrift_membuffer_transport:read(State, Data).
-close(State) ->
- {State, ok}.
+flush(State) -> thrift_membuffer_transport:flush(State).
+
+close(State) -> thrift_membuffer_transport:close(State).
-read(State = #memory_buffer{buffer = Buf}, Len) when is_integer(Len) ->
- Binary = iolist_to_binary(Buf),
- Give = min(iolist_size(Binary), Len),
- {Result, Remaining} = split_binary(Binary, Give),
- {State#memory_buffer{buffer = Remaining}, {ok, Result}}.
diff --git a/lib/erl/src/thrift_socket_transport.erl b/lib/erl/src/thrift_socket_transport.erl
index fec02417a..fa10ed0c6 100644
--- a/lib/erl/src/thrift_socket_transport.erl
+++ b/lib/erl/src/thrift_socket_transport.erl
@@ -21,104 +21,156 @@
-behaviour(thrift_transport).
--export([new/1,
- new/2,
- write/2, read/2, flush/1, close/1,
+%% constructors
+-export([new/1, new/2]).
+%% transport callbacks
+-export([read/2, read_exact/2, write/2, flush/1, close/1]).
+%% legacy api
+-export([new_transport_factory/3]).
- new_transport_factory/3]).
--record(data, {socket,
- recv_timeout=infinity}).
--type state() :: #data{}.
--include("thrift_transport_behaviour.hrl").
+-record(t_socket, {
+ socket,
+ recv_timeout=60000,
+ buffer = []
+}).
-new(Socket) ->
- new(Socket, []).
+-type state() :: #t_socket{}.
-new(Socket, Opts) when is_list(Opts) ->
- State =
- case lists:keysearch(recv_timeout, 1, Opts) of
- {value, {recv_timeout, Timeout}}
- when is_integer(Timeout), Timeout > 0 ->
- #data{socket=Socket, recv_timeout=Timeout};
- _ ->
- #data{socket=Socket}
- end,
- thrift_transport:new(?MODULE, State).
-%% Data :: iolist()
-write(This = #data{socket = Socket}, Data) ->
- {This, gen_tcp:send(Socket, Data)}.
+-spec new(Socket::any()) ->
+ thrift_transport:t_transport().
-read(This = #data{socket=Socket, recv_timeout=Timeout}, Len)
- when is_integer(Len), Len >= 0 ->
- case gen_tcp:recv(Socket, Len, Timeout) of
- Err = {error, timeout} ->
- gen_tcp:close(Socket),
- {This, Err};
- Data ->
- {This, Data}
- end.
+new(Socket) -> new(Socket, []).
-%% We can't really flush - everything is flushed when we write
-flush(This) ->
- {This, ok}.
+-spec new(Socket::any(), Opts::list()) ->
+ thrift_transport:t_transport().
+
+new(Socket, Opts) when is_list(Opts) ->
+ State = parse_opts(Opts, #t_socket{socket = Socket}),
+ thrift_transport:new(?MODULE, State).
-close(This = #data{socket = Socket}) ->
- {This, gen_tcp:close(Socket)}.
+parse_opts([{recv_timeout, Timeout}|Rest], State)
+when is_integer(Timeout), Timeout > 0 ->
+ parse_opts(Rest, State#t_socket{recv_timeout = Timeout});
+parse_opts([{recv_timeout, infinity}|Rest], State) ->
+ parse_opts(Rest, State#t_socket{recv_timeout = infinity});
+parse_opts([], State) ->
+ State.
-%%%% FACTORY GENERATION %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+-include("thrift_transport_behaviour.hrl").
+
+
+read(State = #t_socket{buffer = Buf}, Len)
+when is_integer(Len), Len >= 0 ->
+ Binary = iolist_to_binary(Buf),
+ case iolist_size(Binary) of
+ X when X >= Len ->
+ {Result, Remaining} = split_binary(Binary, Len),
+ {State#t_socket{buffer = Remaining}, {ok, Result}};
+ _ -> recv(State, Len)
+ end.
+
+recv(State = #t_socket{socket = Socket, buffer = Buf}, Len) ->
+ case gen_tcp:recv(Socket, 0, State#t_socket.recv_timeout) of
+ {error, Error} ->
+ gen_tcp:close(Socket),
+ {State, {error, Error}};
+ {ok, Data} ->
+ Binary = iolist_to_binary([Buf, Data]),
+ Give = min(iolist_size(Binary), Len),
+ {Result, Remaining} = split_binary(Binary, Give),
+ {State#t_socket{buffer = Remaining}, {ok, Result}}
+ end.
+
+
+read_exact(State = #t_socket{buffer = Buf}, Len)
+when is_integer(Len), Len >= 0 ->
+ Binary = iolist_to_binary(Buf),
+ case iolist_size(Binary) of
+ X when X >= Len -> read(State, Len);
+ X ->
+ case gen_tcp:recv(State#t_socket.socket, Len - X, State#t_socket.recv_timeout) of
+ {error, Error} ->
+ gen_tcp:close(State#t_socket.socket),
+ {State, {error, Error}};
+ {ok, Data} ->
+ {State#t_socket{buffer = []}, {ok, <<Binary/binary, Data/binary>>}}
+ end
+ end.
+
+
+write(State = #t_socket{socket = Socket}, Data) ->
+ case gen_tcp:send(Socket, Data) of
+ {error, Error} ->
+ gen_tcp:close(Socket),
+ {State, {error, Error}};
+ ok -> {State, ok}
+ end.
+
+
+flush(State) ->
+ {State#t_socket{buffer = []}, ok}.
+
+
+close(State = #t_socket{socket = Socket}) ->
+ {State, gen_tcp:close(Socket)}.
+
+
+%% legacy api. left for compatibility
%% The following "local" record is filled in by parse_factory_options/2
%% below. These options can be passed to new_protocol_factory/3 in a
%% proplists-style option list. They're parsed like this so it is an O(n)
%% operation instead of O(n^2)
--record(factory_opts, {connect_timeout = infinity,
- sockopts = [],
- framed = false}).
-
-parse_factory_options([], Opts) ->
- Opts;
-parse_factory_options([{framed, Bool} | Rest], Opts) when is_boolean(Bool) ->
- parse_factory_options(Rest, Opts#factory_opts{framed=Bool});
-parse_factory_options([{sockopts, OptList} | Rest], Opts) when is_list(OptList) ->
- parse_factory_options(Rest, Opts#factory_opts{sockopts=OptList});
-parse_factory_options([{connect_timeout, TO} | Rest], Opts) when TO =:= infinity; is_integer(TO) ->
- parse_factory_options(Rest, Opts#factory_opts{connect_timeout=TO});
-parse_factory_options([{recv_timeout, TO} | Rest], Opts) when TO =:= infinity; is_integer(TO) ->
- parse_factory_options(Rest, Opts).
+-record(factory_opts, {
+ connect_timeout = infinity,
+ sockopts = [],
+ framed = false
+}).
+
+parse_factory_options([], FactoryOpts, TransOpts) -> {FactoryOpts, TransOpts};
+parse_factory_options([{framed, Bool}|Rest], FactoryOpts, TransOpts)
+when is_boolean(Bool) ->
+ parse_factory_options(Rest, FactoryOpts#factory_opts{framed = Bool}, TransOpts);
+parse_factory_options([{sockopts, OptList}|Rest], FactoryOpts, TransOpts)
+when is_list(OptList) ->
+ parse_factory_options(Rest, FactoryOpts#factory_opts{sockopts = OptList}, TransOpts);
+parse_factory_options([{connect_timeout, TO}|Rest], FactoryOpts, TransOpts)
+when TO =:= infinity; is_integer(TO) ->
+ parse_factory_options(Rest, FactoryOpts#factory_opts{connect_timeout = TO}, TransOpts);
+parse_factory_options([{recv_timeout, TO}|Rest], FactoryOpts, TransOpts)
+when TO =:= infinity; is_integer(TO) ->
+ parse_factory_options(Rest, FactoryOpts, [{recv_timeout, TO}] ++ TransOpts).
-%%
%% Generates a "transport factory" function - a fun which returns a thrift_transport()
%% instance.
-%% This can be passed into a protocol factory to generate a connection to a
+%% State can be passed into a protocol factory to generate a connection to a
%% thrift server over a socket.
-%%
new_transport_factory(Host, Port, Options) ->
- ParsedOpts = parse_factory_options(Options, #factory_opts{}),
-
- F = fun() ->
- SockOpts = [binary,
- {packet, 0},
- {active, false},
- {nodelay, true} |
- ParsedOpts#factory_opts.sockopts],
- case catch gen_tcp:connect(Host, Port, SockOpts,
- ParsedOpts#factory_opts.connect_timeout) of
- {ok, Sock} ->
- {ok, Transport} =
- thrift_socket_transport:new(Sock, Options),
- {ok, BufTransport} =
- case ParsedOpts#factory_opts.framed of
- true -> thrift_framed_transport:new(Transport);
- false -> thrift_buffered_transport:new(Transport)
- end,
- {ok, BufTransport};
- Error ->
- Error
- end
+ {FactoryOpts, TransOpts} = parse_factory_options(Options, #factory_opts{}, []),
+ {ok, fun() -> SockOpts = [binary,
+ {packet, 0},
+ {active, false},
+ {nodelay, true}|FactoryOpts#factory_opts.sockopts
+ ],
+ case catch gen_tcp:connect(
+ Host,
+ Port,
+ SockOpts,
+ FactoryOpts#factory_opts.connect_timeout
+ ) of
+ {ok, Sock} ->
+ {ok, Transport} = thrift_socket_transport:new(Sock, TransOpts),
+ {ok, BufTransport} = case FactoryOpts#factory_opts.framed of
+ true -> thrift_framed_transport:new(Transport);
+ false -> thrift_buffered_transport:new(Transport)
end,
- {ok, F}.
+ {ok, BufTransport};
+ Error -> Error
+ end
+ end}.
+
diff --git a/lib/erl/src/thrift_transport.erl b/lib/erl/src/thrift_transport.erl
index 39f8c056d..0fdf9709c 100644
--- a/lib/erl/src/thrift_transport.erl
+++ b/lib/erl/src/thrift_transport.erl
@@ -20,59 +20,102 @@
-module(thrift_transport).
-export([behaviour_info/1]).
+%% constructors
+-export([new/1, new/2]).
+%% transport callbacks
+-export([read/2, read_exact/2, write/2, flush/1, close/1]).
+
+-export_type([t_transport/0]).
--export([new/2,
- write/2,
- read/2,
- flush/1,
- close/1
- ]).
behaviour_info(callbacks) ->
- [{read, 2},
- {write, 2},
- {flush, 1},
- {close, 1}
- ].
+ [{read, 2}, {write, 2}, {flush, 1}, {close, 1}].
+
+
+-record(t_transport, {
+ module,
+ state
+}).
+
+-type state() :: #t_transport{}.
+-type t_transport() :: #t_transport{}.
--record(transport, {module, data}).
-ifdef(transport_wrapper_module).
-define(debug_wrap(Transport),
- case Transport#transport.module of
- ?transport_wrapper_module ->
- Transport;
- _Else ->
- {ok, Result} = ?transport_wrapper_module:new(Transport),
- Result
- end).
+ case Transport#t_transport.module of
+ ?transport_wrapper_module -> Transport;
+ _Else ->
+ {ok, Result} = ?transport_wrapper_module:new(Transport),
+ Result
+ end
+).
-else.
-define(debug_wrap(Transport), Transport).
-endif.
-new(Module, Data) when is_atom(Module) ->
- Transport0 = #transport{module = Module, data = Data},
- Transport1 = ?debug_wrap(Transport0),
- {ok, Transport1}.
-
--spec write(#transport{}, iolist() | binary()) -> {#transport{}, ok | {error, _Reason}}.
-write(Transport, Data) ->
- Module = Transport#transport.module,
- {NewTransData, Result} = Module:write(Transport#transport.data, Data),
- {Transport#transport{data = NewTransData}, Result}.
-
--spec read(#transport{}, non_neg_integer()) -> {#transport{}, {ok, binary()} | {error, _Reason}}.
-read(Transport, Len) when is_integer(Len) ->
- Module = Transport#transport.module,
- {NewTransData, Result} = Module:read(Transport#transport.data, Len),
- {Transport#transport{data = NewTransData}, Result}.
-
--spec flush(#transport{}) -> {#transport{}, ok | {error, _Reason}}.
-flush(Transport = #transport{module = Module, data = Data}) ->
- {NewTransData, Result} = Module:flush(Data),
- {Transport#transport{data = NewTransData}, Result}.
-
--spec close(#transport{}) -> {#transport{}, ok | {error, _Reason}}.
-close(Transport = #transport{module = Module, data = Data}) ->
- {NewTransData, Result} = Module:close(Data),
- {Transport#transport{data = NewTransData}, Result}.
+
+-type wrappable() ::
+ binary() |
+ list() |
+ {membuffer, binary() | list()} |
+ {tcp, port()} |
+ {tcp, port(), list()} |
+ {file, file:io_device()} |
+ {file, file:io_device(), list()} |
+ {file, file:filename()} |
+ {file, file:filename(), list()}.
+
+-spec new(wrappable()) -> {ok, #t_transport{}}.
+
+new({membuffer, Membuffer}) when is_binary(Membuffer); is_list(Membuffer) ->
+ thrift_membuffer_transport:new(Membuffer);
+new({membuffer, Membuffer, []}) when is_binary(Membuffer); is_list(Membuffer) ->
+ thrift_membuffer_transport:new(Membuffer);
+new({tcp, Socket}) when is_port(Socket) ->
+ new({tcp, Socket, []});
+new({tcp, Socket, Opts}) when is_port(Socket) ->
+ thrift_socket_transport:new(Socket, Opts);
+new({file, Filename}) when is_list(Filename); is_binary(Filename) ->
+ new({file, Filename, []});
+new({file, Filename, Opts}) when is_list(Filename); is_binary(Filename) ->
+ {ok, File} = file:open(Filename, [raw, binary]),
+ new({file, File, Opts});
+new({file, File, Opts}) ->
+ thrift_file_transport:new(File, Opts).
+
+-spec new(Module::module(), State::any()) -> {ok, #t_transport{}}.
+
+new(Module, State) when is_atom(Module) ->
+ {ok, ?debug_wrap(#t_transport{module = Module, state = State})}.
+
+
+-include("thrift_transport_behaviour.hrl").
+
+
+read(Transport = #t_transport{module = Module}, Len)
+when is_integer(Len), Len >= 0 ->
+ {NewState, Result} = Module:read(Transport#t_transport.state, Len),
+ {Transport#t_transport{state = NewState}, Result}.
+
+
+read_exact(Transport = #t_transport{module = Module}, Len)
+when is_integer(Len), Len >= 0 ->
+ {NewState, Result} = Module:read_exact(Transport#t_transport.state, Len),
+ {Transport#t_transport{state = NewState}, Result}.
+
+
+write(Transport = #t_transport{module = Module}, Data) ->
+ {NewState, Result} = Module:write(Transport#t_transport.state, Data),
+ {Transport#t_transport{state = NewState}, Result}.
+
+
+flush(Transport = #t_transport{module = Module}) ->
+ {NewState, Result} = Module:flush(Transport#t_transport.state),
+ {Transport#t_transport{state = NewState}, Result}.
+
+
+close(Transport = #t_transport{module = Module}) ->
+ {NewState, Result} = Module:close(Transport#t_transport.state),
+ {Transport#t_transport{state = NewState}, Result}.
+
diff --git a/lib/erl/test/test_membuffer.erl b/lib/erl/test/test_membuffer.erl
deleted file mode 100644
index 671ae1100..000000000
--- a/lib/erl/test/test_membuffer.erl
+++ /dev/null
@@ -1,115 +0,0 @@
-%%
-%% Licensed to the Apache Software Foundation (ASF) under one
-%% or more contributor license agreements. See the NOTICE file
-%% distributed with this work for additional information
-%% regarding copyright ownership. The ASF licenses this file
-%% to you 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(test_membuffer).
-
--ifdef(TEST).
--include_lib("eunit/include/eunit.hrl").
-
--include("gen-erl/thrift_test_types.hrl").
-
-test_data() ->
- #'Xtruct'{
- string_thing = <<"foobar">>,
- byte_thing = 123,
- i32_thing = 1234567,
- i64_thing = 12345678900
- }.
-
-encode_decode_1_test() ->
- {ok, Transport} = thrift_memory_buffer:new(),
- {ok, Protocol0} = thrift_binary_protocol:new(Transport),
- TestData = test_data(),
- {Protocol1, ok} = thrift_protocol:write(Protocol0,
- {{struct, element(2, thrift_test_types:struct_info('Xtruct'))},
- TestData}),
- {_Protocol2, {ok, Result}} = thrift_protocol:read(Protocol1,
- {struct, element(2, thrift_test_types:struct_info('Xtruct'))},
- 'Xtruct'),
- Result = TestData.
-
-encode_decode_2_test() ->
- {ok, Transport} = thrift_memory_buffer:new(),
- {ok, Protocol0} = thrift_binary_protocol:new(Transport),
- TestData = test_data(),
- {Protocol1, ok} = thrift_protocol:write(Protocol0,
- {{struct, element(2, thrift_test_types:struct_info('Xtruct'))},
- TestData}),
- {_Protocol2, {ok, Result}} = thrift_protocol:read(Protocol1,
- {struct, element(2, thrift_test_types:struct_info('Xtruct3'))},
- 'Xtruct3'),
-
- Result = #'Xtruct3'{string_thing = TestData#'Xtruct'.string_thing,
- changed = undefined,
- i32_thing = TestData#'Xtruct'.i32_thing,
- i64_thing = TestData#'Xtruct'.i64_thing}.
-
-
-encode_decode_3_test() ->
- {ok, Transport} = thrift_memory_buffer:new(),
- {ok, Protocol0} = thrift_binary_protocol:new(Transport),
- TestData = #'Bools'{im_true = true, im_false = false},
- {Protocol1, ok} = thrift_protocol:write(Protocol0,
- {{struct, element(2, thrift_test_types:struct_info('Bools'))},
- TestData}),
- {_Protocol2, {ok, Result}} = thrift_protocol:read(Protocol1,
- {struct, element(2, thrift_test_types:struct_info('Bools'))},
- 'Bools'),
-
- true = TestData#'Bools'.im_true =:= Result#'Bools'.im_true,
- true = TestData#'Bools'.im_false =:= Result#'Bools'.im_false.
-
-
-encode_decode_4_test() ->
- {ok, Transport} = thrift_memory_buffer:new(),
- {ok, Protocol0} = thrift_binary_protocol:new(Transport),
- TestData = #'Insanity'{xtructs=[]},
- {Protocol1, ok} = thrift_protocol:write(Protocol0,
- {{struct, element(2, thrift_test_types:struct_info('Insanity'))},
- TestData}),
- {_Protocol2, {ok, Result}} = thrift_protocol:read(Protocol1,
- {struct, element(2, thrift_test_types:struct_info('Insanity'))},
- 'Insanity'),
-
- TestData = Result.
-
-encode_decode_5_test() ->
- % test writing to a buffer, getting the bytes out, putting them
- % in a new buffer and reading them
-
- % here's the writing part
- {ok, Transport0} = thrift_memory_buffer:new(),
- {ok, Protocol0} = thrift_binary_protocol:new(Transport0),
- TestData = test_data(),
- {Protocol1, ok} = thrift_protocol:write(Protocol0,
- {{struct, element(2, thrift_test_types:struct_info('Xtruct'))},
- TestData}),
- % flush now returns the buffer
- {_Protocol2, Buf} = thrift_protocol:flush_transport(Protocol1),
-
- % now the reading part
- {ok, T2} = thrift_memory_buffer:new (Buf),
- {ok, P2} = thrift_binary_protocol:new(T2),
- {_, {ok, Result}} = thrift_protocol:read(P2,
- {struct, element(2, thrift_test_types:struct_info('Xtruct'))},
- 'Xtruct'),
-
- Result = TestData.
-
--endif.
diff --git a/lib/erl/test/test_thrift_buffered_transport.erl b/lib/erl/test/test_thrift_buffered_transport.erl
new file mode 100644
index 000000000..8519e82a1
--- /dev/null
+++ b/lib/erl/test/test_thrift_buffered_transport.erl
@@ -0,0 +1,359 @@
+%%
+%% Licensed to the Apache Software Foundation (ASF) under one
+%% or more contributor license agreements. See the NOTICE file
+%% distributed with this work for additional information
+%% regarding copyright ownership. The ASF licenses this file
+%% to you 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(test_thrift_buffered_transport).
+-include_lib("eunit/include/eunit.hrl").
+
+
+new(Transport) -> thrift_buffered_transport:new(Transport).
+
+new_test_() ->
+ [
+ {"new buffered membuffer", ?_assertMatch(
+ {ok, {t_transport, thrift_buffered_transport, {t_buffered,
+ {t_transport, thrift_membuffer_transport, {t_membuffer, []}},
+ []
+ }}},
+ new({t_transport, thrift_membuffer_transport, {t_membuffer, []}})
+ )}
+ ].
+
+
+read(Frame, Bytes) -> thrift_buffered_transport:read(Frame, Bytes).
+
+read_test_() ->
+ [
+ {"read zero bytes from an empty buffered membuffer", ?_assertMatch(
+ {
+ {t_buffered,
+ {t_transport, thrift_membuffer_transport, {t_membuffer, <<>>}},
+ []
+ },
+ {ok, <<>>}
+ },
+ read(
+ {t_buffered,
+ {t_transport, thrift_membuffer_transport, {t_membuffer, <<>>}},
+ []
+ },
+ 0
+ )
+ )},
+ {"read 1 byte from an empty buffered membuffer", ?_assertMatch(
+ {_, {ok, <<>>}},
+ read(
+ {t_buffered,
+ {t_transport, thrift_membuffer_transport, {t_membuffer, <<>>}},
+ []
+ },
+ 1
+ )
+ )},
+ {"read zero bytes from nonempty buffered membuffer", ?_assertMatch(
+ {
+ {t_buffered,
+ {t_transport, thrift_membuffer_transport, {t_membuffer,
+ <<"hallo world">>
+ }},
+ []
+ },
+ {ok, <<>>}
+ },
+ read(
+ {t_buffered,
+ {t_transport, thrift_membuffer_transport, {t_membuffer,
+ <<"hallo world">>
+ }},
+ []
+ },
+ 0
+ )
+ )},
+ {"read 1 byte from nonempty buffered membuffer", ?_assertMatch(
+ {
+ {t_buffered,
+ {t_transport, thrift_membuffer_transport, {t_membuffer, <<"allo world">>}},
+ []
+ },
+ {ok, <<"h">>}
+ },
+ read(
+ {t_buffered,
+ {t_transport, thrift_membuffer_transport, {t_membuffer, <<"hallo world">>}},
+ []
+ },
+ 1
+ )
+ )},
+ {"read 1 byte from nonempty buffer", ?_assertMatch(
+ {
+ {t_buffered,
+ {t_transport, thrift_membuffer_transport, {t_membuffer, <<"allo world">>}},
+ []
+ },
+ {ok, <<"h">>}
+ },
+ read(
+ {t_buffered,
+ {t_transport, thrift_membuffer_transport, {t_membuffer, <<"hallo world">>}},
+ []
+ },
+ 1
+ )
+ )},
+ {"read a zillion bytes from nonempty buffered membuffer", ?_assertMatch(
+ {
+ {t_buffered,
+ {t_transport, thrift_membuffer_transport, {t_membuffer, <<>>}},
+ []
+ },
+ {ok, <<"hallo world">>}
+ },
+ read(
+ {t_buffered,
+ {t_transport, thrift_membuffer_transport, {t_membuffer, <<"hallo world">>}},
+ []
+ },
+ 65536
+ )
+ )}
+ ].
+
+
+read_exact(Frame, Bytes) -> thrift_buffered_transport:read_exact(Frame, Bytes).
+
+read_exact_test_() ->
+ [
+ {"read exactly zero bytes from an empty buffered membuffer", ?_assertMatch(
+ {
+ {t_buffered,
+ {t_transport, thrift_membuffer_transport, {t_membuffer, <<>>}},
+ []
+ },
+ {ok, <<>>}
+ },
+ read_exact(
+ {t_buffered,
+ {t_transport, thrift_membuffer_transport, {t_membuffer, <<>>}},
+ []
+ },
+ 0
+ )
+ )},
+ {"read exactly 1 byte from an empty buffered membuffer", ?_assertMatch(
+ {_, {error, eof}},
+ read_exact(
+ {t_buffered,
+ {t_transport, thrift_membuffer_transport, {t_membuffer, <<>>}},
+ []
+ },
+ 1
+ )
+ )},
+ {"read exactly zero bytes from nonempty buffered membuffer", ?_assertMatch(
+ {
+ {t_buffered,
+ {t_transport, thrift_membuffer_transport, {t_membuffer, <<"hallo world">>}},
+ []
+ },
+ {ok, <<>>}
+ },
+ read_exact(
+ {t_buffered,
+ {t_transport, thrift_membuffer_transport, {t_membuffer, <<"hallo world">>}},
+ []
+ },
+ 0
+ )
+ )},
+ {"read exactly 1 byte from nonempty buffered membuffer", ?_assertMatch(
+ {
+ {t_buffered,
+ {t_transport, thrift_membuffer_transport, {t_membuffer, <<"allo world">>}},
+ []
+ },
+ {ok, <<"h">>}
+ },
+ read_exact(
+ {t_buffered,
+ {t_transport, thrift_membuffer_transport, {t_membuffer,
+ <<"hallo world">>
+ }},
+ []
+ },
+ 1
+ )
+ )},
+ {"read exactly 1 byte from nonempty buffer", ?_assertMatch(
+ {
+ {t_buffered,
+ {t_transport, thrift_membuffer_transport, {t_membuffer, <<"allo world">>}},
+ []
+ },
+ {ok, <<"h">>}
+ },
+ read_exact(
+ {t_buffered,
+ {t_transport, thrift_membuffer_transport, {t_membuffer, <<"hallo world">>}},
+ []
+ },
+ 1
+ )
+ )},
+ {"read exactly a zillion bytes from nonempty buffered membuffer", ?_assertMatch(
+ {
+ {t_buffered,
+ {t_transport, thrift_membuffer_transport, {t_membuffer, <<"hallo world">>}},
+ []
+ },
+ {error, eof}
+ },
+ read_exact(
+ {t_buffered,
+ {t_transport, thrift_membuffer_transport, {t_membuffer,
+ <<"hallo world">>
+ }},
+ []
+ },
+ 65536
+ )
+ )}
+ ].
+
+
+write(Framed, Data) -> thrift_buffered_transport:write(Framed, Data).
+
+write_test_() ->
+ [
+ {"write empty list to empty buffered membuffer", ?_assertMatch(
+ {
+ {t_buffered,
+ {t_transport, thrift_membuffer_transport, {t_membuffer, <<>>}},
+ [[], []]
+ },
+ ok
+ },
+ write(
+ {t_buffered,
+ {t_transport, thrift_membuffer_transport, {t_membuffer, <<>>}},
+ []
+ },
+ []
+ )
+ )},
+ {"write empty list to nonempty buffered membuffer", ?_assertMatch(
+ {
+ {t_buffered,
+ {t_transport, thrift_membuffer_transport, {t_membuffer, <<>>}},
+ [["hallo world"], []]
+ },
+ ok
+ },
+ write(
+ {t_buffered,
+ {t_transport, thrift_membuffer_transport, {t_membuffer, <<>>}},
+ ["hallo world"]
+ },
+ []
+ )
+ )},
+ {"write empty binary to empty buffered membuffer", ?_assertMatch(
+ {
+ {t_buffered,
+ {t_transport, thrift_membuffer_transport, {t_membuffer, <<>>}},
+ [[], <<>>]
+ },
+ ok
+ },
+ write(
+ {t_buffered,
+ {t_transport, thrift_membuffer_transport, {t_membuffer, <<>>}},
+ []
+ },
+ <<>>
+ )
+ )},
+ {"write empty binary to nonempty buffered membuffer", ?_assertMatch(
+ {
+ {t_buffered,
+ {t_transport, thrift_membuffer_transport, {t_membuffer, <<>>}},
+ [["hallo world"], <<>>]
+ },
+ ok
+ },
+ write(
+ {t_buffered,
+ {t_transport, thrift_membuffer_transport, {t_membuffer, <<>>}},
+ ["hallo world"]
+ },
+ <<>>
+ )
+ )}
+ ].
+
+
+flush(Transport) -> thrift_buffered_transport:flush(Transport).
+
+flush_test_() ->
+ [
+ {"flush empty buffered membuffer", ?_assertMatch(
+ {{t_buffered,
+ {t_transport, thrift_membuffer_transport, {t_membuffer, <<>>}},
+ []
+ },
+ ok
+ },
+ flush({t_buffered,
+ {t_transport, thrift_membuffer_transport, {t_membuffer, <<>>}},
+ []
+ })
+ )},
+ {"flush nonempty buffered membuffer", ?_assertMatch(
+ {{t_buffered,
+ {t_transport, thrift_membuffer_transport, {t_membuffer,
+ [<<>>, <<"hallo world">>]
+ }},
+ []
+ },
+ ok
+ },
+ flush({t_buffered,
+ {t_transport, thrift_membuffer_transport, {t_membuffer, <<>>}},
+ <<"hallo world">>
+ })
+ )}
+ ].
+
+
+close(Transport) -> thrift_buffered_transport:close(Transport).
+
+close_test_() ->
+ {"close buffered membuffer", ?_assertMatch(
+ {{t_buffered,
+ {t_transport, thrift_membuffer_transport, {t_membuffer, <<>>}},
+ []
+ },
+ ok
+ },
+ close({t_buffered,
+ {t_transport, thrift_membuffer_transport, {t_membuffer, <<>>}},
+ []
+ })
+ )}.
+
diff --git a/lib/erl/test/test_thrift_file_transport.erl b/lib/erl/test/test_thrift_file_transport.erl
new file mode 100644
index 000000000..3e5c1d1e4
--- /dev/null
+++ b/lib/erl/test/test_thrift_file_transport.erl
@@ -0,0 +1,213 @@
+%%
+%% Licensed to the Apache Software Foundation (ASF) under one
+%% or more contributor license agreements. See the NOTICE file
+%% distributed with this work for additional information
+%% regarding copyright ownership. The ASF licenses this file
+%% to you 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(test_thrift_file_transport).
+-include_lib("eunit/include/eunit.hrl").
+
+
+new(File) -> thrift_file_transport:new(File).
+new(File, Opts) -> thrift_file_transport:new(File, Opts).
+
+new_test_() ->
+ [
+ {"new file", ?_assertMatch(
+ {ok, {_, thrift_file_transport, {t_file, a_fake_file, true, write}}},
+ new(a_fake_file)
+ )},
+ {"new file in read mode", ?_assertMatch(
+ {ok, {_, thrift_file_transport, {t_file, a_fake_file, true, read}}},
+ new(a_fake_file, [{mode, read}])
+ )},
+ {"new file in write mode", ?_assertMatch(
+ {ok, {_, thrift_file_transport, {t_file, a_fake_file, true, write}}},
+ new(a_fake_file, [{mode, write}])
+ )},
+ {"new file in should_close true mode", ?_assertMatch(
+ {ok, {_, thrift_file_transport, {t_file, a_fake_file, true, write}}},
+ new(a_fake_file, [{should_close, true}])
+ )},
+ {"new file in should_close false mode", ?_assertMatch(
+ {ok, {_, thrift_file_transport, {t_file, a_fake_file, false, write}}},
+ new(a_fake_file, [{should_close, false}])
+ )}
+ ].
+
+
+read(File, Bytes) -> thrift_file_transport:read(File, Bytes).
+
+read_test_() ->
+ {setup,
+ fun() ->
+ meck:new(file, [unstick, passthrough]),
+ meck:expect(file, read, fun(Bin, N) ->
+ {Result, _} = split_binary(Bin, min(iolist_size(Bin), N)),
+ {ok, Result}
+ end)
+ end,
+ fun(_) -> meck:unload(file) end,
+ [
+ {"read zero bytes from empty file", ?_assertMatch(
+ {_, {ok, <<>>}},
+ read({t_file, <<>>, true, read}, 0)
+ )},
+ {"read 1 byte from empty file", ?_assertMatch(
+ {_, {ok, <<>>}},
+ read({t_file, <<>>, true, read}, 1)
+ )},
+ {"read zero bytes from nonempty file", ?_assertMatch(
+ {_, {ok, <<>>}},
+ read({t_file, <<"hallo world">>, true, read}, 0)
+ )},
+ {"read 1 byte from nonempty file", ?_assertMatch(
+ {_, {ok, <<"h">>}},
+ read({t_file, <<"hallo world">>, true, read}, 1)
+ )},
+ {"read a zillion bytes from nonempty file", ?_assertMatch(
+ {_, {ok, <<"hallo world">>}},
+ read({t_file, <<"hallo world">>, true, read}, 65536)
+ )},
+ {"read 0 byte from file in write mode", ?_assertMatch(
+ {_, {error, write_mode}},
+ read({t_file, <<>>, true, write}, 0)
+ )},
+ {"read 1 byte from file in write mode", ?_assertMatch(
+ {_, {error, write_mode}},
+ read({t_file, <<>>, true, write}, 1)
+ )}
+ ]
+ }.
+
+
+read_exact(File, Bytes) -> thrift_file_transport:read_exact(File, Bytes).
+
+read_exact_test_() ->
+ {setup,
+ fun() ->
+ meck:new(file, [unstick, passthrough]),
+ meck:expect(file, read, fun(Bin, N) ->
+ {Result, _} = split_binary(Bin, min(iolist_size(Bin), N)),
+ {ok, Result}
+ end)
+ end,
+ fun(_) -> meck:unload(file) end,
+ [
+ {"read exactly zero bytes from empty file", ?_assertMatch(
+ {_, {ok, <<>>}},
+ read_exact({t_file, <<>>, true, read}, 0)
+ )},
+ {"read exactly 1 byte from empty file", ?_assertMatch(
+ {_, {error, eof}},
+ read_exact({t_file, <<>>, true, read}, 1)
+ )},
+ {"read exactly zero bytes from nonempty file", ?_assertMatch(
+ {_, {ok, <<>>}},
+ read_exact({t_file, <<"hallo world">>, true, read}, 0)
+ )},
+ {"read exactly 1 byte from nonempty file", ?_assertMatch(
+ {_, {ok, <<"h">>}},
+ read_exact({t_file, <<"hallo world">>, true, read}, 1)
+ )},
+ {"read exactly a zillion bytes from nonempty file", ?_assertMatch(
+ {_, {error, eof}},
+ read_exact({t_file, <<"hallo world">>, true, read}, 65536)
+ )},
+ {"read exactly 0 byte from file in write mode", ?_assertMatch(
+ {_, {error, write_mode}},
+ read_exact({t_file, <<>>, true, write}, 0)
+ )},
+ {"read exactly 1 byte from file in write mode", ?_assertMatch(
+ {_, {error, write_mode}},
+ read_exact({t_file, <<>>, true, write}, 1)
+ )}
+ ]
+ }.
+
+
+write(File, Data) -> thrift_file_transport:write(File, Data).
+
+write_test_() ->
+ {setup,
+ fun() ->
+ meck:new(file, [unstick, passthrough]),
+ meck:expect(file, write, fun(_, _) -> ok end)
+ end,
+ fun(_) -> meck:unload(file) end,
+ [
+ {"write empty list to file", ?_assertMatch(
+ {{t_file, a_fake_file, true, write}, ok},
+ write({t_file, a_fake_file, true, write}, [])
+ )},
+ {"write empty binary to file", ?_assertMatch(
+ {{t_file, a_fake_file, true, write}, ok},
+ write({t_file, a_fake_file, true, write}, <<>>)
+ )},
+ {"write a list to file", ?_assertMatch(
+ {{t_file, a_fake_file, true, write}, ok},
+ write({t_file, a_fake_file, true, write}, "hallo world")
+ )},
+ {"write a binary to file", ?_assertMatch(
+ {{t_file, a_fake_file, true, write}, ok},
+ write({t_file, a_fake_file, true, write}, <<"hallo world">>)
+ )},
+ {"write a binary to file in read mode", ?_assertMatch(
+ {_, {error, read_mode}},
+ write({t_file, a_fake_file, true, read}, <<"hallo world">>)
+ )},
+ {"write a list to file in read mode", ?_assertMatch(
+ {_, {error, read_mode}},
+ write({t_file, a_fake_file, true, read}, "hallo world")
+ )}
+ ]
+ }.
+
+
+flush(Transport) -> thrift_file_transport:flush(Transport).
+
+flush_test_() ->
+ {setup,
+ fun() ->
+ meck:new(file, [unstick, passthrough]),
+ meck:expect(file, sync, fun(_File) -> ok end)
+ end,
+ fun(_) -> meck:unload(file) end,
+ [
+ {"flush file", ?_assertMatch(
+ {{t_file, a_fake_file, true, write}, ok},
+ flush({t_file, a_fake_file, true, write})
+ )}
+ ]
+ }.
+
+
+close(Transport) -> thrift_file_transport:close(Transport).
+
+close_test_() ->
+ {setup,
+ fun() ->
+ meck:new(file, [unstick, passthrough]),
+ meck:expect(file, close, fun(_) -> ok end)
+ end,
+ fun(_) -> meck:unload(file) end,
+ [
+ {"close file", ?_assertMatch(
+ {{t_file, a_fake_file, true, write}, ok},
+ close({t_file, a_fake_file, true, write})
+ )}
+ ]
+ }. \ No newline at end of file
diff --git a/lib/erl/test/test_thrift_framed_transport.erl b/lib/erl/test/test_thrift_framed_transport.erl
new file mode 100644
index 000000000..8a538a53a
--- /dev/null
+++ b/lib/erl/test/test_thrift_framed_transport.erl
@@ -0,0 +1,404 @@
+%%
+%% Licensed to the Apache Software Foundation (ASF) under one
+%% or more contributor license agreements. See the NOTICE file
+%% distributed with this work for additional information
+%% regarding copyright ownership. The ASF licenses this file
+%% to you 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(test_thrift_framed_transport).
+-include_lib("eunit/include/eunit.hrl").
+
+
+new(Transport) -> thrift_framed_transport:new(Transport).
+
+new_test_() ->
+ [
+ {"new framed membuffer", ?_assertMatch(
+ {ok, {t_transport, thrift_framed_transport, {t_framed,
+ {t_transport, thrift_membuffer_transport, {t_membuffer, []}},
+ [],
+ []
+ }}},
+ new({t_transport, thrift_membuffer_transport, {t_membuffer, []}})
+ )}
+ ].
+
+
+read(Frame, Bytes) -> thrift_framed_transport:read(Frame, Bytes).
+
+read_test_() ->
+ [
+ {"read zero bytes from an empty framed membuffer", ?_assertMatch(
+ {
+ {t_framed,
+ {t_transport, thrift_membuffer_transport, {t_membuffer, <<>>}},
+ [],
+ []
+ },
+ {ok, <<>>}
+ },
+ read(
+ {t_framed,
+ {t_transport, thrift_membuffer_transport, {t_membuffer, <<>>}},
+ [],
+ []
+ },
+ 0
+ )
+ )},
+ {"read 1 byte from an empty framed membuffer", ?_assertMatch(
+ {_, {error, eof}},
+ read(
+ {t_framed,
+ {t_transport, thrift_membuffer_transport, {t_membuffer, <<>>}},
+ [],
+ []
+ },
+ 1
+ )
+ )},
+ {"read zero bytes from nonempty framed membuffer", ?_assertMatch(
+ {
+ {t_framed,
+ {t_transport, thrift_membuffer_transport, {t_membuffer,
+ <<0, 0, 0, 11, "hallo world">>
+ }},
+ [],
+ []
+ },
+ {ok, <<>>}
+ },
+ read(
+ {t_framed,
+ {t_transport, thrift_membuffer_transport, {t_membuffer,
+ <<0, 0, 0, 11, "hallo world">>
+ }},
+ [],
+ []
+ },
+ 0
+ )
+ )},
+ {"read 1 byte from nonempty framed membuffer", ?_assertMatch(
+ {
+ {t_framed,
+ {t_transport, thrift_membuffer_transport, {t_membuffer, <<>>}},
+ <<"allo world">>,
+ []
+ },
+ {ok, <<"h">>}
+ },
+ read(
+ {t_framed,
+ {t_transport, thrift_membuffer_transport, {t_membuffer,
+ <<0, 0, 0, 11, "hallo world">>
+ }},
+ [],
+ []
+ },
+ 1
+ )
+ )},
+ {"read 1 byte from nonempty buffer", ?_assertMatch(
+ {
+ {t_framed,
+ {t_transport, thrift_membuffer_transport, {t_membuffer, <<>>}},
+ <<"allo world">>,
+ []
+ },
+ {ok, <<"h">>}
+ },
+ read(
+ {t_framed,
+ {t_transport, thrift_membuffer_transport, {t_membuffer, <<>>}},
+ <<"hallo world">>,
+ []
+ },
+ 1
+ )
+ )},
+ {"read a zillion bytes from nonempty framed membuffer", ?_assertMatch(
+ {
+ {t_framed,
+ {t_transport, thrift_membuffer_transport, {t_membuffer, <<>>}},
+ <<>>,
+ []
+ },
+ {ok, <<"hallo world">>}
+ },
+ read(
+ {t_framed,
+ {t_transport, thrift_membuffer_transport, {t_membuffer,
+ <<0, 0, 0, 11, "hallo world">>
+ }},
+ [],
+ []
+ },
+ 65536
+ )
+ )}
+ ].
+
+
+read_exact(Frame, Bytes) -> thrift_framed_transport:read_exact(Frame, Bytes).
+
+read_exact_test_() ->
+ [
+ {"read exactly zero bytes from an empty framed membuffer", ?_assertMatch(
+ {
+ {t_framed,
+ {t_transport, thrift_membuffer_transport, {t_membuffer, <<>>}},
+ <<>>,
+ []
+ },
+ {ok, <<>>}
+ },
+ read_exact(
+ {t_framed,
+ {t_transport, thrift_membuffer_transport, {t_membuffer, <<>>}},
+ [],
+ []
+ },
+ 0
+ )
+ )},
+ {"read exactly 1 byte from an empty framed membuffer", ?_assertMatch(
+ {_, {error, eof}},
+ read_exact(
+ {t_framed,
+ {t_transport, thrift_membuffer_transport, {t_membuffer, <<>>}},
+ [],
+ []
+ },
+ 1
+ )
+ )},
+ {"read exactly zero bytes from nonempty framed membuffer", ?_assertMatch(
+ {
+ {t_framed,
+ {t_transport, thrift_membuffer_transport, {t_membuffer,
+ <<0, 0, 0, 11, "hallo world">>
+ }},
+ <<>>,
+ []
+ },
+ {ok, <<>>}
+ },
+ read_exact(
+ {t_framed,
+ {t_transport, thrift_membuffer_transport, {t_membuffer,
+ <<0, 0, 0, 11, "hallo world">>
+ }},
+ [],
+ []
+ },
+ 0
+ )
+ )},
+ {"read exactly 1 byte from nonempty framed membuffer", ?_assertMatch(
+ {
+ {t_framed,
+ {t_transport, thrift_membuffer_transport, {t_membuffer, <<>>}},
+ <<"allo world">>,
+ []
+ },
+ {ok, <<"h">>}
+ },
+ read_exact(
+ {t_framed,
+ {t_transport, thrift_membuffer_transport, {t_membuffer,
+ <<0, 0, 0, 11, "hallo world">>
+ }},
+ [],
+ []
+ },
+ 1
+ )
+ )},
+ {"read exactly 1 byte from nonempty buffer", ?_assertMatch(
+ {
+ {t_framed,
+ {t_transport, thrift_membuffer_transport, {t_membuffer, <<>>}},
+ <<"allo world">>,
+ []
+ },
+ {ok, <<"h">>}
+ },
+ read_exact(
+ {t_framed,
+ {t_transport, thrift_membuffer_transport, {t_membuffer, <<>>}},
+ <<"hallo world">>,
+ []
+ },
+ 1
+ )
+ )},
+ {"read exactly a zillion bytes from nonempty framed membuffer", ?_assertMatch(
+ {
+ {t_framed,
+ {t_transport, thrift_membuffer_transport, {t_membuffer, <<>>}},
+ [[],<<"hallo world">>],
+ []
+ },
+ {error, eof}
+ },
+ read_exact(
+ {t_framed,
+ {t_transport, thrift_membuffer_transport, {t_membuffer,
+ <<0, 0, 0, 11, "hallo world">>
+ }},
+ [],
+ []
+ },
+ 65536
+ )
+ )}
+ ].
+
+
+write(Framed, Data) -> thrift_framed_transport:write(Framed, Data).
+
+write_test_() ->
+ [
+ {"write empty list to empty framed membuffer", ?_assertMatch(
+ {
+ {t_framed,
+ {t_transport, thrift_membuffer_transport, {t_membuffer, <<>>}},
+ [],
+ [[], []]
+ },
+ ok
+ },
+ write(
+ {t_framed,
+ {t_transport, thrift_membuffer_transport, {t_membuffer, <<>>}},
+ [],
+ []
+ },
+ []
+ )
+ )},
+ {"write empty list to nonempty framed membuffer", ?_assertMatch(
+ {
+ {t_framed,
+ {t_transport, thrift_membuffer_transport, {t_membuffer, <<>>}},
+ [],
+ [["hallo world"], []]
+ },
+ ok
+ },
+ write(
+ {t_framed,
+ {t_transport, thrift_membuffer_transport, {t_membuffer, <<>>}},
+ [],
+ ["hallo world"]
+ },
+ []
+ )
+ )},
+ {"write empty binary to empty framed membuffer", ?_assertMatch(
+ {
+ {t_framed,
+ {t_transport, thrift_membuffer_transport, {t_membuffer, <<>>}},
+ [],
+ [[], <<>>]
+ },
+ ok
+ },
+ write(
+ {t_framed,
+ {t_transport, thrift_membuffer_transport, {t_membuffer, <<>>}},
+ [],
+ []
+ },
+ <<>>
+ )
+ )},
+ {"write empty binary to nonempty framed membuffer", ?_assertMatch(
+ {
+ {t_framed,
+ {t_transport, thrift_membuffer_transport, {t_membuffer, <<>>}},
+ [],
+ [["hallo world"], <<>>]
+ },
+ ok
+ },
+ write(
+ {t_framed,
+ {t_transport, thrift_membuffer_transport, {t_membuffer, <<>>}},
+ [],
+ ["hallo world"]
+ },
+ <<>>
+ )
+ )}
+ ].
+
+
+flush(Transport) -> thrift_framed_transport:flush(Transport).
+
+flush_test_() ->
+ [
+ {"flush empty framed membuffer", ?_assertMatch(
+ {{t_framed,
+ {t_transport, thrift_membuffer_transport, {t_membuffer, <<>>}},
+ [],
+ []
+ },
+ ok
+ },
+ flush({t_framed,
+ {t_transport, thrift_membuffer_transport, {t_membuffer, <<>>}},
+ [],
+ []
+ })
+ )},
+ {"flush nonempty framed membuffer", ?_assertMatch(
+ {{t_framed,
+ {t_transport, thrift_membuffer_transport, {t_membuffer,
+ [<<>>, [<<0, 0, 0, 11>>, <<"hallo world">>]]
+ }},
+ [],
+ []
+ },
+ ok
+ },
+ flush({t_framed,
+ {t_transport, thrift_membuffer_transport, {t_membuffer, <<>>}},
+ [],
+ <<"hallo world">>
+ })
+ )}
+ ].
+
+
+close(Transport) -> thrift_framed_transport:close(Transport).
+
+close_test_() ->
+ {"close framed membuffer", ?_assertMatch(
+ {{t_framed,
+ {t_transport, thrift_membuffer_transport, {t_membuffer, <<>>}},
+ [],
+ []
+ },
+ ok
+ },
+ close({t_framed,
+ {t_transport, thrift_membuffer_transport, {t_membuffer, <<>>}},
+ [],
+ []
+ })
+ )}.
+
diff --git a/lib/erl/test/test_thrift_membuffer_transport.erl b/lib/erl/test/test_thrift_membuffer_transport.erl
new file mode 100644
index 000000000..9689c7987
--- /dev/null
+++ b/lib/erl/test/test_thrift_membuffer_transport.erl
@@ -0,0 +1,167 @@
+%%
+%% Licensed to the Apache Software Foundation (ASF) under one
+%% or more contributor license agreements. See the NOTICE file
+%% distributed with this work for additional information
+%% regarding copyright ownership. The ASF licenses this file
+%% to you 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(test_thrift_membuffer_transport).
+-include_lib("eunit/include/eunit.hrl").
+
+
+new() -> thrift_membuffer_transport:new().
+new(Data) -> thrift_membuffer_transport:new(Data).
+
+new_test_() ->
+ [
+ {"new empty membuffer", ?_assertMatch(
+ {ok, {_, _, {t_membuffer, []}}},
+ new()
+ )},
+ {"new membuffer with <<>>", ?_assertMatch(
+ {ok, {_, _, {t_membuffer, [<<>>]}}},
+ new(<<>>)
+ )},
+ {"new membuffer with []", ?_assertMatch(
+ {ok, {_, _, {t_membuffer, []}}},
+ new([])
+ )},
+ {"new membuffer with <<\"hallo world\">>", ?_assertMatch(
+ {ok, {_, _, {t_membuffer, [<<"hallo world">>]}}},
+ new(<<"hallo world">>)
+ )},
+ {"new membuffer with \"hallo world\"", ?_assertMatch(
+ {ok, {_, _, {t_membuffer, "hallo world"}}},
+ new("hallo world")
+ )}
+ ].
+
+
+read(Membuffer, Bytes) -> thrift_membuffer_transport:read(Membuffer, Bytes).
+
+read_test_() ->
+ [
+ {"read zero bytes from an empty membuffer", ?_assertMatch(
+ {_, {ok, <<>>}},
+ read({t_membuffer, []}, 0)
+ )},
+ {"read 1 byte from an empty membuffer", ?_assertMatch(
+ {_, {ok, <<>>}},
+ read({t_membuffer, []}, 1)
+ )},
+ {"read zero bytes from nonempty membuffer", ?_assertMatch(
+ {{t_membuffer, <<"hallo world">>}, {ok, <<>>}},
+ read({t_membuffer, [["hallo", " "], "world"]}, 0)
+ )},
+ {"read 1 byte from nonempty membuffer", ?_assertMatch(
+ {{t_membuffer, <<"allo world">>}, {ok, <<"h">>}},
+ read({t_membuffer, [["hallo", " "], "world"]}, 1)
+ )},
+ {"read a zillion bytes from nonempty buffer", ?_assertMatch(
+ {{t_membuffer, <<>>}, {ok, <<"hallo world">>}},
+ read({t_membuffer, [["hallo", " "], "world"]}, 65536)
+ )}
+ ].
+
+
+read_exact(Membuffer, Bytes) ->
+ thrift_membuffer_transport:read_exact(Membuffer, Bytes).
+
+read_exact_test_() ->
+ [
+ {"read exactly zero bytes from an empty membuffer", ?_assertMatch(
+ {_, {ok, <<>>}},
+ read_exact({t_membuffer, []}, 0)
+ )},
+ {"read exactly 1 byte from an empty membuffer", ?_assertMatch(
+ {_, {error, eof}},
+ read_exact({t_membuffer, []}, 1)
+ )},
+ {"read exactly zero bytes from nonempty membuffer", ?_assertMatch(
+ {{t_membuffer, <<"hallo world">>}, {ok, <<>>}},
+ read_exact({t_membuffer, [["hallo", " "], "world"]}, 0)
+ )},
+ {"read exactly 1 byte from nonempty membuffer", ?_assertMatch(
+ {{t_membuffer, <<"allo world">>}, {ok, <<"h">>}},
+ read_exact({t_membuffer, [["hallo", " "], "world"]}, 1)
+ )},
+ {"read exactly a zillion bytes from nonempty buffer", ?_assertMatch(
+ {{t_membuffer, [["hallo", " "], "world"]}, {error, eof}},
+ read_exact({t_membuffer, [["hallo", " "], "world"]}, 65536)
+ )}
+ ].
+
+
+write(Membuffer, Data) -> thrift_membuffer_transport:write(Membuffer, Data).
+
+write_test_() ->
+ [
+ {"write empty list to empty membuffer", ?_assertMatch(
+ {{t_membuffer, [[], []]}, ok},
+ write({t_membuffer, []}, [])
+ )},
+ {"write empty list to nonempty membuffer", ?_assertMatch(
+ {{t_membuffer, ["hallo world", []]}, ok},
+ write({t_membuffer, "hallo world"}, [])
+ )},
+ {"write empty binary to empty membuffer", ?_assertMatch(
+ {{t_membuffer, [[], <<>>]}, ok},
+ write({t_membuffer, []}, <<>>)
+ )},
+ {"write empty binary to nonempty membuffer", ?_assertMatch(
+ {{t_membuffer, ["hallo world", <<>>]}, ok},
+ write({t_membuffer, "hallo world"}, <<>>)
+ )},
+ {"write a list to empty membuffer", ?_assertMatch(
+ {{t_membuffer, [[], "hallo world"]}, ok},
+ write({t_membuffer, []}, "hallo world")
+ )},
+ {"write a list to nonempty membuffer", ?_assertMatch(
+ {{t_membuffer, [["hallo", " "], "world"]}, ok},
+ write({t_membuffer, ["hallo", " "]}, "world")
+ )},
+ {"write a binary to empty membuffer", ?_assertMatch(
+ {{t_membuffer, [[], <<"hallo world">>]}, ok},
+ write({t_membuffer, []}, <<"hallo world">>)
+ )},
+ {"write a binary to nonempty membuffer", ?_assertMatch(
+ {{t_membuffer, [["hallo", " "], <<"world">>]}, ok},
+ write({t_membuffer, ["hallo", " "]}, <<"world">>)
+ )}
+ ].
+
+
+flush(Transport) -> thrift_membuffer_transport:flush(Transport).
+
+flush_test_() ->
+ [
+ {"flush empty membuffer", ?_assertMatch(
+ {{t_membuffer, []}, ok},
+ flush({t_membuffer, []})
+ )},
+ {"flush nonempty membuffer", ?_assertMatch(
+ {{t_membuffer, [<<"hallo world">>]}, ok},
+ flush({t_membuffer, [<<"hallo world">>]})
+ )}
+ ].
+
+
+close(Transport) -> thrift_membuffer_transport:close(Transport).
+
+close_test_() ->
+ {"close membuffer", ?_assertMatch(
+ {{t_membuffer, _}, ok},
+ close({t_membuffer, []})
+ )}. \ No newline at end of file
diff --git a/lib/erl/test/test_thrift_socket_transport.erl b/lib/erl/test/test_thrift_socket_transport.erl
new file mode 100644
index 000000000..5bc0f246d
--- /dev/null
+++ b/lib/erl/test/test_thrift_socket_transport.erl
@@ -0,0 +1,199 @@
+%%
+%% Licensed to the Apache Software Foundation (ASF) under one
+%% or more contributor license agreements. See the NOTICE file
+%% distributed with this work for additional information
+%% regarding copyright ownership. The ASF licenses this file
+%% to you 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(test_thrift_socket_transport).
+-include_lib("eunit/include/eunit.hrl").
+
+
+new(Socket) -> thrift_socket_transport:new(Socket).
+new(Socket, Opts) -> thrift_socket_transport:new(Socket, Opts).
+
+new_test_() ->
+ [
+ {"new socket", ?_assertMatch(
+ {ok, {_, thrift_socket_transport, {t_socket, a_fake_socket, 60000, []}}},
+ new(a_fake_socket)
+ )},
+ {"new socket with no options", ?_assertMatch(
+ {ok, {_, thrift_socket_transport, {t_socket, a_fake_socket, 60000, []}}},
+ new(a_fake_socket, [])
+ )},
+ {"new socket with integer timeout", ?_assertMatch(
+ {ok, {_, thrift_socket_transport, {t_socket, a_fake_socket, 5000, []}}},
+ new(a_fake_socket, [{recv_timeout, 5000}])
+ )},
+ {"new socket with infinity timeout", ?_assertMatch(
+ {ok, {_, thrift_socket_transport, {t_socket, a_fake_socket, infinity, []}}},
+ new(a_fake_socket, [{recv_timeout, infinity}])
+ )}
+ ].
+
+
+read(Socket, Bytes) -> thrift_socket_transport:read(Socket, Bytes).
+
+read_test_() ->
+ {setup,
+ fun() ->
+ meck:new(gen_tcp, [unstick, passthrough]),
+ meck:expect(gen_tcp, recv, fun(Bin, 0, _) -> {ok, Bin} end)
+ end,
+ fun(_) -> meck:unload(gen_tcp) end,
+ [
+ {"read zero bytes from empty socket", ?_assertMatch(
+ {_, {ok, <<>>}},
+ read({t_socket, <<>>, 60000, []}, 0)
+ )},
+ {"read 1 byte from empty socket", ?_assertMatch(
+ {_, {ok, <<>>}},
+ read({t_socket, <<>>, 60000, []}, 1)
+ )},
+ {"read zero bytes from nonempty socket", ?_assertMatch(
+ {{t_socket, _, _, _}, {ok, <<>>}},
+ read({t_socket, <<"hallo world">>, 60000, []}, 0)
+ )},
+ {"read 1 byte from nonempty socket", ?_assertMatch(
+ {{t_socket, _, _, <<"allo world">>}, {ok, <<"h">>}},
+ read({t_socket, <<"hallo world">>, 60000, []}, 1)
+ )},
+ {"read a zillion bytes from nonempty socket", ?_assertMatch(
+ {{t_socket, _, _, <<>>}, {ok, <<"hallo world">>}},
+ read({t_socket, <<"hallo world">>, 60000, []}, 65536)
+ )},
+ {"read 1 byte from previously buffered socket", ?_assertMatch(
+ {{t_socket, _, _, <<"allo">>}, {ok, <<"h">>}},
+ read({t_socket, <<" world">>, 60000, <<"hallo">>}, 1)
+ )},
+ {"read 6 byte from previously buffered socket", ?_assertMatch(
+ {{t_socket, _, _, <<"world">>}, {ok, <<"hallo ">>}},
+ read({t_socket, <<" world">>, 60000, <<"hallo">>}, 6)
+ )},
+ {"read a zillion bytes from previously buffered socket", ?_assertMatch(
+ {{t_socket, _, _, <<>>}, {ok, <<"hallo world">>}},
+ read({t_socket, <<" world">>, 60000, <<"hallo">>}, 65536)
+ )}
+ ]
+ }.
+
+
+read_exact(Socket, Bytes) -> thrift_socket_transport:read_exact(Socket, Bytes).
+
+read_exact_test_() ->
+ {setup,
+ fun() ->
+ meck:new(gen_tcp, [unstick, passthrough]),
+ meck:expect(gen_tcp, recv, fun(Bin, N, _) ->
+ case N of
+ 0 -> {ok, Bin};
+ 1 -> {ok, <<"h">>};
+ N when N > 2 -> {error, timeout}
+ end
+ end),
+ meck:expect(gen_tcp, close, fun(_) -> ok end)
+ end,
+ fun(_) -> meck:unload(gen_tcp) end,
+ [
+ {"read_exact zero bytes from empty socket", ?_assertMatch(
+ {_, {ok, <<>>}},
+ read_exact({t_socket, <<>>, 60000, []}, 0)
+ )},
+ {"read_exact zero bytes from nonempty socket", ?_assertMatch(
+ {{t_socket, _, _, _}, {ok, <<>>}},
+ read_exact({t_socket, <<"hallo world">>, 60000, []}, 0)
+ )},
+ {"read_exact 1 byte from nonempty socket", ?_assertMatch(
+ {{t_socket, _, _, []}, {ok, <<"h">>}},
+ read_exact({t_socket, <<"hallo world">>, 60000, []}, 1)
+ )},
+ {"read_exact a zillion bytes from nonempty socket", ?_assertMatch(
+ {{t_socket, _, _, []}, {error, timeout}},
+ read_exact({t_socket, <<"hallo world">>, 60000, []}, 65536)
+ )},
+ {"read_exact 1 byte from previously buffered socket", ?_assertMatch(
+ {{t_socket, _, _, <<"allo">>}, {ok, <<"h">>}},
+ read_exact({t_socket, <<" world">>, 60000, <<"hallo">>}, 1)
+ )},
+ {"read_exact 6 byte from previously buffered socket", ?_assertMatch(
+ {{t_socket, _, _, []}, {ok, <<"more h">>}},
+ read_exact({t_socket, <<"hallo">>, 60000, <<"more ">>}, 6)
+ )},
+ {"read_exact a zillion bytes from previously buffered socket", ?_assertMatch(
+ {{t_socket, _, _, <<"hallo">>}, {error, timeout}},
+ read_exact({t_socket, <<" world">>, 60000, <<"hallo">>}, 65536)
+ )}
+ ]
+ }.
+
+
+write(Socket, Data) -> thrift_socket_transport:write(Socket, Data).
+
+write_test_() ->
+ {setup,
+ fun() ->
+ meck:new(gen_tcp, [unstick, passthrough]),
+ meck:expect(gen_tcp, send, fun(_, _) -> ok end)
+ end,
+ fun(_) -> meck:unload(gen_tcp) end,
+ [
+ {"write empty list to socket", ?_assertMatch(
+ {{t_socket, a_fake_socket, 60000, []}, ok},
+ write({t_socket, a_fake_socket, 60000, []}, [])
+ )},
+ {"write empty binary to socket", ?_assertMatch(
+ {{t_socket, a_fake_socket, 60000, []}, ok},
+ write({t_socket, a_fake_socket, 60000, []}, <<>>)
+ )},
+ {"write a list to socket", ?_assertMatch(
+ {{t_socket, a_fake_socket, 60000, []}, ok},
+ write({t_socket, a_fake_socket, 60000, []}, "hallo world")
+ )},
+ {"write a binary to socket", ?_assertMatch(
+ {{t_socket, a_fake_socket, 60000, []}, ok},
+ write({t_socket, a_fake_socket, 60000, []}, <<"hallo world">>)
+ )}
+ ]
+ }.
+
+
+flush(Transport) -> thrift_socket_transport:flush(Transport).
+
+flush_test_() ->
+ [
+ {"flush socket", ?_assertMatch(
+ {{t_socket, a_fake_socket, 60000, []}, ok},
+ flush({t_socket, a_fake_socket, 60000, []})
+ )}
+ ].
+
+
+close(Transport) -> thrift_socket_transport:close(Transport).
+
+close_test_() ->
+ {setup,
+ fun() ->
+ meck:new(gen_tcp, [unstick, passthrough]),
+ meck:expect(gen_tcp, close, fun(_) -> ok end)
+ end,
+ fun(_) -> meck:unload(gen_tcp) end,
+ [
+ {"close membuffer", ?_assertMatch(
+ {{t_socket, a_fake_socket, 60000, []}, ok},
+ close({t_socket, a_fake_socket, 60000, []})
+ )}
+ ]
+ }. \ No newline at end of file