fluentd 0.12.40 → 1.6.2

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of fluentd might be problematic. Click here for more details.

Files changed (428) hide show
  1. checksums.yaml +5 -5
  2. data/.github/ISSUE_TEMPLATE/bug_report.md +39 -0
  3. data/.github/ISSUE_TEMPLATE/feature_request.md +23 -0
  4. data/.github/ISSUE_TEMPLATE.md +17 -0
  5. data/.github/PULL_REQUEST_TEMPLATE.md +13 -0
  6. data/.gitignore +5 -0
  7. data/.gitlab/cicd-template.yaml +10 -0
  8. data/.gitlab-ci.yml +147 -0
  9. data/.travis.yml +56 -20
  10. data/ADOPTERS.md +5 -0
  11. data/CHANGELOG.md +1369 -0
  12. data/CONTRIBUTING.md +16 -5
  13. data/GOVERNANCE.md +55 -0
  14. data/Gemfile +5 -0
  15. data/GithubWorkflow.md +78 -0
  16. data/LICENSE +202 -0
  17. data/MAINTAINERS.md +7 -0
  18. data/README.md +23 -11
  19. data/Rakefile +48 -2
  20. data/Vagrantfile +17 -0
  21. data/appveyor.yml +37 -0
  22. data/bin/fluent-binlog-reader +7 -0
  23. data/bin/fluent-ca-generate +6 -0
  24. data/bin/fluent-plugin-config-format +5 -0
  25. data/bin/fluent-plugin-generate +5 -0
  26. data/bin/fluentd +3 -0
  27. data/code-of-conduct.md +3 -0
  28. data/example/copy_roundrobin.conf +39 -0
  29. data/example/counter.conf +18 -0
  30. data/example/in_dummy_blocks.conf +17 -0
  31. data/example/in_dummy_with_compression.conf +23 -0
  32. data/example/in_forward.conf +7 -0
  33. data/example/in_forward_client.conf +37 -0
  34. data/example/in_forward_shared_key.conf +15 -0
  35. data/example/in_forward_tls.conf +14 -0
  36. data/example/in_forward_users.conf +24 -0
  37. data/example/in_forward_workers.conf +21 -0
  38. data/example/in_http.conf +3 -1
  39. data/example/in_out_forward.conf +17 -0
  40. data/example/logevents.conf +25 -0
  41. data/example/multi_filters.conf +61 -0
  42. data/example/out_exec_filter.conf +42 -0
  43. data/example/out_forward.conf +13 -13
  44. data/example/out_forward_buf_file.conf +23 -0
  45. data/example/out_forward_client.conf +109 -0
  46. data/example/out_forward_heartbeat_none.conf +16 -0
  47. data/example/out_forward_shared_key.conf +36 -0
  48. data/example/out_forward_tls.conf +18 -0
  49. data/example/out_forward_users.conf +65 -0
  50. data/example/out_null.conf +36 -0
  51. data/example/secondary_file.conf +42 -0
  52. data/example/suppress_config_dump.conf +7 -0
  53. data/example/worker_section.conf +36 -0
  54. data/fluent.conf +29 -0
  55. data/fluentd.gemspec +21 -11
  56. data/lib/fluent/agent.rb +67 -90
  57. data/lib/fluent/clock.rb +62 -0
  58. data/lib/fluent/command/binlog_reader.rb +244 -0
  59. data/lib/fluent/command/ca_generate.rb +181 -0
  60. data/lib/fluent/command/cat.rb +42 -18
  61. data/lib/fluent/command/debug.rb +12 -10
  62. data/lib/fluent/command/fluentd.rb +153 -5
  63. data/lib/fluent/command/plugin_config_formatter.rb +292 -0
  64. data/lib/fluent/command/plugin_generator.rb +324 -0
  65. data/lib/fluent/compat/call_super_mixin.rb +67 -0
  66. data/lib/fluent/compat/detach_process_mixin.rb +33 -0
  67. data/lib/fluent/compat/exec_util.rb +129 -0
  68. data/lib/fluent/compat/file_util.rb +54 -0
  69. data/lib/fluent/compat/filter.rb +68 -0
  70. data/lib/fluent/compat/formatter.rb +111 -0
  71. data/lib/fluent/compat/formatter_utils.rb +85 -0
  72. data/lib/fluent/compat/handle_tag_and_time_mixin.rb +62 -0
  73. data/lib/fluent/compat/handle_tag_name_mixin.rb +53 -0
  74. data/lib/fluent/compat/input.rb +49 -0
  75. data/lib/fluent/compat/output.rb +718 -0
  76. data/lib/fluent/compat/output_chain.rb +60 -0
  77. data/lib/fluent/compat/parser.rb +310 -0
  78. data/lib/fluent/compat/parser_utils.rb +40 -0
  79. data/lib/fluent/compat/propagate_default.rb +62 -0
  80. data/lib/fluent/compat/record_filter_mixin.rb +34 -0
  81. data/lib/fluent/compat/set_tag_key_mixin.rb +50 -0
  82. data/lib/fluent/compat/set_time_key_mixin.rb +69 -0
  83. data/lib/fluent/compat/socket_util.rb +165 -0
  84. data/lib/fluent/compat/string_util.rb +34 -0
  85. data/lib/fluent/compat/structured_format_mixin.rb +26 -0
  86. data/lib/fluent/compat/type_converter.rb +90 -0
  87. data/lib/fluent/config/configure_proxy.rb +210 -62
  88. data/lib/fluent/config/dsl.rb +12 -5
  89. data/lib/fluent/config/element.rb +107 -9
  90. data/lib/fluent/config/literal_parser.rb +9 -3
  91. data/lib/fluent/config/parser.rb +4 -4
  92. data/lib/fluent/config/section.rb +51 -14
  93. data/lib/fluent/config/types.rb +28 -13
  94. data/lib/fluent/config/v1_parser.rb +3 -5
  95. data/lib/fluent/config.rb +23 -20
  96. data/lib/fluent/configurable.rb +79 -21
  97. data/lib/fluent/counter/base_socket.rb +46 -0
  98. data/lib/fluent/counter/client.rb +297 -0
  99. data/lib/fluent/counter/error.rb +86 -0
  100. data/lib/fluent/counter/mutex_hash.rb +163 -0
  101. data/lib/fluent/counter/server.rb +273 -0
  102. data/lib/fluent/counter/store.rb +205 -0
  103. data/lib/fluent/counter/validator.rb +145 -0
  104. data/lib/fluent/counter.rb +23 -0
  105. data/lib/fluent/daemon.rb +15 -0
  106. data/lib/fluent/engine.rb +102 -65
  107. data/lib/fluent/env.rb +7 -3
  108. data/lib/fluent/error.rb +30 -0
  109. data/lib/fluent/event.rb +197 -21
  110. data/lib/fluent/event_router.rb +93 -10
  111. data/lib/fluent/filter.rb +2 -50
  112. data/lib/fluent/formatter.rb +4 -293
  113. data/lib/fluent/input.rb +2 -32
  114. data/lib/fluent/label.rb +10 -2
  115. data/lib/fluent/load.rb +3 -3
  116. data/lib/fluent/log.rb +348 -81
  117. data/lib/fluent/match.rb +37 -36
  118. data/lib/fluent/mixin.rb +12 -176
  119. data/lib/fluent/msgpack_factory.rb +62 -0
  120. data/lib/fluent/output.rb +10 -612
  121. data/lib/fluent/output_chain.rb +23 -0
  122. data/lib/fluent/parser.rb +4 -800
  123. data/lib/fluent/plugin/bare_output.rb +63 -0
  124. data/lib/fluent/plugin/base.rb +192 -0
  125. data/lib/fluent/plugin/buf_file.rb +128 -174
  126. data/lib/fluent/plugin/buf_memory.rb +9 -92
  127. data/lib/fluent/plugin/buffer/chunk.rb +221 -0
  128. data/lib/fluent/plugin/buffer/file_chunk.rb +383 -0
  129. data/lib/fluent/plugin/buffer/memory_chunk.rb +90 -0
  130. data/lib/fluent/plugin/buffer.rb +779 -0
  131. data/lib/fluent/plugin/compressable.rb +92 -0
  132. data/lib/fluent/plugin/exec_util.rb +3 -108
  133. data/lib/fluent/plugin/file_util.rb +4 -34
  134. data/lib/fluent/plugin/file_wrapper.rb +120 -0
  135. data/lib/fluent/plugin/filter.rb +93 -0
  136. data/lib/fluent/plugin/filter_grep.rb +117 -34
  137. data/lib/fluent/plugin/filter_parser.rb +85 -62
  138. data/lib/fluent/plugin/filter_record_transformer.rb +27 -39
  139. data/lib/fluent/plugin/filter_stdout.rb +15 -12
  140. data/lib/fluent/plugin/formatter.rb +50 -0
  141. data/lib/fluent/plugin/formatter_csv.rb +52 -0
  142. data/lib/fluent/plugin/formatter_hash.rb +33 -0
  143. data/lib/fluent/plugin/formatter_json.rb +55 -0
  144. data/lib/fluent/plugin/formatter_ltsv.rb +42 -0
  145. data/lib/fluent/plugin/formatter_msgpack.rb +33 -0
  146. data/lib/fluent/plugin/formatter_out_file.rb +51 -0
  147. data/lib/fluent/plugin/formatter_single_value.rb +34 -0
  148. data/lib/fluent/plugin/formatter_stdout.rb +76 -0
  149. data/lib/fluent/plugin/formatter_tsv.rb +38 -0
  150. data/lib/fluent/plugin/in_debug_agent.rb +17 -6
  151. data/lib/fluent/plugin/in_dummy.rb +47 -20
  152. data/lib/fluent/plugin/in_exec.rb +55 -123
  153. data/lib/fluent/plugin/in_forward.rb +299 -216
  154. data/lib/fluent/plugin/in_gc_stat.rb +14 -36
  155. data/lib/fluent/plugin/in_http.rb +204 -91
  156. data/lib/fluent/plugin/in_monitor_agent.rb +186 -258
  157. data/lib/fluent/plugin/in_object_space.rb +13 -41
  158. data/lib/fluent/plugin/in_syslog.rb +112 -134
  159. data/lib/fluent/plugin/in_tail.rb +408 -745
  160. data/lib/fluent/plugin/in_tcp.rb +66 -9
  161. data/lib/fluent/plugin/in_udp.rb +60 -11
  162. data/lib/fluent/plugin/{in_stream.rb → in_unix.rb} +8 -4
  163. data/lib/fluent/plugin/input.rb +37 -0
  164. data/lib/fluent/plugin/multi_output.rb +158 -0
  165. data/lib/fluent/plugin/out_copy.rb +23 -35
  166. data/lib/fluent/plugin/out_exec.rb +67 -70
  167. data/lib/fluent/plugin/out_exec_filter.rb +204 -271
  168. data/lib/fluent/plugin/out_file.rb +267 -73
  169. data/lib/fluent/plugin/out_forward.rb +854 -325
  170. data/lib/fluent/plugin/out_null.rb +42 -9
  171. data/lib/fluent/plugin/out_relabel.rb +9 -5
  172. data/lib/fluent/plugin/out_roundrobin.rb +18 -37
  173. data/lib/fluent/plugin/out_secondary_file.rb +133 -0
  174. data/lib/fluent/plugin/out_stdout.rb +43 -10
  175. data/lib/fluent/plugin/out_stream.rb +7 -2
  176. data/lib/fluent/plugin/output.rb +1498 -0
  177. data/lib/fluent/plugin/owned_by_mixin.rb +42 -0
  178. data/lib/fluent/plugin/parser.rb +191 -0
  179. data/lib/fluent/plugin/parser_apache.rb +28 -0
  180. data/lib/fluent/plugin/parser_apache2.rb +88 -0
  181. data/lib/fluent/plugin/parser_apache_error.rb +26 -0
  182. data/lib/fluent/plugin/parser_csv.rb +39 -0
  183. data/lib/fluent/plugin/parser_json.rb +94 -0
  184. data/lib/fluent/plugin/parser_ltsv.rb +49 -0
  185. data/lib/fluent/plugin/parser_msgpack.rb +50 -0
  186. data/lib/fluent/plugin/parser_multiline.rb +106 -0
  187. data/lib/fluent/plugin/parser_nginx.rb +28 -0
  188. data/lib/fluent/plugin/parser_none.rb +36 -0
  189. data/lib/fluent/plugin/parser_regexp.rb +68 -0
  190. data/lib/fluent/plugin/parser_syslog.rb +142 -0
  191. data/lib/fluent/plugin/parser_tsv.rb +42 -0
  192. data/lib/fluent/plugin/socket_util.rb +3 -143
  193. data/lib/fluent/plugin/storage.rb +84 -0
  194. data/lib/fluent/plugin/storage_local.rb +164 -0
  195. data/lib/fluent/plugin/string_util.rb +3 -15
  196. data/lib/fluent/plugin.rb +122 -121
  197. data/lib/fluent/plugin_helper/cert_option.rb +178 -0
  198. data/lib/fluent/plugin_helper/child_process.rb +364 -0
  199. data/lib/fluent/plugin_helper/compat_parameters.rb +333 -0
  200. data/lib/fluent/plugin_helper/counter.rb +51 -0
  201. data/lib/fluent/plugin_helper/event_emitter.rb +93 -0
  202. data/lib/fluent/plugin_helper/event_loop.rb +170 -0
  203. data/lib/fluent/plugin_helper/extract.rb +104 -0
  204. data/lib/fluent/plugin_helper/formatter.rb +147 -0
  205. data/lib/fluent/plugin_helper/http_server/app.rb +79 -0
  206. data/lib/fluent/plugin_helper/http_server/compat/server.rb +81 -0
  207. data/lib/fluent/plugin_helper/http_server/compat/webrick_handler.rb +58 -0
  208. data/lib/fluent/plugin_helper/http_server/methods.rb +35 -0
  209. data/lib/fluent/plugin_helper/http_server/request.rb +42 -0
  210. data/lib/fluent/plugin_helper/http_server/router.rb +54 -0
  211. data/lib/fluent/plugin_helper/http_server/server.rb +87 -0
  212. data/lib/fluent/plugin_helper/http_server.rb +76 -0
  213. data/lib/fluent/plugin_helper/inject.rb +151 -0
  214. data/lib/fluent/plugin_helper/parser.rb +147 -0
  215. data/lib/fluent/plugin_helper/record_accessor.rb +210 -0
  216. data/lib/fluent/plugin_helper/retry_state.rb +205 -0
  217. data/lib/fluent/plugin_helper/server.rb +807 -0
  218. data/lib/fluent/plugin_helper/socket.rb +250 -0
  219. data/lib/fluent/plugin_helper/socket_option.rb +80 -0
  220. data/lib/fluent/plugin_helper/storage.rb +349 -0
  221. data/lib/fluent/plugin_helper/thread.rb +179 -0
  222. data/lib/fluent/plugin_helper/timer.rb +92 -0
  223. data/lib/fluent/plugin_helper.rb +73 -0
  224. data/lib/fluent/plugin_id.rb +80 -0
  225. data/lib/fluent/process.rb +3 -489
  226. data/lib/fluent/registry.rb +52 -10
  227. data/lib/fluent/root_agent.rb +204 -42
  228. data/lib/fluent/supervisor.rb +597 -359
  229. data/lib/fluent/system_config.rb +131 -42
  230. data/lib/fluent/test/base.rb +6 -54
  231. data/lib/fluent/test/driver/base.rb +224 -0
  232. data/lib/fluent/test/driver/base_owned.rb +70 -0
  233. data/lib/fluent/test/driver/base_owner.rb +135 -0
  234. data/lib/fluent/test/driver/event_feeder.rb +98 -0
  235. data/lib/fluent/test/driver/filter.rb +57 -0
  236. data/lib/fluent/test/driver/formatter.rb +30 -0
  237. data/lib/fluent/test/driver/input.rb +31 -0
  238. data/lib/fluent/test/driver/multi_output.rb +53 -0
  239. data/lib/fluent/test/driver/output.rb +102 -0
  240. data/lib/fluent/test/driver/parser.rb +30 -0
  241. data/lib/fluent/test/driver/test_event_router.rb +45 -0
  242. data/lib/fluent/test/filter_test.rb +0 -1
  243. data/lib/fluent/test/formatter_test.rb +4 -1
  244. data/lib/fluent/test/helpers.rb +58 -10
  245. data/lib/fluent/test/input_test.rb +27 -19
  246. data/lib/fluent/test/log.rb +79 -0
  247. data/lib/fluent/test/output_test.rb +28 -39
  248. data/lib/fluent/test/parser_test.rb +3 -1
  249. data/lib/fluent/test/startup_shutdown.rb +46 -0
  250. data/lib/fluent/test.rb +33 -1
  251. data/lib/fluent/time.rb +450 -1
  252. data/lib/fluent/timezone.rb +27 -3
  253. data/lib/fluent/{status.rb → unique_id.rb} +15 -24
  254. data/lib/fluent/version.rb +1 -1
  255. data/lib/fluent/winsvc.rb +85 -0
  256. data/templates/new_gem/Gemfile +3 -0
  257. data/templates/new_gem/README.md.erb +43 -0
  258. data/templates/new_gem/Rakefile +13 -0
  259. data/templates/new_gem/fluent-plugin.gemspec.erb +27 -0
  260. data/templates/new_gem/lib/fluent/plugin/filter.rb.erb +14 -0
  261. data/templates/new_gem/lib/fluent/plugin/formatter.rb.erb +14 -0
  262. data/templates/new_gem/lib/fluent/plugin/input.rb.erb +11 -0
  263. data/templates/new_gem/lib/fluent/plugin/output.rb.erb +11 -0
  264. data/templates/new_gem/lib/fluent/plugin/parser.rb.erb +15 -0
  265. data/templates/new_gem/test/helper.rb.erb +8 -0
  266. data/templates/new_gem/test/plugin/test_filter.rb.erb +18 -0
  267. data/templates/new_gem/test/plugin/test_formatter.rb.erb +18 -0
  268. data/templates/new_gem/test/plugin/test_input.rb.erb +18 -0
  269. data/templates/new_gem/test/plugin/test_output.rb.erb +18 -0
  270. data/templates/new_gem/test/plugin/test_parser.rb.erb +18 -0
  271. data/templates/plugin_config_formatter/param.md-compact.erb +25 -0
  272. data/templates/plugin_config_formatter/param.md.erb +34 -0
  273. data/templates/plugin_config_formatter/section.md.erb +12 -0
  274. data/test/command/test_binlog_reader.rb +346 -0
  275. data/test/command/test_ca_generate.rb +70 -0
  276. data/test/command/test_fluentd.rb +901 -0
  277. data/test/command/test_plugin_config_formatter.rb +276 -0
  278. data/test/command/test_plugin_generator.rb +92 -0
  279. data/test/compat/test_calls_super.rb +166 -0
  280. data/test/compat/test_parser.rb +92 -0
  281. data/test/config/test_config_parser.rb +126 -2
  282. data/test/config/test_configurable.rb +946 -187
  283. data/test/config/test_configure_proxy.rb +424 -74
  284. data/test/config/test_dsl.rb +11 -11
  285. data/test/config/test_element.rb +500 -0
  286. data/test/config/test_literal_parser.rb +8 -0
  287. data/test/config/test_plugin_configuration.rb +56 -0
  288. data/test/config/test_section.rb +79 -7
  289. data/test/config/test_system_config.rb +122 -35
  290. data/test/config/test_types.rb +38 -0
  291. data/test/counter/test_client.rb +559 -0
  292. data/test/counter/test_error.rb +44 -0
  293. data/test/counter/test_mutex_hash.rb +179 -0
  294. data/test/counter/test_server.rb +589 -0
  295. data/test/counter/test_store.rb +258 -0
  296. data/test/counter/test_validator.rb +137 -0
  297. data/test/helper.rb +89 -6
  298. data/test/helpers/fuzzy_assert.rb +89 -0
  299. data/test/plugin/test_bare_output.rb +118 -0
  300. data/test/plugin/test_base.rb +115 -0
  301. data/test/plugin/test_buf_file.rb +823 -460
  302. data/test/plugin/test_buf_memory.rb +32 -194
  303. data/test/plugin/test_buffer.rb +1233 -0
  304. data/test/plugin/test_buffer_chunk.rb +198 -0
  305. data/test/plugin/test_buffer_file_chunk.rb +844 -0
  306. data/test/plugin/test_buffer_memory_chunk.rb +338 -0
  307. data/test/plugin/test_compressable.rb +84 -0
  308. data/test/plugin/test_filter.rb +357 -0
  309. data/test/plugin/test_filter_grep.rb +540 -29
  310. data/test/plugin/test_filter_parser.rb +439 -452
  311. data/test/plugin/test_filter_record_transformer.rb +123 -166
  312. data/test/plugin/test_filter_stdout.rb +160 -72
  313. data/test/plugin/test_formatter_csv.rb +111 -0
  314. data/test/plugin/test_formatter_hash.rb +35 -0
  315. data/test/plugin/test_formatter_json.rb +51 -0
  316. data/test/plugin/test_formatter_ltsv.rb +62 -0
  317. data/test/plugin/test_formatter_msgpack.rb +28 -0
  318. data/test/plugin/test_formatter_out_file.rb +95 -0
  319. data/test/plugin/test_formatter_single_value.rb +38 -0
  320. data/test/plugin/test_formatter_tsv.rb +68 -0
  321. data/test/plugin/test_in_debug_agent.rb +24 -1
  322. data/test/plugin/test_in_dummy.rb +111 -18
  323. data/test/plugin/test_in_exec.rb +200 -113
  324. data/test/plugin/test_in_forward.rb +990 -387
  325. data/test/plugin/test_in_gc_stat.rb +10 -8
  326. data/test/plugin/test_in_http.rb +600 -224
  327. data/test/plugin/test_in_monitor_agent.rb +690 -0
  328. data/test/plugin/test_in_object_space.rb +24 -8
  329. data/test/plugin/test_in_syslog.rb +154 -215
  330. data/test/plugin/test_in_tail.rb +1006 -707
  331. data/test/plugin/test_in_tcp.rb +125 -48
  332. data/test/plugin/test_in_udp.rb +204 -63
  333. data/test/plugin/{test_in_stream.rb → test_in_unix.rb} +14 -13
  334. data/test/plugin/test_input.rb +126 -0
  335. data/test/plugin/test_metadata.rb +89 -0
  336. data/test/plugin/test_multi_output.rb +180 -0
  337. data/test/plugin/test_out_copy.rb +117 -112
  338. data/test/plugin/test_out_exec.rb +258 -53
  339. data/test/plugin/test_out_exec_filter.rb +538 -115
  340. data/test/plugin/test_out_file.rb +865 -178
  341. data/test/plugin/test_out_forward.rb +998 -210
  342. data/test/plugin/test_out_null.rb +105 -0
  343. data/test/plugin/test_out_relabel.rb +28 -0
  344. data/test/plugin/test_out_roundrobin.rb +36 -29
  345. data/test/plugin/test_out_secondary_file.rb +458 -0
  346. data/test/plugin/test_out_stdout.rb +135 -37
  347. data/test/plugin/test_out_stream.rb +18 -0
  348. data/test/plugin/test_output.rb +984 -0
  349. data/test/plugin/test_output_as_buffered.rb +2021 -0
  350. data/test/plugin/test_output_as_buffered_backup.rb +312 -0
  351. data/test/plugin/test_output_as_buffered_compress.rb +165 -0
  352. data/test/plugin/test_output_as_buffered_overflow.rb +250 -0
  353. data/test/plugin/test_output_as_buffered_retries.rb +911 -0
  354. data/test/plugin/test_output_as_buffered_secondary.rb +874 -0
  355. data/test/plugin/test_output_as_standard.rb +374 -0
  356. data/test/plugin/test_owned_by.rb +35 -0
  357. data/test/plugin/test_parser.rb +359 -0
  358. data/test/plugin/test_parser_apache.rb +42 -0
  359. data/test/plugin/test_parser_apache2.rb +47 -0
  360. data/test/plugin/test_parser_apache_error.rb +45 -0
  361. data/test/plugin/test_parser_csv.rb +103 -0
  362. data/test/plugin/test_parser_json.rb +138 -0
  363. data/test/plugin/test_parser_labeled_tsv.rb +145 -0
  364. data/test/plugin/test_parser_multiline.rb +100 -0
  365. data/test/plugin/test_parser_nginx.rb +88 -0
  366. data/test/plugin/test_parser_none.rb +52 -0
  367. data/test/plugin/test_parser_regexp.rb +289 -0
  368. data/test/plugin/test_parser_syslog.rb +441 -0
  369. data/test/plugin/test_parser_tsv.rb +122 -0
  370. data/test/plugin/test_storage.rb +167 -0
  371. data/test/plugin/test_storage_local.rb +335 -0
  372. data/test/plugin_helper/data/cert/cert-key.pem +27 -0
  373. data/test/plugin_helper/data/cert/cert-with-no-newline.pem +19 -0
  374. data/test/plugin_helper/data/cert/cert.pem +19 -0
  375. data/test/plugin_helper/http_server/test_app.rb +65 -0
  376. data/test/plugin_helper/http_server/test_route.rb +32 -0
  377. data/test/plugin_helper/test_cert_option.rb +16 -0
  378. data/test/plugin_helper/test_child_process.rb +794 -0
  379. data/test/plugin_helper/test_compat_parameters.rb +353 -0
  380. data/test/plugin_helper/test_event_emitter.rb +51 -0
  381. data/test/plugin_helper/test_event_loop.rb +52 -0
  382. data/test/plugin_helper/test_extract.rb +194 -0
  383. data/test/plugin_helper/test_formatter.rb +255 -0
  384. data/test/plugin_helper/test_http_server_helper.rb +205 -0
  385. data/test/plugin_helper/test_inject.rb +519 -0
  386. data/test/plugin_helper/test_parser.rb +264 -0
  387. data/test/plugin_helper/test_record_accessor.rb +197 -0
  388. data/test/plugin_helper/test_retry_state.rb +442 -0
  389. data/test/plugin_helper/test_server.rb +1714 -0
  390. data/test/plugin_helper/test_storage.rb +542 -0
  391. data/test/plugin_helper/test_thread.rb +164 -0
  392. data/test/plugin_helper/test_timer.rb +132 -0
  393. data/test/scripts/exec_script.rb +0 -6
  394. data/test/scripts/fluent/plugin/formatter1/formatter_test1.rb +7 -0
  395. data/test/scripts/fluent/plugin/formatter2/formatter_test2.rb +7 -0
  396. data/test/scripts/fluent/plugin/out_test.rb +23 -15
  397. data/test/scripts/fluent/plugin/out_test2.rb +80 -0
  398. data/test/test_clock.rb +164 -0
  399. data/test/test_config.rb +16 -7
  400. data/test/test_configdsl.rb +2 -2
  401. data/test/test_event.rb +360 -13
  402. data/test/test_event_router.rb +108 -11
  403. data/test/test_event_time.rb +199 -0
  404. data/test/test_filter.rb +48 -6
  405. data/test/test_formatter.rb +11 -391
  406. data/test/test_input.rb +1 -1
  407. data/test/test_log.rb +591 -31
  408. data/test/test_mixin.rb +1 -1
  409. data/test/test_output.rb +121 -185
  410. data/test/test_plugin.rb +251 -0
  411. data/test/test_plugin_classes.rb +177 -10
  412. data/test/test_plugin_helper.rb +81 -0
  413. data/test/test_plugin_id.rb +101 -0
  414. data/test/test_process.rb +8 -42
  415. data/test/test_root_agent.rb +766 -21
  416. data/test/test_supervisor.rb +481 -0
  417. data/test/test_test_drivers.rb +135 -0
  418. data/test/test_time_formatter.rb +282 -0
  419. data/test/test_time_parser.rb +231 -0
  420. data/test/test_unique_id.rb +47 -0
  421. metadata +454 -60
  422. data/COPYING +0 -14
  423. data/ChangeLog +0 -666
  424. data/lib/fluent/buffer.rb +0 -365
  425. data/lib/fluent/plugin/in_status.rb +0 -76
  426. data/test/plugin/test_in_status.rb +0 -38
  427. data/test/test_buffer.rb +0 -624
  428. data/test/test_parser.rb +0 -1305
