You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

981 lines
41 KiB

пре 13 година
пре 9 година
пре 13 година
пре 13 година
пре 13 година
пре 11 година
пре 13 година
пре 11 година
пре 13 година
пре 9 година
пре 13 година
пре 13 година
пре 13 година
пре 13 година
пре 13 година
пре 13 година
пре 9 година
пре 13 година
пре 13 година
пре 13 година
пре 13 година
пре 13 година
пре 13 година
пре 13 година
пре 11 година
пре 11 година
пре 13 година
пре 11 година
пре 11 година
пре 11 година
пре 13 година
пре 11 година
пре 13 година
пре 13 година
пре 13 година
пре 11 година
пре 13 година
пре 11 година
пре 13 година
пре 11 година
пре 13 година
пре 13 година
пре 13 година
пре 13 година
пре 13 година
пре 11 година
пре 11 година
пре 11 година
пре 11 година
пре 13 година
пре 13 година
пре 13 година
пре 13 година
пре 13 година
пре 9 година
пре 9 година
пре 11 година
пре 11 година
пре 11 година
пре 11 година
пре 11 година
пре 11 година
пре 13 година
пре 13 година
  1. %% Copyright (c) 2012, Magnus Klaar <klaar@ninenines.eu>
  2. %%
  3. %% Permission to use, copy, modify, and/or distribute this software for any
  4. %% purpose with or without fee is hereby granted, provided that the above
  5. %% copyright notice and this permission notice appear in all copies.
  6. %%
  7. %% THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
  8. %% WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
  9. %% MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
  10. %% ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
  11. %% WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
  12. %% ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  13. %% OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  14. %% @doc Event filter implementation.
  15. %%
  16. %% An event query is constructed using the built in operators exported from
  17. %% this module. The filtering operators are used to specify which events
  18. %% should be included in the output of the query. The default output action
  19. %% is to copy all events matching the input filters associated with a query
  20. %% to the output. This makes it possible to construct and compose multiple
  21. %% queries at runtime.
  22. %%
  23. %% === Examples of built in filters ===
  24. %% ```
  25. %% %% Select all events where 'a' exists and is greater than 0.
  26. %% glc:gt(a, 0).
  27. %% %% Select all events where 'a' exists and is equal to 0.
  28. %% glc:eq(a, 0).
  29. %% %% Select all events where 'a' exists and is not equal to 0.
  30. %% glc:neq(a, 0).
  31. %% %% Select all events where 'a' exists and is less than 0.
  32. %% glc:lt(a, 0).
  33. %% %% Select all events where 'a' exists and is anything.
  34. %% glc:wc(a).
  35. %%
  36. %% %% Select no input events. Used as black hole query.
  37. %% glc:null(false).
  38. %% %% Select all input events. Used as passthrough query.
  39. %% glc:null(true).
  40. %% '''
  41. %%
  42. %% === Examples of combining filters ===
  43. %% ```
  44. %% %% Select all events where both 'a' and 'b' exists and are greater than 0.
  45. %% glc:all([glc:gt(a, 0), glc:gt(b, 0)]).
  46. %% %% Select all events where 'a' or 'b' exists and are greater than 0.
  47. %% glc:any([glc:gt(a, 0), glc:gt(b, 0)]).
  48. %% '''
  49. %%
  50. %% === Handling output events ===
  51. %%
  52. %% Once a query has been composed it is possible to override the output action
  53. %% with an erlang function. The function will be applied to each output event
  54. %% from the query. The return value from the function will be ignored.
  55. %%
  56. %% ```
  57. %% %% Write all input events as info reports to the error logger.
  58. %% glc:with(glc:null(true), fun(E) ->
  59. %% error_logger:info_report(gre:pairs(E)) end).
  60. %% '''
  61. %%
  62. -module(glc).
  63. -export([
  64. compile/2,
  65. compile/3,
  66. compile/4,
  67. handle/2,
  68. get/2,
  69. delete/1,
  70. reset_counters/1,
  71. reset_counters/2
  72. ]).
  73. -export([
  74. lt/2, lte/2,
  75. eq/2, neq/2,
  76. gt/2, gte/2,
  77. wc/1,
  78. nf/1
  79. ]).
  80. -export([
  81. all/1,
  82. any/1,
  83. null/1,
  84. with/2,
  85. run/3
  86. ]).
  87. -export([
  88. input/1,
  89. output/1,
  90. job_input/1,
  91. job_run/1,
  92. job_error/1,
  93. job_time/1,
  94. filter/1,
  95. union/1
  96. ]).
  97. -record(module, {
  98. 'query' :: term(),
  99. tables :: [{atom(), atom()}],
  100. qtree :: term(),
  101. store :: term()
  102. }).
  103. -spec lt(atom(), term()) -> glc_ops:op().
  104. lt(Key, Term) ->
  105. glc_ops:lt(Key, Term).
  106. -spec lte(atom(), term()) -> glc_ops:op().
  107. lte(Key, Term) ->
  108. glc_ops:lte(Key, Term).
  109. -spec eq(atom(), term()) -> glc_ops:op().
  110. eq(Key, Term) ->
  111. glc_ops:eq(Key, Term).
  112. -spec neq(atom(), term()) -> glc_ops:op().
  113. neq(Key, Term) ->
  114. glc_ops:neq(Key, Term).
  115. -spec gt(atom(), term()) -> glc_ops:op().
  116. gt(Key, Term) ->
  117. glc_ops:gt(Key, Term).
  118. -spec gte(atom(), term()) -> glc_ops:op().
  119. gte(Key, Term) ->
  120. glc_ops:gte(Key, Term).
  121. -spec wc(atom()) -> glc_ops:op().
  122. wc(Key) ->
  123. glc_ops:wc(Key).
  124. -spec nf(atom()) -> glc_ops:op().
  125. nf(Key) ->
  126. glc_ops:nf(Key).
  127. %% @doc Filter the input using multiple filters.
  128. %%
  129. %% For an input to be considered valid output the all filters specified
  130. %% in the list must hold for the input event. The list is expected to
  131. %% be a non-empty list. If the list of filters is an empty list a `badarg'
  132. %% error will be thrown.
  133. -spec all([glc_ops:op()]) -> glc_ops:op().
  134. all(Filters) ->
  135. glc_ops:all(Filters).
  136. %% @doc Filter the input using one of multiple filters.
  137. %%
  138. %% For an input to be considered valid output on of the filters specified
  139. %% in the list must hold for the input event. The list is expected to be
  140. %% a non-empty list. If the list of filters is an empty list a `badarg'
  141. %% error will be thrown.
  142. -spec any([glc_ops:op()]) -> glc_ops:op().
  143. any(Filters) ->
  144. glc_ops:any(Filters).
  145. %% @doc Always return `true' or `false'.
  146. -spec null(boolean()) -> glc_ops:op().
  147. null(Result) ->
  148. glc_ops:null(Result).
  149. %% @doc Apply a function to each output of a query.
  150. %%
  151. %% Updating the output action of a query finalizes it. Attempting
  152. %% to use a finalized query to construct a new query will result
  153. %% in a `badarg' error.
  154. -spec with(glc_ops:op(), fun((gre:event()) -> term())) -> glc_ops:op().
  155. with(Query, Action) ->
  156. glc_ops:with(Query, Action).
  157. %% @doc Return a union of multiple queries.
  158. %%
  159. %% The union of multiple queries is the equivalent of executing multiple
  160. %% queries separately on the same input event. The advantage is that filter
  161. %% conditions that are common to all or some of the queries only need to
  162. %% be tested once.
  163. %%
  164. %% All queries are expected to be valid and have an output action other
  165. %% than the default which is `output'. If these expectations don't hold
  166. %% a `badarg' error will be thrown.
  167. -spec union([glc_ops:op()]) -> glc_ops:op().
  168. union(Queries) ->
  169. glc_ops:union(Queries).
  170. %% @doc Compile a query to a module.
  171. %%
  172. %% On success the module representing the query is returned. The module and
  173. %% data associated with the query must be released using the {@link delete/1}
  174. %% function. The name of the query module is expected to be unique.
  175. %% The counters are reset by default, unless Reset is set to false
  176. -spec compile(atom(), glc_ops:op() | [glc_ops:op()]) -> {ok, atom()}.
  177. compile(Module, Query) ->
  178. compile(Module, Query, undefined, true).
  179. -spec compile(atom(), glc_ops:op() | [glc_ops:op()], boolean()) -> {ok, atom()}.
  180. compile(Module, Query, Reset) when is_boolean(Reset) ->
  181. compile(Module, Query, undefined, Reset);
  182. compile(Module, Query, undefined) ->
  183. compile(Module, Query, undefined, true);
  184. compile(Module, Query, Store) when is_list(Store) ->
  185. compile(Module, Query, Store, true).
  186. compile(Module, Query, Store, Reset) ->
  187. {ok, ModuleData} = module_data(Module, Query, Store),
  188. case glc_code:compile(Module, ModuleData) of
  189. {ok, Module} when Reset ->
  190. reset_counters(Module),
  191. {ok, Module};
  192. {ok, Module} ->
  193. {ok, Module}
  194. end.
  195. %% @doc Handle an event using a compiled query.
  196. %%
  197. %% The input event is expected to have been returned from {@link gre:make/2}.
  198. -spec handle(atom(), list({atom(), term()}) | gre:event()) -> ok.
  199. handle(Module, Event) when is_list(Event) ->
  200. Module:handle(gre:make(Event, [list]));
  201. handle(Module, Event) ->
  202. Module:handle(Event).
  203. get(Module, Key) ->
  204. Module:get(Key).
  205. run(Module, Fun, Event) when is_list(Event) ->
  206. Module:runjob(Fun, gre:make(Event, [list]));
  207. run(Module, Fun, Event) ->
  208. Module:runjob(Fun, Event).
  209. %% @doc The number of input events for this query module.
  210. -spec input(atom()) -> non_neg_integer().
  211. input(Module) ->
  212. Module:info(input).
  213. %% @doc The number of output events for this query module.
  214. -spec output(atom()) -> non_neg_integer().
  215. output(Module) ->
  216. Module:info(output).
  217. %% @doc The number of filtered events for this query module.
  218. -spec filter(atom()) -> non_neg_integer().
  219. filter(Module) ->
  220. Module:info(filter).
  221. %% @doc The number of job runs for this query module.
  222. -spec job_run(atom()) -> non_neg_integer().
  223. job_run(Module) ->
  224. Module:info(job_run).
  225. %% @doc The number of job errors for this query module.
  226. -spec job_error(atom()) -> non_neg_integer().
  227. job_error(Module) ->
  228. Module:info(job_error).
  229. %% @doc The number of job inputs for this query module.
  230. -spec job_input(atom()) -> non_neg_integer().
  231. job_input(Module) ->
  232. Module:info(job_input).
  233. %% @doc The amount of time jobs took for this query module.
  234. -spec job_time(atom()) -> non_neg_integer().
  235. job_time(Module) ->
  236. Module:info(job_time).
  237. %% @doc Release a compiled query.
  238. %%
  239. %% This releases all resources allocated by a compiled query. The query name
  240. %% is expected to be associated with an existing query module. Calling this
  241. %% function will shutdown all relevant processes and purge/delete the module.
  242. -spec delete(atom()) -> ok.
  243. delete(Module) ->
  244. Params = params_name(Module),
  245. Counts = counts_name(Module),
  246. ManageParams = manage_params_name(Module),
  247. ManageCounts = manage_counts_name(Module),
  248. _ = [ begin
  249. ok = supervisor:terminate_child(Sup, Name),
  250. ok = supervisor:delete_child(Sup, Name)
  251. end || {Sup, Name} <-
  252. [{gr_manager_sup, ManageParams}, {gr_manager_sup, ManageCounts},
  253. {gr_param_sup, Params}, {gr_counter_sup, Counts}]
  254. ],
  255. code:soft_purge(Module),
  256. code:delete(Module),
  257. ok.
  258. %% @doc Reset all counters
  259. %%
  260. %% This resets all the counters associated with a module
  261. -spec reset_counters(atom()) -> ok.
  262. reset_counters(Module) ->
  263. Module:reset_counters(all).
  264. %% @doc Reset a specific counter
  265. %%
  266. %% This resets a specific counter associated with a module
  267. -spec reset_counters(atom(), atom()) -> ok.
  268. reset_counters(Module, Counter) ->
  269. Module:reset_counters(Counter).
  270. %% @private Map a query to a module data term.
  271. -spec module_data(atom(), term(), term()) -> {ok, #module{}}.
  272. module_data(Module, Query, Store) ->
  273. %% terms in the query which are not valid arguments to the
  274. %% erl_syntax:abstract/1 functions are stored in ETS.
  275. %% the terms are only looked up once they are necessary to
  276. %% continue evaluation of the query.
  277. %% query counters are stored in a shared ETS table. this should
  278. %% be an optional feature. enabled by defaults to simplify tests.
  279. %% the abstract_tables/1 function expects a list of name-atom pairs.
  280. %% tables are referred to by name in the generated code. the table/1
  281. %% function maps names to registered processes response for those tables.
  282. Tables = module_tables(Module),
  283. Query2 = glc_lib:reduce(Query),
  284. {ok, #module{'query'=Query, tables=Tables, qtree=Query2, store=Store}}.
  285. %% @private Create a data managed supervised process for params, counter tables
  286. module_tables(Module) ->
  287. Params = params_name(Module),
  288. Counts = counts_name(Module),
  289. ManageParams = manage_params_name(Module),
  290. ManageCounts = manage_counts_name(Module),
  291. Counters = [{input,0}, {filter,0}, {output,0},
  292. {job_input, 0}, {job_run,0}, {job_time, 0},
  293. {job_error, 0}],
  294. _ = supervisor:start_child(gr_param_sup,
  295. {Params, {gr_param, start_link, [Params]},
  296. transient, brutal_kill, worker, [Params]}),
  297. _ = supervisor:start_child(gr_counter_sup,
  298. {Counts, {gr_counter, start_link, [Counts]},
  299. transient, brutal_kill, worker, [Counts]}),
  300. _ = supervisor:start_child(gr_manager_sup,
  301. {ManageParams, {gr_manager, start_link, [ManageParams, Params, []]},
  302. transient, brutal_kill, worker, [ManageParams]}),
  303. _ = supervisor:start_child(gr_manager_sup, {ManageCounts,
  304. {gr_manager, start_link, [ManageCounts, Counts, Counters]},
  305. transient, brutal_kill, worker, [ManageCounts]}),
  306. [{params,Params}, {counters, Counts}].
  307. reg_name(Module, Name) ->
  308. list_to_atom("gr_" ++ atom_to_list(Module) ++ Name).
  309. params_name(Module) -> reg_name(Module, "_params").
  310. counts_name(Module) -> reg_name(Module, "_counters").
  311. manage_params_name(Module) -> reg_name(Module, "_params_mgr").
  312. manage_counts_name(Module) -> reg_name(Module, "_counters_mgr").
  313. %% @todo Move comment.
  314. %% @private Map a query to a simplified query tree term.
  315. %%
  316. %% The simplified query tree is used to combine multiple queries into one
  317. %% query module. The goal of this is to reduce the filtering and dispatch
  318. %% overhead when multiple concurrent queries are executed.
  319. %%
  320. %% A fixed selection condition may be used to specify a property that an event
  321. %% must have in order to be considered part of the input stream for a query.
  322. %%
  323. %% For the sake of simplicity it is only possible to define selection
  324. %% conditions using the fields present in the context and identifiers
  325. %% of an event. The fields in the context are bound to the reserved
  326. %% names:
  327. %%
  328. %% - '$n': node name
  329. %% - '$a': application name
  330. %% - '$p': process identifier
  331. %% - '$t': timestamp
  332. %%
  333. %%
  334. %% If an event must be selected based on the runtime state of an event handler
  335. %% this must be done in the body of the handler.
  336. -ifdef(TEST).
  337. -include_lib("eunit/include/eunit.hrl").
  338. setup_query(Module, Query) ->
  339. setup_query(Module, Query, undefined).
  340. setup_query(Module, Query, Store) ->
  341. ?assertNot(erlang:module_loaded(Module)),
  342. ?assertEqual({ok, Module}, case (catch compile(Module, Query, Store)) of
  343. {'EXIT',_}=Error -> ?debugFmt("~p", [Error]), Error; Else -> Else end),
  344. ?assert(erlang:function_exported(Module, table, 1)),
  345. ?assert(erlang:function_exported(Module, handle, 1)),
  346. {compiled, Module}.
  347. events_test_() ->
  348. {foreach,
  349. fun() ->
  350. error_logger:tty(false),
  351. application:start(syntax_tools),
  352. application:start(compiler),
  353. application:start(goldrush)
  354. end,
  355. fun(_) ->
  356. application:stop(goldrush),
  357. application:stop(compiler),
  358. application:stop(syntax_tools),
  359. error_logger:tty(true)
  360. end,
  361. [
  362. {"null query compiles",
  363. fun() ->
  364. {compiled, Mod} = setup_query(testmod1, glc:null(false)),
  365. ?assertError(badarg, Mod:table(noexists))
  366. end
  367. },
  368. {"params table exists",
  369. fun() ->
  370. {compiled, Mod} = setup_query(testmod2, glc:null(false)),
  371. ?assert(is_atom(Mod:table(params))),
  372. ?assertMatch([_|_], gr_param:info(Mod:table(params)))
  373. end
  374. },
  375. {"null query exists",
  376. fun() ->
  377. {compiled, Mod} = setup_query(testmod3, glc:null(false)),
  378. ?assert(erlang:function_exported(Mod, info, 1)),
  379. ?assertError(badarg, Mod:info(invalid)),
  380. ?assertEqual({null, false}, Mod:info('query'))
  381. end
  382. },
  383. {"init counters test",
  384. fun() ->
  385. {compiled, Mod} = setup_query(testmod4, glc:null(false)),
  386. ?assertEqual(0, Mod:info(input)),
  387. ?assertEqual(0, Mod:info(filter)),
  388. ?assertEqual(0, Mod:info(output))
  389. end
  390. },
  391. {"filtered events test",
  392. fun() ->
  393. %% If no selection condition is specified no inputs can match.
  394. {compiled, Mod} = setup_query(testmod5, glc:null(false)),
  395. glc:handle(Mod, gre:make([], [list])),
  396. ?assertEqual(1, Mod:info(input)),
  397. ?assertEqual(1, Mod:info(filter)),
  398. ?assertEqual(0, Mod:info(output))
  399. end
  400. },
  401. {"nomatch event test",
  402. fun() ->
  403. %% If a selection condition but no body is specified the event
  404. %% is expected to count as filtered out if the condition does
  405. %% not hold.
  406. {compiled, Mod} = setup_query(testmod6, glc:eq('$n', 'noexists@nohost')),
  407. glc:handle(Mod, gre:make([{'$n', 'noexists2@nohost'}], [list])),
  408. ?assertEqual(1, Mod:info(input)),
  409. ?assertEqual(1, Mod:info(filter)),
  410. ?assertEqual(0, Mod:info(output))
  411. end
  412. },
  413. {"opfilter equal test",
  414. fun() ->
  415. %% If a selection condition but no body is specified the event
  416. %% counts as input to the query, but not as filtered out.
  417. {compiled, Mod} = setup_query(testmod7a, glc:eq('$n', 'noexists@nohost')),
  418. glc:handle(Mod, gre:make([{'$n', 'noexists@nohost'}], [list])),
  419. ?assertEqual(1, Mod:info(input)),
  420. ?assertEqual(0, Mod:info(filter)),
  421. ?assertEqual(1, Mod:info(output))
  422. end
  423. },
  424. {"opfilter not equal test",
  425. fun() ->
  426. {compiled, Mod} = setup_query(testmod7b, glc:neq('$n', 'noexists@nohost')),
  427. glc:handle(Mod, gre:make([{'$n', 'noexists@nohost'}], [list])),
  428. glc:handle(Mod, gre:make([{'$n', 'notexists@nohost'}], [list])),
  429. ?assertEqual(2, Mod:info(input)),
  430. ?assertEqual(1, Mod:info(filter)),
  431. ?assertEqual(1, Mod:info(output))
  432. end
  433. },
  434. {"opfilter wildcard test",
  435. fun() ->
  436. {compiled, Mod} = setup_query(testmod8, glc:wc(a)),
  437. glc:handle(Mod, gre:make([{b, 2}], [list])),
  438. ?assertEqual(1, Mod:info(input)),
  439. ?assertEqual(1, Mod:info(filter)),
  440. ?assertEqual(0, Mod:info(output)),
  441. glc:handle(Mod, gre:make([{a, 2}], [list])),
  442. ?assertEqual(2, Mod:info(input)),
  443. ?assertEqual(1, Mod:info(filter)),
  444. ?assertEqual(1, Mod:info(output))
  445. end
  446. },
  447. {"opfilter notfound test",
  448. fun() ->
  449. {compiled, Mod} = setup_query(testmod9, glc:nf(a)),
  450. glc:handle(Mod, gre:make([{a, 2}], [list])),
  451. ?assertEqual(1, Mod:info(input)),
  452. ?assertEqual(1, Mod:info(filter)),
  453. ?assertEqual(0, Mod:info(output)),
  454. glc:handle(Mod, gre:make([{b, 2}], [list])),
  455. ?assertEqual(2, Mod:info(input)),
  456. ?assertEqual(1, Mod:info(filter)),
  457. ?assertEqual(1, Mod:info(output))
  458. end
  459. },
  460. {"opfilter greater than test",
  461. fun() ->
  462. {compiled, Mod} = setup_query(testmod10a, glc:gt(a, 1)),
  463. glc:handle(Mod, gre:make([{'a', 2}], [list])),
  464. ?assertEqual(1, Mod:info(input)),
  465. ?assertEqual(0, Mod:info(filter)),
  466. glc:handle(Mod, gre:make([{'a', 0}], [list])),
  467. ?assertEqual(2, Mod:info(input)),
  468. ?assertEqual(1, Mod:info(filter)),
  469. ?assertEqual(1, Mod:info(output))
  470. end
  471. },
  472. {"opfilter greater than or equal to test",
  473. fun() ->
  474. {compiled, Mod} = setup_query(testmod10b, glc:gte(a, 1)),
  475. glc:handle(Mod, gre:make([{'a', 2}], [list])),
  476. ?assertEqual(1, Mod:info(input)),
  477. ?assertEqual(0, Mod:info(filter)),
  478. glc:handle(Mod, gre:make([{'a', 1}], [list])),
  479. ?assertEqual(2, Mod:info(input)),
  480. ?assertEqual(0, Mod:info(filter)),
  481. glc:handle(Mod, gre:make([{'a', 0}], [list])),
  482. ?assertEqual(3, Mod:info(input)),
  483. ?assertEqual(1, Mod:info(filter)),
  484. ?assertEqual(2, Mod:info(output))
  485. end
  486. },
  487. {"opfilter less than test",
  488. fun() ->
  489. {compiled, Mod} = setup_query(testmod11a, glc:lt(a, 1)),
  490. glc:handle(Mod, gre:make([{'a', 0}], [list])),
  491. ?assertEqual(1, Mod:info(input)),
  492. ?assertEqual(0, Mod:info(filter)),
  493. ?assertEqual(1, Mod:info(output)),
  494. glc:handle(Mod, gre:make([{'a', 2}], [list])),
  495. ?assertEqual(2, Mod:info(input)),
  496. ?assertEqual(1, Mod:info(filter)),
  497. ?assertEqual(1, Mod:info(output))
  498. end
  499. },
  500. {"opfilter less than or equal to test",
  501. fun() ->
  502. {compiled, Mod} = setup_query(testmod11b, glc:lte(a, 1)),
  503. glc:handle(Mod, gre:make([{'a', 0}], [list])),
  504. ?assertEqual(1, Mod:info(input)),
  505. ?assertEqual(0, Mod:info(filter)),
  506. ?assertEqual(1, Mod:info(output)),
  507. glc:handle(Mod, gre:make([{'a', 1}], [list])),
  508. ?assertEqual(2, Mod:info(input)),
  509. ?assertEqual(0, Mod:info(filter)),
  510. ?assertEqual(2, Mod:info(output)),
  511. glc:handle(Mod, gre:make([{'a', 2}], [list])),
  512. ?assertEqual(3, Mod:info(input)),
  513. ?assertEqual(1, Mod:info(filter)),
  514. ?assertEqual(2, Mod:info(output))
  515. end
  516. },
  517. {"allholds op test",
  518. fun() ->
  519. {compiled, Mod} = setup_query(testmod12,
  520. glc:all([glc:eq(a, 1), glc:eq(b, 2)])),
  521. glc:handle(Mod, gre:make([{'a', 1}], [list])),
  522. glc:handle(Mod, gre:make([{'a', 2}], [list])),
  523. ?assertEqual(2, Mod:info(input)),
  524. ?assertEqual(2, Mod:info(filter)),
  525. glc:handle(Mod, gre:make([{'b', 1}], [list])),
  526. glc:handle(Mod, gre:make([{'b', 2}], [list])),
  527. ?assertEqual(4, Mod:info(input)),
  528. ?assertEqual(4, Mod:info(filter)),
  529. glc:handle(Mod, gre:make([{'a', 1},{'b', 2}], [list])),
  530. ?assertEqual(5, Mod:info(input)),
  531. ?assertEqual(4, Mod:info(filter)),
  532. ?assertEqual(1, Mod:info(output))
  533. end
  534. },
  535. {"anyholds op test",
  536. fun() ->
  537. {compiled, Mod} = setup_query(testmod13,
  538. glc:any([glc:eq(a, 1), glc:eq(b, 2)])),
  539. glc:handle(Mod, gre:make([{'a', 2}], [list])),
  540. glc:handle(Mod, gre:make([{'b', 1}], [list])),
  541. ?assertEqual(2, Mod:info(input)),
  542. ?assertEqual(2, Mod:info(filter)),
  543. glc:handle(Mod, gre:make([{'a', 1}], [list])),
  544. glc:handle(Mod, gre:make([{'b', 2}], [list])),
  545. ?assertEqual(4, Mod:info(input)),
  546. ?assertEqual(2, Mod:info(filter))
  547. end
  548. },
  549. {"with function test",
  550. fun() ->
  551. Self = self(),
  552. {compiled, Mod} = setup_query(testmod14,
  553. glc:with(glc:eq(a, 1), fun(Event) -> Self ! gre:fetch(a, Event) end)),
  554. glc:handle(Mod, gre:make([{a,1}], [list])),
  555. ?assertEqual(1, Mod:info(output)),
  556. ?assertEqual(1, receive Msg -> Msg after 0 -> notcalled end)
  557. end
  558. },
  559. {"with function storage test",
  560. fun() ->
  561. Self = self(),
  562. Store = [{stored, value}],
  563. {compiled, Mod} = setup_query(testmod15,
  564. glc:with(glc:eq(a, 1), fun(Event, EStore) ->
  565. Self ! {gre:fetch(a, Event), EStore} end),
  566. Store),
  567. glc:handle(Mod, gre:make([{a,1}], [list])),
  568. ?assertEqual(1, Mod:info(output)),
  569. ?assertEqual(1, receive {Msg, Store} -> Msg after 0 -> notcalled end)
  570. end
  571. },
  572. {"delete test",
  573. fun() ->
  574. {compiled, Mod} = setup_query(testmod16, glc:null(false)),
  575. ?assert(is_atom(Mod:table(params))),
  576. ?assertMatch([_|_], gr_param:info(Mod:table(params))),
  577. ?assert(is_list(code:which(Mod))),
  578. ?assert(is_pid(whereis(params_name(Mod)))),
  579. ?assert(is_pid(whereis(counts_name(Mod)))),
  580. ?assert(is_pid(whereis(manage_params_name(Mod)))),
  581. ?assert(is_pid(whereis(manage_counts_name(Mod)))),
  582. glc:delete(Mod),
  583. ?assertEqual(non_existing, code:which(Mod)),
  584. ?assertEqual(undefined, whereis(params_name(Mod))),
  585. ?assertEqual(undefined, whereis(counts_name(Mod))),
  586. ?assertEqual(undefined, whereis(manage_params_name(Mod))),
  587. ?assertEqual(undefined, whereis(manage_counts_name(Mod)))
  588. end
  589. },
  590. {"reset counters test",
  591. fun() ->
  592. {compiled, Mod} = setup_query(testmod17,
  593. glc:any([glc:eq(a, 1), glc:eq(b, 2)])),
  594. glc:handle(Mod, gre:make([{'a', 2}], [list])),
  595. glc:handle(Mod, gre:make([{'b', 1}], [list])),
  596. ?assertEqual(2, Mod:info(input)),
  597. ?assertEqual(2, Mod:info(filter)),
  598. glc:handle(Mod, gre:make([{'a', 1}], [list])),
  599. glc:handle(Mod, gre:make([{'b', 2}], [list])),
  600. ?assertEqual(4, Mod:info(input)),
  601. ?assertEqual(2, Mod:info(filter)),
  602. ?assertEqual(2, Mod:info(output)),
  603. glc:reset_counters(Mod, input),
  604. ?assertEqual(0, Mod:info(input)),
  605. ?assertEqual(2, Mod:info(filter)),
  606. ?assertEqual(2, Mod:info(output)),
  607. glc:reset_counters(Mod, filter),
  608. ?assertEqual(0, Mod:info(input)),
  609. ?assertEqual(0, Mod:info(filter)),
  610. ?assertEqual(2, Mod:info(output)),
  611. glc:reset_counters(Mod),
  612. ?assertEqual(0, Mod:info(input)),
  613. ?assertEqual(0, Mod:info(filter)),
  614. ?assertEqual(0, Mod:info(output))
  615. end
  616. },
  617. {"ets data recovery test",
  618. fun() ->
  619. Self = self(),
  620. {compiled, Mod} = setup_query(testmod18,
  621. glc:with(glc:eq(a, 1), fun(Event) -> Self ! gre:fetch(a, Event) end)),
  622. glc:handle(Mod, gre:make([{a,1}], [list])),
  623. ?assertEqual(1, Mod:info(output)),
  624. ?assertEqual(1, receive Msg -> Msg after 0 -> notcalled end),
  625. ?assertEqual(1, length(gr_param:list(Mod:table(params)))),
  626. ?assertEqual(7, length(gr_param:list(Mod:table(counters)))),
  627. true = exit(whereis(Mod:table(params)), kill),
  628. true = exit(whereis(Mod:table(counters)), kill),
  629. ?assertEqual(1, Mod:info(input)),
  630. glc:handle(Mod, gre:make([{'a', 1}], [list])),
  631. ?assertEqual(2, Mod:info(input)),
  632. ?assertEqual(2, Mod:info(output)),
  633. ?assertEqual(1, length(gr_param:list(Mod:table(params)))),
  634. ?assertEqual(7, length(gr_counter:list(Mod:table(counters))))
  635. end
  636. },
  637. {"run timed job test",
  638. fun() ->
  639. Self = self(),
  640. Store = [{stored, value}],
  641. Runtime = 0.15,
  642. {compiled, Mod} = setup_query(testmod19,
  643. glc:gt(runtime, Runtime),
  644. Store),
  645. glc:run(Mod, fun(Event, EStore) ->
  646. timer:sleep(100),
  647. Self ! {gre:fetch(a, Event), EStore}
  648. end, gre:make([{a,1}], [list])),
  649. ?assertEqual(0, Mod:info(output)),
  650. ?assertEqual(1, Mod:info(filter)),
  651. ?assertEqual(1, receive {Msg, Store} -> Msg after 0 -> notcalled end),
  652. delete(testmod19),
  653. {compiled, Mod} = setup_query(testmod19,
  654. glc:gt(runtime, Runtime),
  655. Store),
  656. glc:handle(Mod, gre:make([{'a', 1}], [list])),
  657. glc:run(Mod, fun(Event, EStore) ->
  658. timer:sleep(200),
  659. Self ! {gre:fetch(a, Event), EStore}
  660. end, gre:make([{a,2}], [list])),
  661. ?assertEqual(1, Mod:info(output)),
  662. ?assertEqual(1, Mod:info(filter)),
  663. ?assertEqual(2, receive {Msg, Store} -> Msg after 0 -> notcalled end)
  664. end
  665. },
  666. {"reset job counters",
  667. fun() ->
  668. {compiled, Mod} = setup_query(testmod20,
  669. glc:any([glc:eq(a, 1), glc:gt(runtime, 0.15)])),
  670. glc:handle(Mod, gre:make([{'a', 2}], [list])),
  671. glc:handle(Mod, gre:make([{'b', 1}], [list])),
  672. ?assertEqual(2, Mod:info(input)),
  673. ?assertEqual(2, Mod:info(filter)),
  674. glc:handle(Mod, gre:make([{'a', 1}], [list])),
  675. glc:handle(Mod, gre:make([{'b', 2}], [list])),
  676. ?assertEqual(4, Mod:info(input)),
  677. ?assertEqual(3, Mod:info(filter)),
  678. ?assertEqual(1, Mod:info(output)),
  679. Self = self(),
  680. glc:run(Mod, fun(Event, EStore) ->
  681. timer:sleep(100),
  682. Self ! {gre:fetch(a, Event), EStore}
  683. end, gre:make([{a,1}], [list])),
  684. ?assertEqual(2, Mod:info(output)),
  685. ?assertEqual(3, Mod:info(filter)),
  686. ?assertEqual(1, receive {Msg, undefined} -> Msg after 0 -> notcalled end),
  687. Msg1 = glc:run(Mod, fun(_Event, _EStore) ->
  688. timer:sleep(200),
  689. {error, badtest}
  690. end, gre:make([{a,1}], [list])),
  691. ?assertEqual(2, Mod:info(output)),
  692. ?assertEqual(3, Mod:info(filter)),
  693. ?assertEqual(2, Mod:info(job_input)),
  694. ?assertEqual(1, Mod:info(job_error)),
  695. ?assertEqual(1, Mod:info(job_run)),
  696. ?assertEqual({error, badtest}, Msg1),
  697. Msg2 = glc:run(Mod, fun(_Event, _EStore) ->
  698. timer:sleep(200),
  699. {ok, goodtest}
  700. end, gre:make([{a,1}], [list])),
  701. ?assertEqual(3, Mod:info(output)),
  702. ?assertEqual(3, Mod:info(filter)),
  703. ?assertEqual(3, Mod:info(job_input)),
  704. ?assertEqual(1, Mod:info(job_error)),
  705. ?assertEqual(2, Mod:info(job_run)),
  706. ?assertEqual({ok, goodtest}, Msg2),
  707. glc:reset_counters(Mod, input),
  708. ?assertEqual(0, Mod:info(input)),
  709. ?assertEqual(3, Mod:info(filter)),
  710. ?assertEqual(3, Mod:info(output)),
  711. ?assertEqual(3, Mod:info(job_input)),
  712. ?assertEqual(1, Mod:info(job_error)),
  713. ?assertEqual(2, Mod:info(job_run)),
  714. glc:reset_counters(Mod, filter),
  715. ?assertEqual(0, glc:input(Mod)),
  716. ?assertEqual(0, glc:filter(Mod)),
  717. ?assertEqual(3, glc:output(Mod)),
  718. ?assertEqual(3, glc:job_input(Mod)),
  719. ?assertEqual(1, glc:job_error(Mod)),
  720. ?assertEqual(2, glc:job_run(Mod)),
  721. glc:reset_counters(Mod, output),
  722. ?assertEqual(0, Mod:info(input)),
  723. ?assertEqual(0, Mod:info(filter)),
  724. ?assertEqual(0, Mod:info(output)),
  725. ?assertEqual(3, Mod:info(job_input)),
  726. ?assertEqual(1, Mod:info(job_error)),
  727. ?assertEqual(2, Mod:info(job_run)),
  728. glc:reset_counters(Mod, job_input),
  729. ?assertEqual(0, Mod:info(input)),
  730. ?assertEqual(0, Mod:info(filter)),
  731. ?assertEqual(0, Mod:info(output)),
  732. ?assertEqual(0, Mod:info(job_input)),
  733. ?assertEqual(1, Mod:info(job_error)),
  734. ?assertEqual(2, Mod:info(job_run)),
  735. glc:reset_counters(Mod, job_error),
  736. ?assertEqual(0, Mod:info(input)),
  737. ?assertEqual(0, Mod:info(filter)),
  738. ?assertEqual(0, Mod:info(output)),
  739. ?assertEqual(0, Mod:info(job_input)),
  740. ?assertEqual(0, Mod:info(job_error)),
  741. ?assertEqual(2, Mod:info(job_run)),
  742. glc:reset_counters(Mod, job_run),
  743. ?assertEqual(0, Mod:info(input)),
  744. ?assertEqual(0, Mod:info(filter)),
  745. ?assertEqual(0, Mod:info(output)),
  746. ?assertEqual(0, Mod:info(job_input)),
  747. ?assertEqual(0, Mod:info(job_error)),
  748. ?assertEqual(0, Mod:info(job_run))
  749. end
  750. },
  751. {"variable storage test",
  752. fun() ->
  753. {compiled, Mod} = setup_query(testmod21,
  754. glc:eq(a, 2), [{stream, time}]),
  755. glc:handle(Mod, gre:make([{'a', 2}], [list])),
  756. glc:handle(Mod, gre:make([{'b', 1}], [list])),
  757. ?assertEqual(2, Mod:info(input)),
  758. ?assertEqual(1, Mod:info(filter)),
  759. glc:handle(Mod, gre:make([{'b', 2}], [list])),
  760. ?assertEqual(3, Mod:info(input)),
  761. ?assertEqual(2, Mod:info(filter)),
  762. ?assertEqual({ok, time}, glc:get(Mod, stream)),
  763. ?assertEqual({error, undefined}, glc:get(Mod, beam))
  764. end
  765. },
  766. {"variable storage test",
  767. fun() ->
  768. {compiled, Mod} = setup_query(testmod19,
  769. glc:eq(a, 2), [{stream, time}]),
  770. glc:handle(Mod, gre:make([{'a', 2}], [list])),
  771. glc:handle(Mod, gre:make([{'b', 1}], [list])),
  772. ?assertEqual(2, Mod:info(input)),
  773. ?assertEqual(1, Mod:info(filter)),
  774. glc:handle(Mod, gre:make([{'b', 2}], [list])),
  775. ?assertEqual(3, Mod:info(input)),
  776. ?assertEqual(2, Mod:info(filter)),
  777. ?assertEqual({ok, time}, glc:get(Mod, stream)),
  778. ?assertEqual({error, undefined}, glc:get(Mod, beam))
  779. end
  780. },
  781. {"with multi function any test",
  782. fun() ->
  783. Self = self(),
  784. Store = [{stored, value}],
  785. G1 = glc:with(glc:eq(a, 1), fun(_Event, EStore) ->
  786. Self ! {a, EStore} end),
  787. G2 = glc:with(glc:eq(b, 2), fun(_Event, EStore) ->
  788. Self ! {b, EStore} end),
  789. {compiled, Mod} = setup_query(testmod20, any([G1, G2]),
  790. Store),
  791. glc:handle(Mod, gre:make([{a,1}], [list])),
  792. ?assertEqual(1, Mod:info(output)),
  793. ?assertEqual(a, receive {Msg, _Store} -> Msg after 0 -> notcalled end),
  794. ?assertEqual(b, receive {Msg, _Store} -> Msg after 0 -> notcalled end)
  795. end
  796. },
  797. {"with multi function all test",
  798. fun() ->
  799. Self = self(),
  800. Store = [{stored, value}],
  801. G1 = glc:with(glc:eq(a, 1), fun(_Event, EStore) ->
  802. Self ! {a, EStore} end),
  803. G2 = glc:with(glc:eq(b, 2), fun(_Event, EStore) ->
  804. Self ! {b, EStore} end),
  805. G3 = glc:with(glc:eq(c, 3), fun(_Event, EStore) ->
  806. Self ! {c, EStore} end),
  807. {compiled, Mod} = setup_query(testmod21, all([G1, G2, G3]),
  808. Store),
  809. glc:handle(Mod, gre:make([{a,1}], [list])),
  810. ?assertEqual(0, Mod:info(output)),
  811. ?assertEqual(1, Mod:info(filter)),
  812. glc:handle(Mod, gre:make([{a,1}, {b, 2}], [list])),
  813. ?assertEqual(0, Mod:info(output)),
  814. ?assertEqual(2, Mod:info(filter)),
  815. glc:handle(Mod, gre:make([{a,1}, {b, 2}, {c, 3}], [list])),
  816. ?assertEqual(1, Mod:info(output)),
  817. ?assertEqual(a, receive {Msg, _Store} -> Msg after 0 -> notcalled end),
  818. ?assertEqual(b, receive {Msg, _Store} -> Msg after 0 -> notcalled end),
  819. ?assertEqual(c, receive {Msg, _Store} -> Msg after 0 -> notcalled end)
  820. end
  821. },
  822. {"with multi-function output match test",
  823. fun() ->
  824. Self = self(),
  825. Store = [{stored, value}],
  826. {compiled, Mod} = setup_query(testmod22,
  827. [glc:with(glc:eq(a, 1), fun(Event, _EStore) ->
  828. Self ! {a, gre:fetch(a, Event)} end),
  829. glc:with(glc:gt(b, 1), fun(Event, _EStore) ->
  830. Self ! {b, gre:fetch(b, Event)} end)],
  831. Store),
  832. glc:handle(Mod, gre:make([{a,1}, {b, 1}], [list])),
  833. ?assertEqual(1, Mod:info(output)),
  834. ?assertEqual(a, receive {a=Msg, _Store} -> Msg after 0 -> notcalled end)
  835. end
  836. },
  837. {"with multi-function output double-match test",
  838. fun() ->
  839. Self = self(),
  840. Store = [{stored, value}],
  841. {compiled, Mod} = setup_query(testmod23,
  842. [glc:with(glc:eq(a, 1), fun(Event, _EStore) ->
  843. Self ! {a, gre:fetch(a, Event)} end),
  844. glc:with(glc:eq(b, 1), fun(Event, _EStore) ->
  845. Self ! {b, gre:fetch(b, Event)} end)],
  846. Store),
  847. glc:handle(Mod, gre:make([{a,1}, {b, 1}], [list])),
  848. ?assertEqual(2, Mod:info(output)),
  849. ?assertEqual(a, receive {a=Msg, _Store} -> Msg after 0 -> notcalled end),
  850. ?assertEqual(b, receive {b=Msg, _Store} -> Msg after 0 -> notcalled end)
  851. end
  852. },
  853. {"with multi function complex match test",
  854. fun() ->
  855. Self = self(),
  856. Store = [{stored, value}],
  857. G1 = glc:with(glc:gt(r, 0.1), fun(_Event, EStore) ->
  858. Self ! {a, EStore} end),
  859. G2 = glc:with(glc:all([glc:eq(a, 1), glc:gt(r, 0.5)]), fun(_Event, EStore) ->
  860. Self ! {b, EStore} end),
  861. G3 = glc:with(glc:all([glc:eq(a, 1), glc:eq(b, 2), glc:gt(r, 0.6)]), fun(_Event, EStore) ->
  862. Self ! {c, EStore} end),
  863. {compiled, Mod} = setup_query(testmod24, [G1, G2, G3],
  864. Store),
  865. glc:handle(Mod, gre:make([{a,1}, {r, 0.7}, {b, 3}], [list])),
  866. ?assertEqual(2, Mod:info(output)),
  867. ?assertEqual(1, Mod:info(input)),
  868. ?assertEqual(1, Mod:info(filter)),
  869. ?assertEqual(b, receive {Msg, _Store} -> Msg after 0 -> notcalled end),
  870. ?assertEqual(a, receive {Msg, _Store} -> Msg after 0 -> notcalled end),
  871. %
  872. glc:handle(Mod, gre:make([{a,1}, {r, 0.6}], [list])),
  873. ?assertEqual(4, Mod:info(output)),
  874. ?assertEqual(2, Mod:info(input)),
  875. ?assertEqual(2, Mod:info(filter)),
  876. ?assertEqual(b, receive {Msg, _Store} -> Msg after 0 -> notcalled end),
  877. ?assertEqual(a, receive {Msg, _Store} -> Msg after 0 -> notcalled end),
  878. %
  879. glc:handle(Mod, gre:make([{a,2}, {r, 0.7}, {b, 3}], [list])),
  880. ?assertEqual(5, Mod:info(output)),
  881. ?assertEqual(3, Mod:info(input)),
  882. ?assertEqual(4, Mod:info(filter)),
  883. ?assertEqual(a, receive {Msg, _Store} -> Msg after 0 -> notcalled end),
  884. glc:handle(Mod, gre:make([{a,1}, {r, 0.7}, {b, 2}], [list])),
  885. ?assertEqual(8, Mod:info(output)),
  886. ?assertEqual(4, Mod:info(input)),
  887. ?assertEqual(4, Mod:info(filter)),
  888. ?assertEqual(c, receive {Msg, _Store} -> Msg after 0 -> notcalled end),
  889. ?assertEqual(b, receive {Msg, _Store} -> Msg after 0 -> notcalled end),
  890. ?assertEqual(a, receive {Msg, _Store} -> Msg after 0 -> notcalled end)
  891. end
  892. }
  893. ]
  894. }.
  895. union_error_test() ->
  896. ?assertError(badarg, glc:union([glc:eq(a, 1)])),
  897. done.
  898. -endif.