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 line
41 KiB

  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.