@@ -0,0 +1,258 @@
1
+ require_relative '../helper'
2
+ require 'fluent/counter/store'
3
+ require 'fluent/time'
4
+ require 'timecop'
5
+
6
+ class CounterStoreTest < ::Test::Unit::TestCase
7
+ setup do
8
+ @name = 'key_name'
9
+ @scope = "server\tworker\tplugin"
10
+
11
+ # timecop isn't compatible with EventTime
12
+ t = Time.parse('2016-09-22 16:59:59 +0900')
13
+ Timecop.freeze(t)
14
+ @now = Fluent::EventTime.now
15
+ end
16
+
17
+ shutdown do
18
+ Timecop.return
19
+ end
20
+
21
+ def extract_value_from_counter(counter, key)
22
+ store = counter.instance_variable_get(:@storage).instance_variable_get(:@store)
23
+ store[key]
24
+ end
25
+
26
+ def travel(sec)
27
+ # Since Timecop.travel() causes test failures on Windows/AppVeyor by inducing
28
+ # rounding errors to Time.now, we need to use Timecop.freeze() instead.
29
+ Timecop.freeze(Time.now + sec)
30
+ end
31
+
32
+ sub_test_case 'init' do
33
+ setup do
34
+ @reset_interval = 10
35
+ @store = Fluent::Counter::Store.new
36
+ @data = { 'name' => @name, 'reset_interval' => @reset_interval }
37
+ @key = Fluent::Counter::Store.gen_key(@scope, @name)
38
+ end
39
+
40
+ test 'create new value in the counter' do
41
+ v = @store.init(@key, @data)
42
+
43
+ assert_equal @name, v['name']
44
+ assert_equal @reset_interval, v['reset_interval']
45
+
46
+ v2 = extract_value_from_counter(@store, @key)
47
+ v2 = @store.send(:build_response, v2)
48
+ assert_equal v, v2
49
+ end
50
+
51
+ test 'raise an error when a passed key already exists' do
52
+ @store.init(@key, @data)
53
+
54
+ assert_raise Fluent::Counter::InvalidParams do
55
+ @store.init(@key, @data)
56
+ end
57
+ end
58
+
59
+ test 'return a value when passed key already exists and a ignore option is true' do
60
+ v = @store.init(@key, @data)
61
+ v1 = extract_value_from_counter(@store, @key)
62
+ v1 = @store.send(:build_response, v1)
63
+ v2 = @store.init(@key, @data, ignore: true)
64
+ assert_equal v, v2
65
+ assert_equal v1, v2
66
+ end
67
+ end
68
+
69
+ sub_test_case 'get' do
70
+ setup do
71
+ @store = Fluent::Counter::Store.new
72
+ data = { 'name' => @name, 'reset_interval' => 10 }
73
+ @key = Fluent::Counter::Store.gen_key(@scope, @name)
74
+ @store.init(@key, data)
75
+ end
76
+
77
+ test 'return a value from the counter' do
78
+ v = extract_value_from_counter(@store, @key)
79
+ expected = @store.send(:build_response, v)
80
+ assert_equal expected, @store.get(@key)
81
+ end
82
+
83
+ test 'return a raw value from the counter when raw option is true' do
84
+ v = extract_value_from_counter(@store, @key)
85
+ assert_equal v, @store.get(@key, raw: true)
86
+ end
87
+
88
+ test "return nil when a passed key doesn't exist" do
89
+ assert_equal nil, @store.get('unknown_key')
90
+ end
91
+
92
+ test "raise a error when a passed key doesn't exist and raise_error option is true" do
93
+ assert_raise Fluent::Counter::UnknownKey do
94
+ @store.get('unknown_key', raise_error: true)
95
+ end
96
+ end
97
+ end
98
+
99
+ sub_test_case 'key?' do
100
+ setup do
101
+ @store = Fluent::Counter::Store.new
102
+ data = { 'name' => @name, 'reset_interval' => 10 }
103
+ @key = Fluent::Counter::Store.gen_key(@scope, @name)
104
+ @store.init(@key, data)
105
+ end
106
+
107
+ test 'return true when passed key exists' do
108
+ assert_true @store.key?(@key)
109
+ end
110
+
111
+ test "return false when passed key doesn't exist" do
112
+ assert_true !@store.key?('unknown_key')
113
+ end
114
+ end
115
+
116
+ sub_test_case 'delete' do
117
+ setup do
118
+ @store = Fluent::Counter::Store.new
119
+ data = { 'name' => @name, 'reset_interval' => 10 }
120
+ @key = Fluent::Counter::Store.gen_key(@scope, @name)
121
+ @init_value = @store.init(@key, data)
122
+ end
123
+
124
+ test 'delete a value from the counter' do
125
+ v = @store.delete(@key)
126
+ assert_equal @init_value, v
127
+ assert_nil extract_value_from_counter(@store, @key)
128
+ end
129
+
130
+ test "raise an error when passed key doesn't exist" do
131
+ assert_raise Fluent::Counter::UnknownKey do
132
+ @store.delete('unknown_key')
133
+ end
134
+ end
135
+ end
136
+
137
+ sub_test_case 'inc' do
138
+ setup do
139
+ @store = Fluent::Counter::Store.new
140
+ @init_data = { 'name' => @name, 'reset_interval' => 10 }
141
+ @travel_sec = 10
142
+ end
143
+
144
+ data(
145
+ positive: 10,
146
+ negative: -10
147
+ )
148
+ test 'increment or decrement a value in the counter' do |value|
149
+ key = Fluent::Counter::Store.gen_key(@scope, @name)
150
+ @store.init(key, @init_data)
151
+ travel(@travel_sec)
152
+ v = @store.inc(key, { 'value' => value })
153
+
154
+ assert_equal value, v['total']
155
+ assert_equal value, v['current']
156
+ assert_equal @now, v['last_reset_at'] # last_reset_at doesn't change
157
+
158
+ v1 = extract_value_from_counter(@store, key)
159
+ v1 = @store.send(:build_response, v1)
160
+ assert_equal v, v1
161
+ end
162
+
163
+ test "raise an error when passed key doesn't exist" do
164
+ assert_raise Fluent::Counter::UnknownKey do
165
+ @store.inc('unknown_key', { 'value' => 1 })
166
+ end
167
+ end
168
+
169
+ test 'raise an error when a type of passed value is incompatible with a stored value' do
170
+ key1 = Fluent::Counter::Store.gen_key(@scope, @name)
171
+ key2 = Fluent::Counter::Store.gen_key(@scope, 'name2')
172
+ key3 = Fluent::Counter::Store.gen_key(@scope, 'name3')
173
+ v1 = @store.init(key1, @init_data.merge('type' => 'integer'))
174
+ v2 = @store.init(key2, @init_data.merge('type' => 'float'))
175
+ v3 = @store.init(key3, @init_data.merge('type' => 'numeric'))
176
+ assert_equal 'integer', v1['type']
177
+ assert_equal 'float', v2['type']
178
+ assert_equal 'numeric', v3['type']
179
+
180
+ assert_raise Fluent::Counter::InvalidParams do
181
+ @store.inc(key1, { 'value' => 1.1 })
182
+ end
183
+
184
+ assert_raise Fluent::Counter::InvalidParams do
185
+ @store.inc(key2, { 'value' => 1 })
186
+ end
187
+
188
+ assert_nothing_raised do
189
+ @store.inc(key3, { 'value' => 1 })
190
+ @store.inc(key3, { 'value' => 1.0 })
191
+ end
192
+ end
193
+ end
194
+
195
+ sub_test_case 'reset' do
196
+ setup do
197
+ @store = Fluent::Counter::Store.new
198
+ @travel_sec = 10
199
+
200
+ @inc_value = 10
201
+ @key = Fluent::Counter::Store.gen_key(@scope, @name)
202
+ @store.init(@key, { 'name' => @name, 'reset_interval' => 10 })
203
+ @store.inc(@key, { 'value' => 10 })
204
+ end
205
+
206
+ test 'reset a value in the counter' do
207
+ travel(@travel_sec)
208
+
209
+ v = @store.reset(@key)
210
+ assert_equal @travel_sec, v['elapsed_time']
211
+ assert_true v['success']
212
+ counter = v['counter_data']
213
+
214
+ assert_equal @name, counter['name']
215
+ assert_equal @inc_value, counter['total']
216
+ assert_equal @inc_value, counter['current']
217
+ assert_equal 'numeric', counter['type']
218
+ assert_equal @now, counter['last_reset_at']
219
+ assert_equal 10, counter['reset_interval']
220
+
221
+ v1 = extract_value_from_counter(@store, @key)
222
+ assert_equal 0, v1['current']
223
+ assert_true v1['current'].is_a?(Integer)
224
+ assert_equal @inc_value, v1['total']
225
+ assert_equal (@now + @travel_sec), Fluent::EventTime.new(*v1['last_reset_at'])
226
+ assert_equal (@now + @travel_sec), Fluent::EventTime.new(*v1['last_modified_at'])
227
+ end
228
+
229
+ test 'reset a value after `reset_interval` passed' do
230
+ first_travel_sec = 5
231
+ travel(first_travel_sec) # jump time less than reset_interval
232
+ v = @store.reset(@key)
233
+
234
+ assert_equal false, v['success']
235
+ assert_equal first_travel_sec, v['elapsed_time']
236
+ store = extract_value_from_counter(@store, @key)
237
+ assert_equal 10, store['current']
238
+ assert_equal @now, Fluent::EventTime.new(*store['last_reset_at'])
239
+
240
+ # time is passed greater than reset_interval
241
+ travel(@travel_sec)
242
+ v = @store.reset(@key)
243
+ assert_true v['success']
244
+ assert_equal @travel_sec + first_travel_sec, v['elapsed_time']
245
+
246
+ v1 = extract_value_from_counter(@store, @key)
247
+ assert_equal 0, v1['current']
248
+ assert_equal (@now + @travel_sec + first_travel_sec), Fluent::EventTime.new(*v1['last_reset_at'])
249
+ assert_equal (@now + @travel_sec + first_travel_sec), Fluent::EventTime.new(*v1['last_modified_at'])
250
+ end
251
+
252
+ test "raise an error when passed key doesn't exist" do
253
+ assert_raise Fluent::Counter::UnknownKey do
254
+ @store.reset('unknown_key')
255
+ end
256
+ end
257
+ end
258
+ end
@@ -0,0 +1,137 @@
1
+ require_relative '../helper'
2
+ require 'fluent/counter/validator'
3
+
4
+ class CounterValidatorTest < ::Test::Unit::TestCase
5
+ data(
6
+ invalid_name1: '',
7
+ invalid_name3: '_',
8
+ invalid_name4: 'A',
9
+ invalid_name5: 'a*',
10
+ invalid_name6: "a\t",
11
+ invalid_name7: "\n",
12
+ )
13
+ test 'invalid name' do |invalid_name|
14
+ assert_nil(Fluent::Counter::Validator::VALID_NAME =~ invalid_name)
15
+ end
16
+
17
+ sub_test_case 'request' do
18
+ test 'return an empty array' do
19
+ data = { 'id' => 0, 'method' => 'init' }
20
+ errors = Fluent::Counter::Validator.request(data)
21
+ assert_empty errors
22
+ end
23
+
24
+ data(
25
+ missing_id: [
26
+ { 'method' => 'init' },
27
+ { 'code' => 'invalid_request', 'message' => 'Request should include `id`' }
28
+ ],
29
+ missing_method: [
30
+ { 'id' => 0 },
31
+ { 'code' => 'invalid_request', 'message' => 'Request should include `method`' }
32
+ ],
33
+ invalid_method: [
34
+ { 'id' => 0, 'method' => "A\t" },
35
+ { 'code' => 'invalid_request', 'message' => '`method` is the invalid format' }
36
+ ],
37
+ unknown_method: [
38
+ { 'id' => 0, 'method' => 'unknown_method' },
39
+ { 'code' => 'method_not_found', 'message' => 'Unknown method name passed: unknown_method' }
40
+ ]
41
+ )
42
+ test 'return an error array' do |(data, expected_error)|
43
+ errors = Fluent::Counter::Validator.request(data)
44
+ assert_equal [expected_error], errors
45
+ end
46
+ end
47
+
48
+ sub_test_case 'call' do
49
+ test "return an error hash when passed method doesn't exist" do
50
+ v = Fluent::Counter::Validator.new(:unknown)
51
+ success, errors = v.call(['key1'])
52
+ assert_empty success
53
+ assert_equal 'internal_server_error', errors.first.to_hash['code']
54
+ end
55
+ end
56
+
57
+ test 'validate_empty!' do
58
+ v = Fluent::Counter::Validator.new(:empty)
59
+ success, errors = v.call([])
60
+ assert_empty success
61
+ assert_equal [Fluent::Counter::InvalidParams.new('One or more `params` are required')], errors
62
+ end
63
+ end
64
+
65
+ class CounterArrayValidatorTest < ::Test::Unit::TestCase
66
+ test 'validate_key!' do
67
+ ary = ['key', 100, '_']
68
+ error_expected = [
69
+ { 'code' => 'invalid_params', 'message' => 'The type of `key` should be String' },
70
+ { 'code' => 'invalid_params', 'message' => '`key` is the invalid format' }
71
+ ]
72
+ v = Fluent::Counter::ArrayValidator.new(:key)
73
+ valid_params, errors = v.call(ary)
74
+
75
+ assert_equal ['key'], valid_params
76
+ assert_equal error_expected, errors.map(&:to_hash)
77
+ end
78
+ end
79
+
80
+ class CounterHashValidatorTest < ::Test::Unit::TestCase
81
+ test 'validate_name!' do
82
+ hash = [
83
+ { 'name' => 'key' },
84
+ {},
85
+ { 'name' => 10 },
86
+ { 'name' => '_' }
87
+ ]
88
+ error_expected = [
89
+ { 'code' => 'invalid_params', 'message' => '`name` is required' },
90
+ { 'code' => 'invalid_params', 'message' => 'The type of `name` should be String' },
91
+ { 'code' => 'invalid_params', 'message' => '`name` is the invalid format' },
92
+ ]
93
+ v = Fluent::Counter::HashValidator.new(:name)
94
+ success, errors = v.call(hash)
95
+
96
+ assert_equal [{ 'name' => 'key' }], success
97
+ assert_equal error_expected, errors.map(&:to_hash)
98
+ end
99
+
100
+ test 'validate_value!' do
101
+ hash = [
102
+ { 'value' => 1 },
103
+ { 'value' => -1 },
104
+ {},
105
+ { 'value' => 'str' }
106
+ ]
107
+ error_expected = [
108
+ { 'code' => 'invalid_params', 'message' => '`value` is required' },
109
+ { 'code' => 'invalid_params', 'message' => 'The type of `value` type should be Numeric' },
110
+ ]
111
+ v = Fluent::Counter::HashValidator.new(:value)
112
+ valid_params, errors = v.call(hash)
113
+
114
+ assert_equal [{ 'value' => 1 }, { 'value' => -1 }], valid_params
115
+ assert_equal error_expected, errors.map(&:to_hash)
116
+ end
117
+
118
+ test 'validate_reset_interval!' do
119
+ hash = [
120
+ { 'reset_interval' => 1 },
121
+ { 'reset_interval' => 1.0 },
122
+ {},
123
+ { 'reset_interval' => -1 },
124
+ { 'reset_interval' => 'str' }
125
+ ]
126
+ error_expected = [
127
+ { 'code' => 'invalid_params', 'message' => '`reset_interval` is required' },
128
+ { 'code' => 'invalid_params', 'message' => '`reset_interval` should be a positive number' },
129
+ { 'code' => 'invalid_params', 'message' => 'The type of `reset_interval` should be Numeric' },
130
+ ]
131
+ v = Fluent::Counter::HashValidator.new(:reset_interval)
132
+ valid_params, errors = v.call(hash)
133
+
134
+ assert_equal [{ 'reset_interval' => 1 }, { 'reset_interval' => 1.0 }], valid_params
135
+ assert_equal error_expected.map(&:to_hash), errors.map(&:to_hash)
136
+ end
137
+ end
data/test/helper.rb CHANGED
@@ -39,9 +39,29 @@ require 'rr'
39
39
  require 'test/unit'
