diff --git a/.travis.yml b/.travis.yml
index 2a3f919..7f4feec 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -2,6 +2,6 @@ language: erlang
otp_release:
- 17.1
- 17.3
-script: "make compile build-plt eunit etest dialyze"
+script: "make compile build-plt eunit dialyze"
notifications:
email: false
diff --git a/Makefile b/Makefile
index a500fe5..ed89d54 100644
--- a/Makefile
+++ b/Makefile
@@ -1,17 +1,12 @@
PROJECT=swirl
REBAR=./rebar
-.PHONY: deps doc
-
all: deps compile doc
build-plt: all
@dialyzer --build_plt --output_plt ~/.$(PROJECT).plt \
--apps erts kernel stdlib crypto public_key ssl
-check-plt:
- @dialyzer --check_plt --plt ~/.$(PROJECT).plt
-
clean:
@$(REBAR) clean
@rm -rf deps ebin doc/edoc-info doc/*.md README.md
@@ -36,9 +31,6 @@ eunit:
@echo "Running EUnit suite..."
@$(REBAR) skip_deps=true eunit
-etest:
- @echo "Running ETest suite..."
- @ERL_LIBS=deps erlc -pa ebin -o test test/*.erl
- @priv/etest-runner
+test: all eunit
-test: all eunit etest
+.PHONY: deps doc
diff --git a/README.md b/README.md
index dd00910..cf58803 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,4 @@
-
-
-# swirl #
+# swirl
__Authors:__ Louis-Philippe Gauthier.
@@ -8,12 +6,36 @@ Lightweight Distributed Stream Processor
[![Build Status](https://travis-ci.org/lpgauth/swirl.svg?branch=master)](https://travis-ci.org/lpgauth/swirl)
-#### Requirements: ####
-- Erlang 17.0+
-
-#### Examples: ####
-
-##### Starting a flow: #####
+### Requirements
+
+* Erlang 17.0 +
+
+### Environment variables
+
+
+
+ Name |
+ Type |
+ Default |
+ Description |
+
+
+ mappers_max |
+ pos_integer() |
+ 100 |
+ maximum number of mappers |
+
+
+ reducers_max |
+ pos_integer() |
+ 100 |
+ maximum number of reducers |
+
+
+
+## Examples
+
+#### Starting a flow
```erlang
ok = application:start(swirl),
@@ -35,7 +57,7 @@ swirl_stream:emit(StreamName, Event),
ok = swirl_flow:stop(Flow)
```
-##### Implementing a flow: #####
+#### Implementing a flow
```erlang
-module(swirl_flow_example).
@@ -67,16 +89,16 @@ output(_Flow, _Period, Rows, OutputOpts) ->
io:format("rows: ~p~n", [Rows]),
```
-#### Stream Filter: ####
-##### Examples: #####
+#### Stream Filter
```erlang
-
exchange_id = 3 AND bidder_id IS NOT NULL
flight_id in (10, 12, 23) OR tag_id = 20
buyer_id notnull AND seller_id > 103
```
-##### Swirl QL: #####
+
+#### Swirl QL
+
variables:
```
@@ -108,36 +130,39 @@ null operators:
null | notnull
```
-#### Resource Limitation: ####
+## TODO
+* node discovery
+* boolean expression indexing
-configurable via:
+## Tests
-```erlang
-application:set_env(swirl, mappers_max, 140)
-application:set_env(swirl, reducers_max, 200)
+```makefile
+make eunit
+make build-plt && make dialyze
```
-#### TODO: ####
-- node discovery
-- boolean expression indexing
+## License
+```license
+The MIT License (MIT)
-## Modules ##
+Copyright (c) 2015 Louis-Philippe Gauthier
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
-
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+```
diff --git a/doc/README.md b/doc/README.md
deleted file mode 100644
index f1327a8..0000000
--- a/doc/README.md
+++ /dev/null
@@ -1,143 +0,0 @@
-
-
-# swirl #
-
-__Authors:__ Louis-Philippe Gauthier.
-
-Lightweight Distributed Stream Processor
-
-[![Build Status](https://travis-ci.org/lpgauth/swirl.svg?branch=master)](https://travis-ci.org/lpgauth/swirl)
-
-#### Requirements: ####
-- Erlang 17.0+
-
-#### Examples: ####
-
-##### Starting a flow: #####
-
-```erlang
-ok = application:start(swirl),
-
-FlowMod = swirl_flow_example,
-FlowOpts = [
- {stream_names, [delivery]},
- {stream_filter, "exchange_id = 3 AND bidder_id IS NOT NULL"}
-],
-MapperNodes = [node()],
-ReducerNode = node(),
-{ok, Flow} = swirl_flow:start(FlowMod, FlowOpts, MapperNodes, ReducerNode),
-
-StreamName = delivery,
-Event = #{exchange_id => 1, bidder_id => 10},
-
-swirl_stream:emit(StreamName, Event),
-
-ok = swirl_flow:stop(Flow)
-```
-
-##### Implementing a flow: #####
-
-```erlang
--module(swirl_flow_example).
--include_lib("swirl/include/swirl.hrl").
-
--behavior(swirl_flow).
--export([
- map/3,
- reduce/3,
- output/4
-]).
-
-%% swirl_flow callbacks
-map(StreamName, Event, _MapperOpts) ->
- Type = ?L(type, Event),
- ExchangeId = ?L(exchange_id, Event),
- BidderId = ?L(bidder_id, Event),
-
- Key = {Type, StreamName, ExchangeId, BidderId},
- CounterIncrements = {1, 10},
-
- {Key, CounterIncrements}.
-
-reduce(_Flow, Row, _ReducerOpts) ->
- Row.
-
-output(_Flow, _Period, Rows, OutputOpts) ->
- %% do something with the output
- io:format("rows: ~p~n", [Rows]),
-```
-
-#### Stream Filter: ####
-##### Examples: #####
-
-```erlang
-
-exchange_id = 3 AND bidder_id IS NOT NULL
-flight_id in (10, 12, 23) OR tag_id = 20
-buyer_id notnull AND seller_id > 103
-```
-##### Swirl QL: #####
-variables:
-
-```
-atom()
-```
-values:
-
-```
-integer() | float() | binary()
-```
-boolean operators:
-
-```
-'and' | 'or'
-```
-comparison operators:
-
-```
-'<' | '<=' | '=' | '>=' | '>' | '<>'
-```
-inclusion operators:
-
-```
-in | notin
-```
-null operators:
-
-```
-null | notnull
-```
-
-#### Resource Limitation: ####
-
-configurable via:
-
-```erlang
-application:set_env(swirl, mappers_max, 140)
-application:set_env(swirl, reducers_max, 200)
-```
-
-#### TODO: ####
-- node discovery
-- boolean expression indexing
-
-
-## Modules ##
-
-
-
-
diff --git a/doc/edoc-info b/doc/edoc-info
index 51008b9..4e0a270 100644
--- a/doc/edoc-info
+++ b/doc/edoc-info
@@ -1,6 +1,5 @@
%% encoding: UTF-8
{application,swirl}.
-{packages,[]}.
{modules,[swirl,swirl_code_server,swirl_config,swirl_ets_manager,swirl_flow,
swirl_mapper,swirl_ql,swirl_ql_lexer,swirl_ql_parser,swirl_reducer,
swirl_stream,swirl_sup,swirl_tracker,swirl_utils]}.
diff --git a/doc/overview.edoc b/doc/overview.edoc
deleted file mode 100644
index 48039a4..0000000
--- a/doc/overview.edoc
+++ /dev/null
@@ -1,101 +0,0 @@
-@author Louis-Philippe Gauthier
-@doc Lightweight Distributed Stream Processor
-
-[![Build Status](https://travis-ci.org/lpgauth/swirl.svg?branch=master)](https://travis-ci.org/lpgauth/swirl)
-
-#### Requirements: ####
-- Erlang 17.0+
-
-#### Examples: ####
-
-##### Starting a flow: #####
-
-ok = application:start(swirl),
-
-FlowMod = swirl_flow_example,
-FlowOpts = [
- {stream_names, [delivery]},
- {stream_filter, "exchange_id = 3 AND bidder_id IS NOT NULL"}
-],
-MapperNodes = [node()],
-ReducerNode = node(),
-{ok, Flow} = swirl_flow:start(FlowMod, FlowOpts, MapperNodes, ReducerNode),
-
-StreamName = delivery,
-Event = #{exchange_id => 1, bidder_id => 10},
-
-swirl_stream:emit(StreamName, Event),
-
-ok = swirl_flow:stop(Flow)
-
-##### Implementing a flow: #####
-
--module(swirl_flow_example).
--include_lib("swirl/include/swirl.hrl").
-
--behavior(swirl_flow).
--export([
- map/3,
- reduce/3,
- output/4
-]).
-
-%% swirl_flow callbacks
-map(StreamName, Event, _MapperOpts) ->
- Type = ?L(type, Event),
- ExchangeId = ?L(exchange_id, Event),
- BidderId = ?L(bidder_id, Event),
-
- Key = {Type, StreamName, ExchangeId, BidderId},
- CounterIncrements = {1, 10},
-
- {Key, CounterIncrements}.
-
-reduce(_Flow, Row, _ReducerOpts) ->
- Row.
-
-output(_Flow, _Period, Rows, OutputOpts) ->
- %% do something with the output
- io:format("rows: ~p~n", [Rows]),
-
-
-#### Stream Filter: ####
-
-##### Examples: #####
-
-exchange_id = 3 AND bidder_id IS NOT NULL
-flight_id in (10, 12, 23) OR tag_id = 20
-buyer_id notnull AND seller_id > 103
-
-##### Swirl QL: #####
-variables:
-atom()
-
-values:
-integer() | float() | binary()
-
-boolean operators:
-'and' | 'or'
-
-comparison operators:
-=' | '>' | '<>']]>
-
-inclusion operators:
-in | notin
-
-null operators:
-null | notnull
-
-#### Resource Limitation: ####
-
-configurable via:
-
-application:set_env(swirl, mappers_max, 140)
-application:set_env(swirl, reducers_max, 200)
-
-#### TODO: ####
-
-- node discovery
-- boolean expression indexing
-
-@end
\ No newline at end of file
diff --git a/doc/swirl.md b/doc/swirl.md
index 18bad36..ccda25c 100644
--- a/doc/swirl.md
+++ b/doc/swirl.md
@@ -5,6 +5,7 @@
* [Function Details](#functions)
__Behaviours:__ [`application`](application.md).
+
## Function Index ##
@@ -21,24 +22,20 @@ __Behaviours:__ [`application`](application.md).
### start/0 ###
-
start() -> ok
-
### start/2 ###
`start(StartType, StartArgs) -> any()`
-
### stop/1 ###
`stop(State) -> any()`
-
diff --git a/doc/swirl_code_server.md b/doc/swirl_code_server.md
index f8bc41f..8a6893c 100644
--- a/doc/swirl_code_server.md
+++ b/doc/swirl_code_server.md
@@ -17,12 +17,10 @@ __Behaviours:__ [`gen_server`](gen_server.md).
### module_vsn() ###
-
module_vsn() = pos_integer()
-
## Function Index ##
@@ -41,72 +39,60 @@ module_vsn() = pos_integer()
`code_change(OldVsn, State, Extra) -> any()`
-
### get_module/3 ###
-
get_module(Node::node(), Module::module(), ModuleVsn::module_vsn()) -> ok
-
### handle_call/3 ###
`handle_call(Request, From, State) -> any()`
-
### handle_cast/2 ###
`handle_cast(Msg, State) -> any()`
-
### handle_info/2 ###
`handle_info(Info, State) -> any()`
-
### init/1 ###
`init(X1) -> any()`
-
### start_link/0 ###
-
start_link() -> {ok, pid()}
-
### terminate/2 ###
`terminate(Reason, State) -> any()`
-
### version/1 ###
-
version(Module::module()) -> {ok, module_vsn()} | {error, term()}
-
diff --git a/doc/swirl_config.md b/doc/swirl_config.md
index 5ef2e21..5d77174 100644
--- a/doc/swirl_config.md
+++ b/doc/swirl_config.md
@@ -4,7 +4,6 @@
* [Function Index](#index)
* [Function Details](#functions)
-
## Function Index ##
@@ -21,87 +20,71 @@
### flows/0 ###
-
flows() -> [tuple()]
-
### flows_count/0 ###
-
flows_count() -> non_neg_integer()
-
### mappers/0 ###
-
mappers() -> [tuple()]
-
### mappers_count/0 ###
-
mappers_count() -> non_neg_integer()
-
### mappers_max/0 ###
-
mappers_max() -> non_neg_integer()
-
### reducers/0 ###
-
reducers() -> [tuple()]
-
### reducers_count/0 ###
-
reducers_count() -> non_neg_integer()
-
### reducers_max/0 ###
-
reducers_max() -> non_neg_integer()
-
diff --git a/doc/swirl_ets_manager.md b/doc/swirl_ets_manager.md
index d8e8e40..75840ad 100644
--- a/doc/swirl_ets_manager.md
+++ b/doc/swirl_ets_manager.md
@@ -5,6 +5,7 @@
* [Function Details](#functions)
__Behaviours:__ [`gen_server`](gen_server.md).
+
## Function Index ##
@@ -23,72 +24,60 @@ __Behaviours:__ [`gen_server`](gen_server.md).
`code_change(OldVsn, State, Extra) -> any()`
-
### handle_call/3 ###
`handle_call(Request, From, State) -> any()`
-
### handle_cast/2 ###
`handle_cast(Msg, State) -> any()`
-
### handle_info/2 ###
`handle_info(Msg, State) -> any()`
-
### init/1 ###
`init(X1) -> any()`
-
### new_table/3 ###
-
new_table(Name::atom(), Options::[atom() | tuple()], Server::atom() | pid()) -> ok
-
### start_link/0 ###
-
start_link() -> {ok, pid()}
-
### table/3 ###
-
table(Name::atom(), Options::[atom() | tuple()], Server::atom() | pid()) -> ok
-
### terminate/2 ###
`terminate(Reason, State) -> any()`
-
diff --git a/doc/swirl_flow.md b/doc/swirl_flow.md
index 647cdd5..bc30e09 100644
--- a/doc/swirl_flow.md
+++ b/doc/swirl_flow.md
@@ -17,7 +17,6 @@ __This module defines the `swirl_flow` behaviour.__
Required callback func
### flow() ###
-
flow() = #flow{id = undefined | binary(), module = undefined | module(), module_vsn = undefined | module_vsn(), stream_filter = undefined | string(), stream_names = undefined | stream_names(), mapper_window = undefined | pos_integer(), mapper_nodes = undefined | [node()], mapper_opts = undefined | mapper_opts(), reducer_window = undefined | pos_integer(), reducer_node = undefined | node(), reducer_opts = undefined | reducer_opts(), reducer_skip = undefined | boolean(), output_opts = undefined | output_opts(), heartbeat = undefined | pos_integer(), window_sync = undefined | boolean(), started_at = undefined | erlang:timestamp(), start_node = undefined | node()}
@@ -25,11 +24,9 @@ flow() = #flow{id = undefined | binary(), module = undefined | module(), module_
-
### flow_opts() ###
-
flow_opts() = {heartbeat, pos_integer()} | {mapper_opts, mapper_opts()} | {mapper_window, pos_integer()} | {output_opts, output_opts()} | {reducer_opts, reducer_opts()} | {reducer_skip, boolean()} | {reducer_window, pos_integer()} | {stream_filter, string()} | {stream_names, stream_names()} | {window_sync, boolean()}
@@ -37,11 +34,9 @@ flow_opts() = {heartbeat, pos_integer()} | {mapper_opts, mapper_opts() ###
-
mapper_opts() = term()
@@ -49,11 +44,9 @@ mapper_opts() = term()
-
### module_vsn() ###
-
module_vsn() = pos_integer()
@@ -61,11 +54,9 @@ module_vsn() = pos_integer()
-
### output_opts() ###
-
output_opts() = term()
@@ -73,11 +64,9 @@ output_opts() = term()
-
### reducer_opts() ###
-
reducer_opts() = term()
@@ -85,11 +74,9 @@ reducer_opts() = term()
-
### stream_name() ###
-
stream_name() = atom()
@@ -97,16 +84,13 @@ stream_name() = atom()
-
### stream_names() ###
-
stream_names() = [stream_name()]
-
## Function Index ##
@@ -123,54 +107,44 @@ stream_names() = [stream_name()]
### lookup/1 ###
-
lookup(FlowId::binary() | flow()) -> undefined | flow()
-
### register/1 ###
-
register(Flow::flow()) -> true
-
### start/4 ###
-
start(FlowMod::atom(), FlowOpts::[flow_opts()], MapperNodes::[node()], ReducerNode::node()) -> {ok, flow()} | {error, flow_mod_undef | {bad_flow_opts, list()}}
-
### stop/1 ###
-
stop(Flow::flow()) -> ok
-
### unregister/1 ###
-
unregister(Flow::flow()) -> true
-
diff --git a/doc/swirl_mapper.md b/doc/swirl_mapper.md
index a638787..a804e3f 100644
--- a/doc/swirl_mapper.md
+++ b/doc/swirl_mapper.md
@@ -17,7 +17,6 @@ __Behaviours:__ [`gen_server`](gen_server.md).
### boolean_op() ###
-
boolean_op() = 'and' | 'or'
@@ -25,11 +24,9 @@ boolean_op() = 'and' | 'or'
-
### comparison_op() ###
-
comparison_op() = '<' | '<=' | '=' | '>=' | '>' | '<>'
@@ -37,11 +34,9 @@ comparison_op() = '<' | '<=' | '=' | '>=' | '>' | '<>'
-
### event() ###
-
event() = [{atom(), value()}]
@@ -49,11 +44,9 @@ event() = [{atom(), value()}]
-
### exp_tree() ###
-
exp_tree() = {boolean_op(), exp_tree(), exp_tree()} | {comparison_op(), variable(), value()} | {inclusion_op(), variable(), [value(), ...]} | {null_op(), variable()}
@@ -61,11 +54,9 @@ exp_tree() = {boolean_op(), flow() ###
-
flow() = #flow{id = undefined | binary(), module = undefined | module(), module_vsn = undefined | module_vsn(), stream_filter = undefined | string(), stream_names = undefined | stream_names(), mapper_window = undefined | pos_integer(), mapper_nodes = undefined | [node()], mapper_opts = undefined | mapper_opts(), reducer_window = undefined | pos_integer(), reducer_node = undefined | node(), reducer_opts = undefined | reducer_opts(), reducer_skip = undefined | boolean(), output_opts = undefined | output_opts(), heartbeat = undefined | pos_integer(), window_sync = undefined | boolean(), started_at = undefined | erlang:timestamp(), start_node = undefined | node()}
@@ -73,11 +64,9 @@ flow() = #flow{id = undefined | binary(), module = undefined | module(), module_
-
### inclusion_op() ###
-
inclusion_op() = in | notin
@@ -85,11 +74,9 @@ inclusion_op() = in | notin
-
### mapper_opts() ###
-
mapper_opts() = term()
@@ -97,11 +84,9 @@ mapper_opts() = term()
-
### module_vsn() ###
-
module_vsn() = pos_integer()
@@ -109,11 +94,9 @@ module_vsn() = pos_integer()
-
### null_op() ###
-
null_op() = null | notnull
@@ -121,11 +104,9 @@ null_op() = null | notnull
-
### output_opts() ###
-
output_opts() = term()
@@ -133,11 +114,9 @@ output_opts() = term()
-
### reducer_opts() ###
-
reducer_opts() = term()
@@ -145,11 +124,9 @@ reducer_opts() = term()
-
### stream() ###
-
stream() = #stream{flow_id = undefined | binary(), flow_mod = undefined | module(), flow_mod_vsn = undefined | module_vsn(), start_node = undefined | node(), exp_tree = undefined | exp_tree(), mapper_opts = undefined | mapper_opts(), table_id = undefined | ets:tab()}
@@ -157,11 +134,9 @@ stream() = #stream{flow_id = undefined | binary(), flow_mod = undefined | module
-
### stream_name() ###
-
stream_name() = atom()
@@ -169,11 +144,9 @@ stream_name() = atom()
-
### stream_names() ###
-
stream_names() = [stream_name()]
@@ -181,11 +154,9 @@ stream_names() = [stream_name()]
-
### value() ###
-
value() = integer() | float() | binary()
@@ -193,16 +164,13 @@ value() = integer() | float() | binary()
-
### variable() ###
-
variable() = atom()
-
## Function Index ##
@@ -221,94 +189,78 @@ variable() = atom()
`code_change(OldVsn, State, Extra) -> any()`
-
### handle_call/3 ###
`handle_call(Request, From, State) -> any()`
-
### handle_cast/2 ###
`handle_cast(Msg, State) -> any()`
-
### handle_info/2 ###
`handle_info(Msg, State) -> any()`
-
### init/1 ###
`init(Flow) -> any()`
-
### lookup/1 ###
-
lookup(FlowId::binary() | flow()) -> undefined | pid
-
### map/3 ###
-
map(StreamName::atom(), Event::event(), Stream::stream()) -> ok
-
### register/1 ###
-
register(Flow::flow()) -> true
-
### start/1 ###
-
start(Flow::flow()) -> {ok, pid()} | {error, mappers_max}
-
### terminate/2 ###
`terminate(Reason, State) -> any()`
-
### unregister/1 ###
-
unregister(Flow::flow()) -> true
-
diff --git a/doc/swirl_ql.md b/doc/swirl_ql.md
index b277c96..962f4ba 100644
--- a/doc/swirl_ql.md
+++ b/doc/swirl_ql.md
@@ -5,8 +5,6 @@
* [Function Index](#index)
* [Function Details](#functions)
-
-
## Data Types ##
@@ -17,7 +15,6 @@
### boolean_op() ###
-
boolean_op() = 'and' | 'or'
@@ -25,11 +22,9 @@ boolean_op() = 'and' | 'or'
-
### comparison_op() ###
-
comparison_op() = '<' | '<=' | '=' | '>=' | '>' | '<>'
@@ -37,11 +32,9 @@ comparison_op() = '<' | '<=' | '=' | '>=' | '>' | '<>'
-
### event() ###
-
event() = [{atom(), value()}]
@@ -49,11 +42,9 @@ event() = [{atom(), value()}]
-
### exp_tree() ###
-
exp_tree() = {boolean_op(), exp_tree(), exp_tree()} | {comparison_op(), variable(), value()} | {inclusion_op(), variable(), [value(), ...]} | {null_op(), variable()}
@@ -61,11 +52,9 @@ exp_tree() = {boolean_op(), inclusion_op() ###
-
inclusion_op() = in | notin
@@ -73,11 +62,9 @@ inclusion_op() = in | notin
-
### null_op() ###
-
null_op() = null | notnull
@@ -85,11 +72,9 @@ null_op() = null | notnull
-
### value() ###
-
value() = integer() | float() | binary()
@@ -97,16 +82,13 @@ value() = integer() | float() | binary()
-
### variable() ###
-
variable() = atom()
-
## Function Index ##
@@ -123,21 +105,17 @@ variable() = atom()
### evaluate/2 ###
-
evaluate(X1::exp_tree(), Vars::event()) -> boolean()
-
### parse/1 ###
-
parse(String::string() | binary()) -> {ok, exp_tree()} | {error, term()}
-
diff --git a/doc/swirl_ql_lexer.md b/doc/swirl_ql_lexer.md
index f2558da..8ef6719 100644
--- a/doc/swirl_ql_lexer.md
+++ b/doc/swirl_ql_lexer.md
@@ -4,7 +4,6 @@
* [Function Index](#index)
* [Function Details](#functions)
-
## Function Index ##
@@ -23,46 +22,39 @@
`format_error(X1) -> any()`
-
### string/1 ###
`string(String) -> any()`
-
### string/2 ###
`string(String, Line) -> any()`
-
### token/2 ###
`token(Cont, Chars) -> any()`
-
### token/3 ###
`token(X1, Chars, Line) -> any()`
-
### tokens/2 ###
`tokens(Cont, Chars) -> any()`
-
### tokens/3 ###
`tokens(X1, Chars, Line) -> any()`
-
diff --git a/doc/swirl_ql_parser.md b/doc/swirl_ql_parser.md
index e5049e3..7ce0fa2 100644
--- a/doc/swirl_ql_parser.md
+++ b/doc/swirl_ql_parser.md
@@ -5,8 +5,6 @@
* [Function Index](#index)
* [Function Details](#functions)
-
-
## Data Types ##
@@ -17,12 +15,10 @@
### yecc_ret() ###
-
yecc_ret() = {error, term()} | {ok, term()}
-
## Function Index ##
@@ -39,32 +35,26 @@ yecc_ret() = {error, term()} | {ok, term()}
### format_error/1 ###
-
format_error(Message::any()) -> [char() | list()]
-
### parse/1 ###
-
parse(Tokens::list()) -> yecc_ret()
-
### parse_and_scan/1 ###
-
parse_and_scan(X1::{function() | {atom(), atom()}, [term()]} | {atom(), atom(), [term()]}) -> yecc_ret()
-
diff --git a/doc/swirl_reducer.md b/doc/swirl_reducer.md
index c901642..e96ce71 100644
--- a/doc/swirl_reducer.md
+++ b/doc/swirl_reducer.md
@@ -17,7 +17,6 @@ __Behaviours:__ [`gen_server`](gen_server.md).
### flow() ###
-
flow() = #flow{id = undefined | binary(), module = undefined | module(), module_vsn = undefined | module_vsn(), stream_filter = undefined | string(), stream_names = undefined | stream_names(), mapper_window = undefined | pos_integer(), mapper_nodes = undefined | [node()], mapper_opts = undefined | mapper_opts(), reducer_window = undefined | pos_integer(), reducer_node = undefined | node(), reducer_opts = undefined | reducer_opts(), reducer_skip = undefined | boolean(), output_opts = undefined | output_opts(), heartbeat = undefined | pos_integer(), window_sync = undefined | boolean(), started_at = undefined | erlang:timestamp(), start_node = undefined | node()}
@@ -25,11 +24,9 @@ flow() = #flow{id = undefined | binary(), module = undefined | module(), module_
-
### mapper_opts() ###
-
mapper_opts() = term()
@@ -37,11 +34,9 @@ mapper_opts() = term()
-
### module_vsn() ###
-
module_vsn() = pos_integer()
@@ -49,11 +44,9 @@ module_vsn() = pos_integer()
-
### output_opts() ###
-
output_opts() = term()
@@ -61,11 +54,9 @@ output_opts() = term()
-
### reducer_opts() ###
-
reducer_opts() = term()
@@ -73,11 +64,9 @@ reducer_opts() = term()
-
### stream_name() ###
-
stream_name() = atom()
@@ -85,16 +74,13 @@ stream_name() = atom()
-
### stream_names() ###
-
stream_names() = [stream_name()]
-
## Function Index ##
@@ -113,83 +99,69 @@ stream_names() = [stream_name()]
`code_change(OldVsn, State, Extra) -> any()`
-
### handle_call/3 ###
`handle_call(Request, From, State) -> any()`
-
### handle_cast/2 ###
`handle_cast(Msg, State) -> any()`
-
### handle_info/2 ###
`handle_info(Msg, State) -> any()`
-
### init/1 ###
`init(Flow) -> any()`
-
### lookup/1 ###
-
lookup(FlowId::binary() | flow()) -> undefined | pid()
-
### register/1 ###
-
register(Flow::flow()) -> true
-
### start/1 ###
-
start(Flow::flow()) -> {ok, pid()} | {error, reducers_max}
-
### terminate/2 ###
`terminate(Reason, State) -> any()`
-
### unregister/1 ###
-
unregister(Flow::flow()) -> true
-
diff --git a/doc/swirl_stream.md b/doc/swirl_stream.md
index 0f2b3f9..df50061 100644
--- a/doc/swirl_stream.md
+++ b/doc/swirl_stream.md
@@ -5,8 +5,6 @@
* [Function Index](#index)
* [Function Details](#functions)
-
-
## Data Types ##
@@ -17,7 +15,6 @@
### event() ###
-
event() = [{atom(), value()}]
@@ -25,11 +22,9 @@ event() = [{atom(), value()}]
-
### flow() ###
-
flow() = #flow{id = undefined | binary(), module = undefined | module(), module_vsn = undefined | module_vsn(), stream_filter = undefined | string(), stream_names = undefined | stream_names(), mapper_window = undefined | pos_integer(), mapper_nodes = undefined | [node()], mapper_opts = undefined | mapper_opts(), reducer_window = undefined | pos_integer(), reducer_node = undefined | node(), reducer_opts = undefined | reducer_opts(), reducer_skip = undefined | boolean(), output_opts = undefined | output_opts(), heartbeat = undefined | pos_integer(), window_sync = undefined | boolean(), started_at = undefined | erlang:timestamp(), start_node = undefined | node()}
@@ -37,11 +32,9 @@ flow() = #flow{id = undefined | binary(), module = undefined | module(), module_
-
### mapper_opts() ###
-
mapper_opts() = term()
@@ -49,11 +42,9 @@ mapper_opts() = term()
-
### module_vsn() ###
-
module_vsn() = pos_integer()
@@ -61,11 +52,9 @@ module_vsn() = pos_integer()
-
### output_opts() ###
-
output_opts() = term()
@@ -73,11 +62,9 @@ output_opts() = term()
-
### reducer_opts() ###
-
reducer_opts() = term()
@@ -85,11 +72,9 @@ reducer_opts() = term()
-
### stream_name() ###
-
stream_name() = atom()
@@ -97,11 +82,9 @@ stream_name() = atom()
-
### stream_names() ###
-
stream_names() = [stream_name()]
@@ -109,16 +92,13 @@ stream_names() = [stream_name()]
-
### value() ###
-
value() = integer() | float() | binary()
-
## Function Index ##
@@ -135,43 +115,35 @@ value() = integer() | float() | binary()
### emit/2 ###
-
emit(StreamName::stream_name(), Event::event()) -> ok
-
### lookup/1 ###
-
lookup(StreamName::stream_name()) -> [tuple()]
-
### register/2 ###
-
register(Flow::flow(), TableId::ets:tab()) -> true
-
### unregister/1 ###
-
unregister(Flow::flow()) -> true
-
diff --git a/doc/swirl_sup.md b/doc/swirl_sup.md
index eb46b06..b412d0a 100644
--- a/doc/swirl_sup.md
+++ b/doc/swirl_sup.md
@@ -5,6 +5,7 @@
* [Function Details](#functions)
__Behaviours:__ [`supervisor`](supervisor.md).
+
## Function Index ##
@@ -23,15 +24,12 @@ __Behaviours:__ [`supervisor`](supervisor.md).
`init(X1) -> any()`
-
### start_link/0 ###
-
start_link() -> {ok, pid()}
-
diff --git a/doc/swirl_tracker.md b/doc/swirl_tracker.md
index 0b94734..fa21233 100644
--- a/doc/swirl_tracker.md
+++ b/doc/swirl_tracker.md
@@ -17,7 +17,6 @@ __Behaviours:__ [`gen_server`](gen_server.md).
### flow() ###
-
flow() = #flow{id = undefined | binary(), module = undefined | module(), module_vsn = undefined | module_vsn(), stream_filter = undefined | string(), stream_names = undefined | stream_names(), mapper_window = undefined | pos_integer(), mapper_nodes = undefined | [node()], mapper_opts = undefined | mapper_opts(), reducer_window = undefined | pos_integer(), reducer_node = undefined | node(), reducer_opts = undefined | reducer_opts(), reducer_skip = undefined | boolean(), output_opts = undefined | output_opts(), heartbeat = undefined | pos_integer(), window_sync = undefined | boolean(), started_at = undefined | erlang:timestamp(), start_node = undefined | node()}
@@ -25,11 +24,9 @@ flow() = #flow{id = undefined | binary(), module = undefined | module(), module_
-
### mapper_opts() ###
-
mapper_opts() = term()
@@ -37,11 +34,9 @@ mapper_opts() = term()
-
### module_vsn() ###
-
module_vsn() = pos_integer()
@@ -49,11 +44,9 @@ module_vsn() = pos_integer()
-
### output_opts() ###
-
output_opts() = term()
@@ -61,11 +54,9 @@ output_opts() = term()
-
### reducer_opts() ###
-
reducer_opts() = term()
@@ -73,11 +64,9 @@ reducer_opts() = term()
-
### stream_name() ###
-
stream_name() = atom()
@@ -85,16 +74,13 @@ stream_name() = atom()
-
### stream_names() ###
-
stream_names() = [stream_name()]
-
## Function Index ##
@@ -113,138 +99,114 @@ stream_names() = [stream_name()]
`code_change(OldVsn, State, Extra) -> any()`
-
### handle_call/3 ###
`handle_call(Request, From, State) -> any()`
-
### handle_cast/2 ###
`handle_cast(Msg, State) -> any()`
-
### handle_info/2 ###
`handle_info(Info, State) -> any()`
-
### init/1 ###
`init(X1) -> any()`
-
### lookup/2 ###
-
lookup(TableId::ets:tab(), Key::term()) -> term()
-
### message/3 ###
-
message(Node::node(), FlowId::binary(), Msg::term()) -> ok
-
### register/3 ###
-
register(TableId::ets:tab(), Key::term(), Value::term()) -> true
-
### start_link/0 ###
-
start_link() -> {ok, pid()}
-
### start_mappers/1 ###
-
start_mappers(Flow::flow()) -> ok
-
### start_reducer/1 ###
-
start_reducer(Flow::flow()) -> ok
-
### stop_mappers/1 ###
-
stop_mappers(Flow::flow()) -> ok
-
### stop_reducer/1 ###
-
stop_reducer(Flow::flow()) -> ok
-
### terminate/2 ###
`terminate(Reason, State) -> any()`
-
### unregister/2 ###
-
unregister(TableId::ets:tab(), Key::term()) -> true
-
diff --git a/doc/swirl_utils.md b/doc/swirl_utils.md
index 77c5546..5fd5e9e 100644
--- a/doc/swirl_utils.md
+++ b/doc/swirl_utils.md
@@ -4,7 +4,6 @@
* [Function Index](#index)
* [Function Details](#functions)
-
## Function Index ##
@@ -23,95 +22,81 @@
`lookup(Key, List) -> any()`
-
### lookup/3 ###
`lookup(Key, List, Default) -> any()`
-
### new_timer/2 ###
`new_timer(Time, Msg) -> any()`
-
### new_timer/3 ###
`new_timer(Time, Msg, X3) -> any()`
-
### proplist_to_record/2 ###
`proplist_to_record(Proplist, Record) -> any()`
-
### record_to_proplist/1 ###
`record_to_proplist(Flow) -> any()`
-
### safe_dict_fetch/2 ###
`safe_dict_fetch(Key, Dict) -> any()`
-
### safe_ets_delete/1 ###
`safe_ets_delete(TableId) -> any()`
-
### safe_ets_increment/3 ###
`safe_ets_increment(TableId, Key, Counters) -> any()`
-
### safe_ets_lookup_element/2 ###
`safe_ets_lookup_element(TableId, Key) -> any()`
-
### tab2list/1 ###
`tab2list(Tid) -> any()`
-
### unix_tstamp_ms/0 ###
`unix_tstamp_ms() -> any()`
-
### update_op/1 ###
`update_op(Counters) -> any()`
-
### uuid/0 ###
`uuid() -> any()`
-
diff --git a/rebar.config b/rebar.config
index aa7a581..258b5c9 100644
--- a/rebar.config
+++ b/rebar.config
@@ -1,17 +1,11 @@
-{clean_files, ["test/*.beam"]}.
+{cover_enabled, true}.
+{cover_print_enabled, true}.
{deps, [
- %% dependencies
- {uuid, "1.3.1",
- {git, "https://github.com/okeuday/uuid.git", {tag, "v1.3.1"}}},
-
- %% tests
- {etest, "1.0.0.beta",
- {git, "https://github.com/wooga/etest.git", {branch, "master"}}},
-
- %% docs
{edown, ".*",
- {git, "https://github.com/uwiger/edown.git", "HEAD"}}
+ {git, "https://github.com/uwiger/edown.git", "HEAD"}},
+ {uuid, ".*",
+ {git, "https://github.com/okeuday/uuid.git", {tag, "v1.5.0"}}}
]}.
{edoc_opts, [
@@ -21,8 +15,7 @@
{includes, ["include"]},
{preprocess, true},
{stylesheet, ""},
- {title, "swirl"},
- {top_level_readme, {"./README.md", "http://github.com/lpgauth/swirl"}}
+ {title, "swirl"}
]}.
{erl_opts, [
diff --git a/src/swirl.app.src b/src/swirl.app.src
index 244c7d1..227461d 100644
--- a/src/swirl.app.src
+++ b/src/swirl.app.src
@@ -1,6 +1,6 @@
{application, swirl, [
{description, "lightweight distributed stream processor"},
- {vsn, "0.2.3"},
+ {vsn, "0.2.4"},
{registered, [
swirl_ets_manager,
swirl_sup,
diff --git a/src/swirl_ql.erl b/src/swirl_ql.erl
index b79ced5..8aaf18c 100644
--- a/src/swirl_ql.erl
+++ b/src/swirl_ql.erl
@@ -66,98 +66,3 @@ compare('<>', A, B) when is_number(A) or
is_number(B) or
is_binary(B) ->
A /= B.
-
--ifdef(TEST).
--include_lib("eunit/include/eunit.hrl").
-
-%% tests
-benchmark_test() ->
- {ok, ExpTree} = parse("exchange_id = 1 AND exchange_seller_id = 181 AND bidder_id IN (1, 5) AND buyer_spend > 150"),
-
- Vars = [
- {exchange_id, 1},
- {exchange_seller_id, 181},
- {bidder_id, 1},
- {buyer_spend, 200}
- ],
-
- FunEvaluate = fun() -> evaluate(ExpTree, Vars) end,
- benchmark(evaluate, FunEvaluate, 100000).
-
-evaluate_test() ->
- % comp predictate
- assert_eval({comp, '=', bidder_id, 1}, [{bidder_id, 1}]),
- assert_not_eval({comp, '=', bidder_id, 1}, [{bidder_id, 2}]),
- assert_eval({comp, '<', price, 100}, [{price, 60}]),
- assert_not_eval({comp, '<', price, 100}, [{price, 160}]),
- assert_eval({comp, '<=', price, 100}, [{price, 100}]),
- assert_not_eval({comp, '<=', price, 100}, [{price, 160}]),
- assert_eval({comp, '>=', price, 100}, [{price, 100}]),
- assert_not_eval({comp, '>=', price, 160}, [{price, 100}]),
- assert_eval({comp, '>', price, 100}, [{price, 160}]),
- assert_not_eval({comp, '>', price, 100}, [{price, 60}]),
- assert_eval({comp, '<>', price, 100}, [{price, 160}]),
- assert_not_eval({comp, '<>', price, 100}, [{price, 100}]),
-
- % in predictate
- assert_eval({in, exchange_id, [1 , 2]}, [{exchange_id, 2}]),
- assert_not_eval({in, exchange_id, [1 , 2]}, [{exchange_id, 3}]),
- assert_eval({notin, exchange_id, [1 , 2]}, [{exchange_id, 3}]),
- assert_not_eval({notin, exchange_id, [1 , 2]}, [{exchange_id, 2}]),
- assert_eval({in_var, 54, segment_ids}, [{segment_ids, [12, 54]}]),
- assert_not_eval({in_var, 54, segment_ids}, [{segment_ids, [12]}]),
- assert_eval({notin_var, 54, segment_ids}, [{segment_ids, [12]}]),
- assert_not_eval({notin_var, 54, segment_ids}, [{segment_ids, [12, 54]}]),
-
- % null predictate
- assert_eval({null, exchange_id}, []),
- assert_not_eval({null, exchange_id}, [{exchange_id, 3}]),
- assert_eval({notnull, exchange_id}, [{exchange_id, 3}]),
- assert_not_eval({notnull, exchange_id}, [{exchange_id, ?NULL}]),
-
- % and
- assert_eval({'and', {comp, '=', bidder_id, 1}, {comp, '=', bidder_id, 1}},
- [{bidder_id, 1}]),
- assert_not_eval({'and', {comp, '=', bidder_id, 1}, {comp, '=', exchange_id, 1}},
- [{bidder_id, 1}, {exchange_id, 2}]),
-
- % or
- assert_eval({'or', {comp, '=', bidder_id, 2}, {comp, '=', bidder_id, 1}},
- [{bidder_id, 1}]),
- assert_not_eval({'or', {comp, '=', bidder_id, 2}, {comp, '=', bidder_id, 3}},
- [{bidder_id, 1}]).
-
-parse_test() ->
- assert_parse({comp, '=', bidder_id, 1}, "bidder_id = 1"),
- assert_parse({comp, '=', domain, <<"ebay.ca">>}, "domain = 'ebay.ca'"),
- assert_parse({comp, '=', domain, <<"ebay.ca">>}, "domain = \"ebay.ca\""),
- assert_parse({in, exchange_id, [1, 2, 3]}, "exchange_id IN (1, 2, 3)"),
- assert_parse({in_var, 4, segment_ids}, "4 IN segment_ids"),
- assert_parse({notin_var, 8, segment_ids}, "8 NOT IN segment_ids"),
- assert_parse({'and', {comp, '=', bidder_id, 1}, {'or', {notin, exchange_id, [1 , 2]},
- {comp, '=', domain, <<"ebay.ca">>}}}, "bidder_id = 1 AND (exchange_id NOT IN (1, 2) OR domain = 'ebay.ca')").
-
-%% test_utils
-assert_eval(ExpTree, Vars) ->
- ?assert(evaluate(ExpTree, Vars)).
-
-assert_not_eval(ExpTree, Vars) ->
- ?assertNot(evaluate(ExpTree, Vars)).
-
-assert_parse(Expected, Expression) ->
- {ok, ExpTree} = parse(Expression),
- ?assertEqual(Expected, ExpTree).
-
-benchmark(Name, Fun, N) ->
- Timestamp = os:timestamp(),
- ok = loop(Fun, N),
- Time = timer:now_diff(os:timestamp(), Timestamp) / N,
- ?debugFmt("~p: ~p microseconds", [Name, Time]).
-
-loop(_, 0) ->
- ok;
-loop(Fun, N) ->
- Fun(),
- loop(Fun, N - 1).
-
--endif.
diff --git a/src/swirl_utils.erl b/src/swirl_utils.erl
index 821f313..7c13af5 100644
--- a/src/swirl_utils.erl
+++ b/src/swirl_utils.erl
@@ -101,7 +101,8 @@ update_op(Counters) when is_list(Counters) ->
update_op(Counters, 2).
uuid() ->
- uuid:get_v1(uuid:new(self(), os)).
+ {Uuid, _UuidState} = uuid:get_v1(uuid:new(self(), os)),
+ Uuid.
%% private
match_all('$end_of_table') ->
diff --git a/test/swirl_flow_test.erl b/test/swirl_flow_tests.erl
similarity index 71%
rename from test/swirl_flow_test.erl
rename to test/swirl_flow_tests.erl
index b51c1d0..6023da1 100644
--- a/test/swirl_flow_test.erl
+++ b/test/swirl_flow_tests.erl
@@ -1,23 +1,19 @@
--module(swirl_flow_test).
--include_lib("etest/include/etest.hrl").
-
--export([
- after_suite/0,
- before_suite/0,
- test_benchmark_emit/0,
- test_swirl_flow/0
-]).
-
--define(N, 10000).
-
-%% public
-after_suite() ->
- ok = application:stop(swirl).
-
-before_suite() ->
- random:seed(erlang:now()),
- application:ensure_all_started(swirl).
-
+-module(swirl_flow_tests).
+-include("test.hrl").
+
+-compile(export_all).
+
+%% runners
+swirl_test_() ->
+ {setup,
+ fun () -> setup() end,
+ fun (_) -> cleanup() end,
+ {inparallel, [
+ ?T(test_benchmark_emit),
+ ?T(test_swirl_flow)
+ ]}}.
+
+%% tests
test_benchmark_emit() ->
Flows = [new_flow() || _ <- lists:seq(1, 100)],
timer:sleep(timer:seconds(1)),
@@ -25,7 +21,7 @@ test_benchmark_emit() ->
Timestamp = os:timestamp(),
emit_loop(?N),
Delta = timer:now_diff(os:timestamp(), Timestamp),
- io:format("~p microseconds~n", [Delta / ?N]),
+ ?debugFmt("~p microseconds~n", [Delta / ?N]),
[swirl_flow:stop(Flow) || Flow <- Flows].
@@ -52,15 +48,20 @@ test_swirl_flow() ->
Rows = receive_loop(),
Expected = [
- {{start,requests,3,50},{1,10}},
{{start,delivery,3,1},{1,10}},
- {{start,delivery,3,10},{1,10}}
+ {{start,delivery,3,10},{1,10}},
+ {{start,requests,3,50},{1,10}}
],
- ?assert_equal(Expected, Rows),
+ ?assertEqual(Expected, lists:usort(Rows)),
swirl_flow:stop(Flow).
-%% private
+%% utils
+cleanup() ->
+ error_logger:tty(false),
+ application:stop(swirl),
+ error_logger:tty(true).
+
emit_loop(0) ->
ok;
emit_loop(N) ->
@@ -98,8 +99,15 @@ random_type() ->
receive_loop() ->
receive
- [] ->
- receive_loop();
- Aggregates ->
- Aggregates
+ [] -> receive_loop();
+ Aggregates -> Aggregates
end.
+
+setup() ->
+ error_logger:tty(false),
+ application:stop(swirl),
+ swirl:start(),
+ error_logger:tty(true).
+
+test(Test) ->
+ {atom_to_list(Test), ?MODULE, Test}.
diff --git a/test/swirl_ql_tests.erl b/test/swirl_ql_tests.erl
new file mode 100644
index 0000000..3a7529a
--- /dev/null
+++ b/test/swirl_ql_tests.erl
@@ -0,0 +1,93 @@
+-module(swirl_ql_tests).
+-include("test.hrl").
+
+-compile(export_all).
+
+benchmark_test() ->
+ {ok, ExpTree} = swirl_ql:parse("exchange_id = 1 AND exchange_seller_id = 181 AND bidder_id IN (1, 5) AND buyer_spend > 150"),
+
+ Vars = [
+ {exchange_id, 1},
+ {exchange_seller_id, 181},
+ {bidder_id, 1},
+ {buyer_spend, 200}
+ ],
+
+ FunEvaluate = fun() -> swirl_ql:evaluate(ExpTree, Vars) end,
+ benchmark(evaluate, FunEvaluate, 100000).
+
+evaluate_test() ->
+ % comp predictate
+ assert_eval({comp, '=', bidder_id, 1}, [{bidder_id, 1}]),
+ assert_not_eval({comp, '=', bidder_id, 1}, [{bidder_id, 2}]),
+ assert_eval({comp, '<', price, 100}, [{price, 60}]),
+ assert_not_eval({comp, '<', price, 100}, [{price, 160}]),
+ assert_eval({comp, '<=', price, 100}, [{price, 100}]),
+ assert_not_eval({comp, '<=', price, 100}, [{price, 160}]),
+ assert_eval({comp, '>=', price, 100}, [{price, 100}]),
+ assert_not_eval({comp, '>=', price, 160}, [{price, 100}]),
+ assert_eval({comp, '>', price, 100}, [{price, 160}]),
+ assert_not_eval({comp, '>', price, 100}, [{price, 60}]),
+ assert_eval({comp, '<>', price, 100}, [{price, 160}]),
+ assert_not_eval({comp, '<>', price, 100}, [{price, 100}]),
+
+ % in predictate
+ assert_eval({in, exchange_id, [1 , 2]}, [{exchange_id, 2}]),
+ assert_not_eval({in, exchange_id, [1 , 2]}, [{exchange_id, 3}]),
+ assert_eval({notin, exchange_id, [1 , 2]}, [{exchange_id, 3}]),
+ assert_not_eval({notin, exchange_id, [1 , 2]}, [{exchange_id, 2}]),
+ assert_eval({in_var, 54, segment_ids}, [{segment_ids, [12, 54]}]),
+ assert_not_eval({in_var, 54, segment_ids}, [{segment_ids, [12]}]),
+ assert_eval({notin_var, 54, segment_ids}, [{segment_ids, [12]}]),
+ assert_not_eval({notin_var, 54, segment_ids}, [{segment_ids, [12, 54]}]),
+
+ % null predictate
+ assert_eval({null, exchange_id}, []),
+ assert_not_eval({null, exchange_id}, [{exchange_id, 3}]),
+ assert_eval({notnull, exchange_id}, [{exchange_id, 3}]),
+ assert_not_eval({notnull, exchange_id}, [{exchange_id, ?NULL}]),
+
+ % and
+ assert_eval({'and', {comp, '=', bidder_id, 1}, {comp, '=', bidder_id, 1}},
+ [{bidder_id, 1}]),
+ assert_not_eval({'and', {comp, '=', bidder_id, 1}, {comp, '=', exchange_id, 1}},
+ [{bidder_id, 1}, {exchange_id, 2}]),
+
+ % or
+ assert_eval({'or', {comp, '=', bidder_id, 2}, {comp, '=', bidder_id, 1}},
+ [{bidder_id, 1}]),
+ assert_not_eval({'or', {comp, '=', bidder_id, 2}, {comp, '=', bidder_id, 3}},
+ [{bidder_id, 1}]).
+
+parse_test() ->
+ assert_parse({comp, '=', bidder_id, 1}, "bidder_id = 1"),
+ assert_parse({comp, '=', domain, <<"ebay.ca">>}, "domain = 'ebay.ca'"),
+ assert_parse({comp, '=', domain, <<"ebay.ca">>}, "domain = \"ebay.ca\""),
+ assert_parse({in, exchange_id, [1, 2, 3]}, "exchange_id IN (1, 2, 3)"),
+ assert_parse({in_var, 4, segment_ids}, "4 IN segment_ids"),
+ assert_parse({notin_var, 8, segment_ids}, "8 NOT IN segment_ids"),
+ assert_parse({'and', {comp, '=', bidder_id, 1}, {'or', {notin, exchange_id, [1 , 2]},
+ {comp, '=', domain, <<"ebay.ca">>}}}, "bidder_id = 1 AND (exchange_id NOT IN (1, 2) OR domain = 'ebay.ca')").
+
+%% test_utils
+assert_eval(ExpTree, Vars) ->
+ ?assert(swirl_ql:evaluate(ExpTree, Vars)).
+
+assert_not_eval(ExpTree, Vars) ->
+ ?assertNot(swirl_ql:evaluate(ExpTree, Vars)).
+
+assert_parse(Expected, Expression) ->
+ {ok, ExpTree} = swirl_ql:parse(Expression),
+ ?assertEqual(Expected, ExpTree).
+
+benchmark(Name, Fun, N) ->
+ Timestamp = os:timestamp(),
+ ok = loop(Fun, N),
+ Time = timer:now_diff(os:timestamp(), Timestamp) / N,
+ ?debugFmt("~p: ~p microseconds", [Name, Time]).
+
+loop(_, 0) ->
+ ok;
+loop(Fun, N) ->
+ Fun(),
+ loop(Fun, N - 1).
diff --git a/test/test.hrl b/test/test.hrl
new file mode 100644
index 0000000..e759a0e
--- /dev/null
+++ b/test/test.hrl
@@ -0,0 +1,5 @@
+-include_lib("eunit/include/eunit.hrl").
+-include_lib("swirl/include/swirl.hrl").
+
+-define(N, 1000).
+-define(T, fun (Test) -> test(Test) end).