40
40
  require 'test/unit/rr'
41
41
  require 'fileutils'
42
+ require 'fluent/config/element'
42
43
  require 'fluent/log'
43
44
  require 'fluent/test'
44
45
  require 'fluent/test/helpers'
46
+ require 'fluent/plugin/base'
47
+ require 'fluent/plugin_id'
48
+ require 'fluent/plugin_helper'
49
+ require 'fluent/msgpack_factory'
50
+ require 'fluent/time'
51
+ require 'serverengine'
52
+ require 'helpers/fuzzy_assert'
53
+
54
+ module Fluent
55
+ module Plugin
56
+ class TestBase < Base
57
+ # a base plugin class, but not input nor output
58
+ # mainly for helpers and owned plugins
59
+ include PluginId
60
+ include PluginLoggerMixin
61
+ include PluginHelper::Mixin
62
+ end
63
+ end
64
+ end
45
65
 
46
66
  unless defined?(Test::Unit::AssertionFailedError)
47
67
  class Test::Unit::AssertionFailedError < StandardError
@@ -50,11 +70,70 @@ end
50
70
 
51
71
  include Fluent::Test::Helpers
52
72
 
53
- def unused_port
54
- s = TCPServer.open(0)
55
- port = s.addr[1]
56
- s.close
57
- port
73
+ def unused_port(num = 1, protocol: :tcp, bind: "0.0.0.0")
74
+ case protocol
75
+ when :tcp
76
+ unused_port_tcp(num)
77
+ when :udp
78
+ unused_port_udp(num, bind: bind)
79
+ else
80
+ raise ArgumentError, "unknown protocol: #{protocol}"
81
+ end
82
+ end
83
+
84
+ def unused_port_tcp(num = 1)
85
+ ports = []
86
+ sockets = []
87
+ num.times do
88
+ s = TCPServer.open(0)
89
+ sockets << s
90
+ ports << s.addr[1]
91
+ end
92
+ sockets.each{|s| s.close }
93
+ if num == 1
94
+ return ports.first
95
+ else
96
+ return *ports
97
+ end
98
+ end
99
+
100
+ PORT_RANGE_AVAILABLE = (1024...65535)
101
+
102
+ def unused_port_udp(num = 1, bind: "0.0.0.0")
103
+ family = IPAddr.new(IPSocket.getaddress(bind)).ipv4? ? ::Socket::AF_INET : ::Socket::AF_INET6
104
+ ports = []
105
+ sockets = []
106
+ while ports.size < num
107
+ port = rand(PORT_RANGE_AVAILABLE)
108
+ u = UDPSocket.new(family)
109
+ if (u.bind(bind, port) rescue nil)
110
+ ports << port
111
+ sockets << u
112
+ else
113
+ u.close
114
+ end
115
+ end
116
+ sockets.each{|s| s.close }
117
+ if num == 1
118
+ return ports.first
119
+ else
120
+ return *ports
121
+ end
122
+ end
123
+
124
+ def waiting(seconds, logs: nil, plugin: nil)
125
+ begin
126
+ Timeout.timeout(seconds) do
127
+ yield
128
+ end
129
+ rescue Timeout::Error
130
+ if logs
131
+ STDERR.print(*logs)
132
+ elsif plugin
133
+ STDERR.print(*plugin.log.out.logs)
134
+ end
135
+ raise
136
+ end
58
137
  end
59
138
 
60
139
  def ipv6_enabled?
@@ -68,4 +147,8 @@ def ipv6_enabled?
68
147
  end
69
148
  end
70
149
 
71
- $log = Fluent::Log.new(Fluent::Test::DummyLogDevice.new, Fluent::Log::LEVEL_WARN)
150
+ dl_opts = {}
151
+ dl_opts[:log_level] = ServerEngine::DaemonLogger::WARN
152
+ logdev = Fluent::Test::DummyLogDevice.new
153
+ logger = ServerEngine::DaemonLogger.new(logdev, dl_opts)
154
+ $log ||= Fluent::Log.new(logger)
@@ -0,0 +1,89 @@
1
+ require 'test/unit'
2
+
3
+ class FuzzyIncludeAssertion
4
+ include Test::Unit::Assertions
5
+
6
+ def self.assert(expected, actual, message = nil)
7
+ new(expected, actual, message).assert
8
+ end
9
+
10
+ def initialize(expected, actual, message)
11
+ @expected = expected
12
+ @actual = actual
13
+ @message = message
14
+ end
15
+
16
+ def assert
17
+ if collection?
18
+ assert_same_collection
19
+ else
20
+ assert_same_value
21
+ end
22
+ end
23
+
24
+ private
25
+
26
+ def assert_same_value
27
+ m = "expected(#{@expected}) !== actual(#{@actual.inspect})"
28
+ if @message
29
+ m = "#{@message}: #{m}"
30
+ end
31
+ assert_true(@expected === @actual, m)
32
+ end
33
+
34
+ def assert_same_class
35
+ if @expected.class != @actual.class
36
+ if (@expected.class.ancestors | @actual.class.ancestors).empty?
37
+ assert_equal(@expected.class, @actual.class, @message)
38
+ end
39
+ end
40
+ end
41
+
42
+ def assert_same_collection
43
+ assert_same_class
44
+ assert_same_values
45
+ end
46
+
47
+ def assert_same_values
48
+ if @expected.is_a?(Array)
49
+ @expected.each_with_index do |val, i|
50
+ self.class.assert(val, @actual[i], @message)
51
+ end
52
+ else
53
+ @expected.each do |key, val|
54
+ self.class.assert(val, @actual[key], "#{key}: ")
55
+ end
56
+ end
57
+ end
58
+
59
+ def collection?
60
+ @actual.is_a?(Array) || @actual.is_a?(Hash)
61
+ end
62
+ end
63
+
64
+ class FuzzyAssertion < FuzzyIncludeAssertion
65
+ private
66
+
67
+ def assert_same_collection
68
+ super
69
+ assert_same_keys
70
+ end
71
+
72
+ def assert_same_keys
73
+ if @expected.is_a?(Array)
74
+ assert_equal(@expected.size, @actual.size, "expected.size(#{@expected}) != actual.size(#{@expected})")
75
+ else
76
+ assert_equal(@expected.keys.sort, @actual.keys.sort)
77
+ end
78
+ end
79
+ end
80
+
81
+ module FuzzyAssert
82
+ def assert_fuzzy_include(left, right, message = nil)
83
+ FuzzyIncludeAssertion.new(left, right, message).assert
84
+ end
85
+
86
+ def assert_fuzzy_equal(left, right, message = nil)
87
+ FuzzyAssertion.new(left, right, message).assert
88
+ end
89
+ end